neon 2.47.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/_shared/auth_selection.js +76 -79
- package/dist/_shared/credentials.js +166 -187
- package/dist/_shared/env-core/env.js +395 -0
- package/dist/_shared/env-core/reuse-secrets.js +179 -0
- package/dist/_shared/paths.js +129 -126
- package/dist/_shared/profiles.js +192 -242
- package/dist/_shared/secure_file.js +36 -38
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/analytics.js +163 -207
- package/dist/api.js +815 -758
- package/dist/auth.js +121 -141
- package/dist/auth_context.js +39 -53
- package/dist/cli.js +4 -7
- package/dist/commands/api.js +220 -250
- package/dist/commands/api_keys.js +251 -314
- package/dist/commands/auth.js +283 -328
- package/dist/commands/bootstrap.js +372 -437
- package/dist/commands/branches.js +304 -455
- package/dist/commands/bucket.js +374 -514
- package/dist/commands/checkout.js +213 -298
- package/dist/commands/config.js +575 -658
- package/dist/commands/connection_string.js +137 -165
- package/dist/commands/data_api.js +238 -260
- package/dist/commands/databases.js +67 -76
- package/dist/commands/deploy.js +31 -25
- package/dist/commands/dev.js +642 -681
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +243 -303
- package/dist/commands/functions.js +275 -341
- package/dist/commands/index.js +70 -65
- package/dist/commands/init.js +84 -119
- package/dist/commands/inspect.js +55 -55
- package/dist/commands/ip_allow.js +88 -120
- package/dist/commands/link.js +874 -1019
- package/dist/commands/logs.js +291 -0
- package/dist/commands/neon_auth.js +725 -933
- package/dist/commands/operations.js +34 -25
- package/dist/commands/orgs.js +28 -18
- package/dist/commands/profile.js +614 -845
- package/dist/commands/projects.js +313 -373
- package/dist/commands/psql.js +60 -58
- package/dist/commands/roles.js +55 -58
- package/dist/commands/schema_diff.js +87 -131
- package/dist/commands/set_context.js +34 -26
- package/dist/commands/snapshots.js +288 -413
- package/dist/commands/status.js +41 -37
- package/dist/commands/user.js +21 -10
- package/dist/commands/vpc_endpoints.js +85 -113
- package/dist/config.js +7 -6
- package/dist/config_format.js +50 -66
- package/dist/config_template.js +128 -157
- package/dist/context.js +183 -235
- package/dist/current_branch_fast_path.js +40 -49
- package/dist/dev/env.js +313 -394
- package/dist/dev/functions.js +54 -64
- package/dist/dev/inputs.js +46 -58
- package/dist/dev/runtime.js +135 -164
- package/dist/dev/websocket.js +766 -959
- package/dist/env.js +27 -33
- package/dist/env_file.js +118 -132
- package/dist/env_services.js +36 -38
- package/dist/errors.js +57 -68
- package/dist/functions_api.js +45 -43
- package/dist/help.js +189 -140
- package/dist/index.js +182 -257
- package/dist/init/agents.js +137 -118
- package/dist/init/auth.js +58 -68
- package/dist/init/bootstrap.js +325 -396
- package/dist/init/build_config.js +4 -2
- package/dist/init/detect_agent.js +56 -101
- package/dist/init/editors.js +35 -52
- package/dist/init/enrich_output.js +51 -66
- package/dist/init/extension.js +134 -171
- package/dist/init/inspect.js +179 -266
- package/dist/init/interactive.js +510 -622
- package/dist/init/neonctl.js +117 -168
- package/dist/init/orchestrate.js +157 -173
- package/dist/init/phases/auth.js +188 -202
- package/dist/init/phases/cleanup.js +23 -23
- package/dist/init/phases/db.js +251 -277
- package/dist/init/phases/getting_started.js +213 -223
- package/dist/init/phases/mcp.js +174 -224
- package/dist/init/phases/migrations.js +247 -248
- package/dist/init/phases/neon_auth.js +114 -133
- package/dist/init/phases/setup.js +546 -703
- package/dist/init/phases/skills.js +75 -86
- package/dist/init/phases/status.js +72 -67
- package/dist/init/resolve_context.js +102 -99
- package/dist/init/route_command.js +91 -98
- package/dist/init/skills.js +174 -218
- package/dist/init/vsix.js +77 -99
- package/dist/log.js +17 -16
- package/dist/neon_services.js +104 -129
- package/dist/parameters.gen.js +481 -471
- package/dist/pkg.js +17 -19
- package/dist/profile_keys.js +44 -47
- package/dist/psql/cli.js +44 -47
- package/dist/psql/command/cmd_cond.js +231 -406
- package/dist/psql/command/cmd_connect.js +557 -764
- package/dist/psql/command/cmd_copy.js +727 -983
- package/dist/psql/command/cmd_describe.js +1499 -1688
- package/dist/psql/command/cmd_format.js +733 -905
- package/dist/psql/command/cmd_io.js +1293 -2082
- package/dist/psql/command/cmd_lo.js +297 -359
- package/dist/psql/command/cmd_meta.js +727 -878
- package/dist/psql/command/cmd_misc.js +138 -172
- package/dist/psql/command/cmd_pipeline.js +547 -1099
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +261 -325
- package/dist/psql/command/inputQueue.js +35 -33
- package/dist/psql/command/shared.js +49 -63
- package/dist/psql/complete/filenames.js +90 -133
- package/dist/psql/complete/index.js +59 -97
- package/dist/psql/complete/matcher.js +236 -300
- package/dist/psql/complete/psqlVars.js +218 -223
- package/dist/psql/complete/queries.js +159 -177
- package/dist/psql/complete/rules.js +1493 -2299
- package/dist/psql/core/common.js +762 -1180
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1302
- package/dist/psql/core/prompt.js +391 -408
- package/dist/psql/core/settings.js +429 -644
- package/dist/psql/core/sqlHelp.js +480 -554
- package/dist/psql/core/startup.js +626 -815
- package/dist/psql/core/syncVars.js +67 -110
- package/dist/psql/core/variables.js +156 -278
- package/dist/psql/describe/formatters.js +884 -1285
- package/dist/psql/describe/processNamePattern.js +173 -260
- package/dist/psql/describe/queries.js +1368 -2403
- package/dist/psql/describe/versionGate.js +32 -41
- package/dist/psql/index.js +1414 -1927
- package/dist/psql/io/history.js +232 -271
- package/dist/psql/io/input.js +103 -108
- package/dist/psql/io/lineEditor/buffer.js +238 -319
- package/dist/psql/io/lineEditor/complete.js +135 -213
- package/dist/psql/io/lineEditor/filename.js +139 -148
- package/dist/psql/io/lineEditor/index.js +653 -870
- package/dist/psql/io/lineEditor/keymap.js +544 -702
- package/dist/psql/io/lineEditor/vt100.js +294 -341
- package/dist/psql/io/pgpass.js +158 -187
- package/dist/psql/io/pgservice.js +146 -183
- package/dist/psql/io/psqlrc.js +328 -403
- package/dist/psql/print/aligned.js +1020 -1683
- package/dist/psql/print/asciidoc.js +180 -214
- package/dist/psql/print/crosstab.js +281 -442
- package/dist/psql/print/csv.js +48 -70
- package/dist/psql/print/html.js +195 -226
- package/dist/psql/print/json.js +75 -88
- package/dist/psql/print/latex.js +291 -364
- package/dist/psql/print/pager.js +171 -242
- package/dist/psql/print/troff.js +194 -226
- package/dist/psql/print/unaligned.js +69 -95
- package/dist/psql/print/units.js +167 -169
- package/dist/psql/scanner/slash.js +428 -483
- package/dist/psql/scanner/sql.js +445 -889
- package/dist/psql/scanner/stringutils.js +309 -379
- package/dist/psql/types/index.js +2 -7
- package/dist/psql/types/scanner.js +25 -22
- package/dist/psql/wire/connection.js +2042 -2803
- package/dist/psql/wire/copy.js +84 -100
- package/dist/psql/wire/notify.js +39 -59
- package/dist/psql/wire/pipeline.js +305 -518
- package/dist/psql/wire/protocol.js +349 -417
- package/dist/psql/wire/sasl.js +180 -265
- package/dist/psql/wire/tls.js +400 -561
- package/dist/storage_api.js +115 -129
- package/dist/test_utils/fixtures.js +94 -113
- package/dist/test_utils/oauth_server.js +10 -7
- package/dist/test_utils/project_dir.js +33 -0
- package/dist/utils/ai_gateway_notice.js +131 -162
- package/dist/utils/api_enums.js +21 -28
- package/dist/utils/auth.js +10 -4
- package/dist/utils/branch_notice.js +20 -19
- package/dist/utils/branch_picker.js +83 -89
- package/dist/utils/cli_name.js +15 -12
- package/dist/utils/compute_units.js +20 -27
- package/dist/utils/config_diff.js +127 -158
- package/dist/utils/enrichers.js +95 -148
- package/dist/utils/esbuild.js +133 -147
- package/dist/utils/flags.js +35 -47
- package/dist/utils/formats.js +8 -15
- package/dist/utils/git_diff.js +69 -80
- package/dist/utils/inspect_db.js +101 -143
- package/dist/utils/inspect_queries.js +179 -142
- package/dist/utils/middlewares.js +37 -44
- package/dist/utils/openapi.js +87 -99
- package/dist/utils/package_manager.js +312 -110
- package/dist/utils/point_in_time.js +49 -53
- package/dist/utils/psql.js +89 -106
- package/dist/utils/service_picker.js +55 -58
- package/dist/utils/string.js +5 -5
- package/dist/utils/ui.js +38 -55
- package/dist/utils/write_sync.js +26 -35
- package/dist/utils/zip.js +4 -3
- package/dist/writer.js +67 -87
- package/package.json +9 -7
|
@@ -1,180 +1,149 @@
|
|
|
1
1
|
import { log } from "../log.js";
|
|
2
|
+
//#region src/utils/ai_gateway_notice.ts
|
|
2
3
|
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
4
|
+
* Friendly guidance shown when a branch enables the AI Gateway (`preview.aiGateway`).
|
|
5
|
+
*
|
|
6
|
+
* The gateway is credential-gated, not provisioned: enabling it always mints a working
|
|
7
|
+
* branch credential, so `apply` / `checkout` / `env pull` succeed regardless of plan. The
|
|
8
|
+
* two things that *do* gate the gateway happen at serving time and are invisible in the
|
|
9
|
+
* provisioning result, so we surface them as a courtesy notice instead:
|
|
10
|
+
*
|
|
11
|
+
* - **Free plan** — credentials provision, but the gateway does not *serve* model requests.
|
|
12
|
+
* The account needs to upgrade to a paid plan.
|
|
13
|
+
* - **Reduced model set** — on a paid plan, an account still ramping up on the beta gets a
|
|
14
|
+
* trimmed model catalog (flagship models are listed but `enabled: false` on `/v1/models`).
|
|
15
|
+
* They can request access to more models.
|
|
16
|
+
*
|
|
17
|
+
* This is deliberately phrased for the user: it never mentions account "verification" or any
|
|
18
|
+
* other internal gating mechanism.
|
|
19
|
+
*/
|
|
19
20
|
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const FREE_SUBSCRIPTION_TYPES = new Set(["free_v2", "free_v3"]);
|
|
21
|
+
* `BillingSubscriptionType` values that mean the account is on a Free plan (the gateway
|
|
22
|
+
* won't serve requests). Everything else — `launch`, `scale`, `business`, the `*_v3`
|
|
23
|
+
* variants, marketplace plans — is treated as paid.
|
|
24
|
+
*/
|
|
25
|
+
const FREE_SUBSCRIPTION_TYPES = /* @__PURE__ */ new Set(["free_v2", "free_v3"]);
|
|
25
26
|
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
? `https://console.neon.tech/app/${orgId}/billing`
|
|
32
|
-
: "https://console.neon.tech/app/billing";
|
|
27
|
+
* The Neon Console billing page to upgrade a Free-plan account so the gateway can serve. It's
|
|
28
|
+
* org-scoped when the project belongs to an org; projects on a personal account (which is
|
|
29
|
+
* where Free plans usually live) have no org id, so we fall back to the account-level page.
|
|
30
|
+
*/
|
|
31
|
+
const aiGatewayUpgradeUrl = (orgId) => orgId ? `https://console.neon.tech/app/${orgId}/billing` : "https://console.neon.tech/app/billing";
|
|
33
32
|
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
FREE_SUBSCRIPTION_TYPES.has(subscriptionType);
|
|
33
|
+
* The branch's AI Gateway page in the Neon Console — where a paid user with a reduced model
|
|
34
|
+
* set requests access to more models. It's branch-scoped, so it's built from the linked
|
|
35
|
+
* project and branch ids.
|
|
36
|
+
*/
|
|
37
|
+
const aiGatewayModelsUrl = (projectId, branchId) => `https://console.neon.tech/app/projects/${projectId}/branches/${branchId}/ai-gateway`;
|
|
38
|
+
const isFreePlan = (subscriptionType) => subscriptionType !== void 0 && FREE_SUBSCRIPTION_TYPES.has(subscriptionType);
|
|
41
39
|
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
* Whether the model catalog includes at least one flagship model enabled. Flagship models
|
|
41
|
+
* (Anthropic Opus, OpenAI Codex / `*-pro`) are the first to be held back for an account still
|
|
42
|
+
* ramping up on the beta, so their total absence from a non-empty enabled set is the signal
|
|
43
|
+
* that the account has a reduced model set. Matched by id substring so it survives model
|
|
44
|
+
* version bumps (e.g. `claude-opus-4-8`).
|
|
45
|
+
*/
|
|
46
|
+
const hasFlagshipModels = (modelIds) => modelIds.some((id) => id.includes("opus") || id.includes("codex") || id.endsWith("-pro"));
|
|
49
47
|
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"gateway won't serve model requests. Upgrade to a paid plan and re-run, or remove " +
|
|
57
|
-
`\`preview.aiGateway\` from neon.ts. Upgrade here: ${upgradeUrl}`;
|
|
48
|
+
* The message shown when a `neon.ts` that enables the AI Gateway is applied on a Free plan.
|
|
49
|
+
* Provisioning is refused up front (see {@link assertAiGatewayProvisionable}) because the
|
|
50
|
+
* gateway won't serve model requests until the account is on a paid plan. `upgradeUrl` is the
|
|
51
|
+
* org-scoped Console billing page (see {@link aiGatewayUpgradeUrl}).
|
|
52
|
+
*/
|
|
53
|
+
const freePlanBlockMessage = (upgradeUrl) => `This neon.ts enables the AI Gateway, which isn't available on the Free plan — the gateway won't serve model requests. Upgrade to a paid plan and re-run, or remove \`preview.aiGateway\` from neon.ts. Upgrade here: ${upgradeUrl}`;
|
|
58
54
|
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
!hasFlagshipModels(modelIds)) {
|
|
79
|
-
return {
|
|
80
|
-
level: "warning",
|
|
81
|
-
message: "AI Gateway is in public beta and not every model is enabled for your account " +
|
|
82
|
-
"yet, so some models are missing from the catalog. Request access to more " +
|
|
83
|
-
`models here: ${moreModelsUrl}`,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
return null;
|
|
55
|
+
* Build the AI Gateway courtesy notice for an account's plan and (optionally) its live model
|
|
56
|
+
* catalog, or `null` when nothing needs saying.
|
|
57
|
+
*
|
|
58
|
+
* `modelIds` is `undefined` when the catalog wasn't probed (e.g. a dry-run `plan`, or a probe
|
|
59
|
+
* that failed); in that case only the plan-based (Free) notice can be produced — never a
|
|
60
|
+
* false "reduced models" warning. `upgradeUrl` is the org-scoped Console billing page
|
|
61
|
+
* (Free notice); `moreModelsUrl` is the branch's Console AI Gateway page
|
|
62
|
+
* (see {@link aiGatewayModelsUrl}), shown when the catalog is reduced.
|
|
63
|
+
*/
|
|
64
|
+
const buildAiGatewayNotice = ({ subscriptionType, modelIds, upgradeUrl, moreModelsUrl }) => {
|
|
65
|
+
if (isFreePlan(subscriptionType)) return {
|
|
66
|
+
level: "warning",
|
|
67
|
+
message: `AI Gateway is enabled, but the gateway does not serve model requests on the Free plan. Upgrade to a paid plan to start making requests: ${upgradeUrl}`
|
|
68
|
+
};
|
|
69
|
+
if (modelIds !== void 0 && modelIds.length > 0 && !hasFlagshipModels(modelIds)) return {
|
|
70
|
+
level: "warning",
|
|
71
|
+
message: `AI Gateway is in public beta and not every model is enabled for your account yet, so some models are missing from the catalog. Request access to more models here: ${moreModelsUrl}`
|
|
72
|
+
};
|
|
73
|
+
return null;
|
|
87
74
|
};
|
|
88
75
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
89
76
|
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (isRecord(entry) &&
|
|
101
|
-
typeof entry.id === "string" &&
|
|
102
|
-
entry.enabled === true) {
|
|
103
|
-
ids.push(entry.id);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return ids;
|
|
77
|
+
* Pull the `id`s of enabled models out of an OpenAI-compatible
|
|
78
|
+
* `{ object: "list", data: [{ id, enabled, ... }] }` body. The gateway lists every model in
|
|
79
|
+
* the catalog but marks ones the account can't serve yet with `enabled: false`; only the
|
|
80
|
+
* enabled set is used to detect a reduced catalog.
|
|
81
|
+
*/
|
|
82
|
+
const extractEnabledModelIds = (body) => {
|
|
83
|
+
if (!isRecord(body) || !Array.isArray(body.data)) return null;
|
|
84
|
+
const ids = [];
|
|
85
|
+
for (const entry of body.data) if (isRecord(entry) && typeof entry.id === "string" && entry.enabled === true) ids.push(entry.id);
|
|
86
|
+
return ids;
|
|
107
87
|
};
|
|
108
88
|
/**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
89
|
+
* `GET {baseUrl}/v1/models` → the enabled model ids, or `null` if the catalog can't be read
|
|
90
|
+
* (network / HTTP / parse failure). Returning `null` keeps the notice silent rather than
|
|
91
|
+
* risking a false "reduced models" warning. `/v1/models` is served only on the unified
|
|
92
|
+
* dialect (the `/openai/v1` Responses dialect returns 404).
|
|
93
|
+
*/
|
|
94
|
+
const fetchGatewayModelIds = async (baseUrl, token) => {
|
|
95
|
+
try {
|
|
96
|
+
const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/v1/models`, { headers: { Authorization: `Bearer ${token}` } });
|
|
97
|
+
if (!res.ok) return null;
|
|
98
|
+
return extractEnabledModelIds(await res.json());
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
126
102
|
};
|
|
127
103
|
/**
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (isFreePlan(subscriptionType)) {
|
|
148
|
-
throw new Error(freePlanBlockMessage(aiGatewayUpgradeUrl(orgId)));
|
|
149
|
-
}
|
|
104
|
+
* Refuse to provision the AI Gateway on a Free plan. Called before the `neon.ts` lifecycle
|
|
105
|
+
* commands provision a branch (`config apply` / `deploy`, `checkout`), so a Free-plan user
|
|
106
|
+
* gets a clear "upgrade first" error instead of a credential that can't serve requests.
|
|
107
|
+
*
|
|
108
|
+
* Best-effort on the plan lookup: if the plan can't be determined (network / API failure) we
|
|
109
|
+
* do NOT block, so a transient error never wrongly refuses a paid user's deploy. Only a
|
|
110
|
+
* positively-identified Free plan throws.
|
|
111
|
+
*/
|
|
112
|
+
const assertAiGatewayProvisionable = async (params) => {
|
|
113
|
+
let subscriptionType;
|
|
114
|
+
let orgId;
|
|
115
|
+
try {
|
|
116
|
+
const { data } = await params.apiClient.getProject(params.projectId);
|
|
117
|
+
subscriptionType = data.project.owner?.subscription_type;
|
|
118
|
+
orgId = data.project.org_id ?? void 0;
|
|
119
|
+
} catch {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (isFreePlan(subscriptionType)) throw new Error(freePlanBlockMessage(aiGatewayUpgradeUrl(orgId)));
|
|
150
123
|
};
|
|
151
124
|
/**
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (notice)
|
|
175
|
-
log.warning(notice.message);
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
// A courtesy notice must never break the command that triggered it.
|
|
179
|
-
}
|
|
125
|
+
* Resolve the account's plan (and, when gateway credentials are on hand, its live model
|
|
126
|
+
* catalog) and print the AI Gateway courtesy notice — used by the `neon.ts` lifecycle and
|
|
127
|
+
* `env pull` whenever a branch has the gateway enabled.
|
|
128
|
+
*
|
|
129
|
+
* Pass `gateway` (base URL + token) to enable the reduced-model-set check; omit it (e.g. a
|
|
130
|
+
* dry-run `plan`) to get only the Free-plan notice. This is best-effort: any failure while
|
|
131
|
+
* fetching the plan or catalog is swallowed so it can never break the underlying command.
|
|
132
|
+
*/
|
|
133
|
+
const warnAiGateway = async (params) => {
|
|
134
|
+
try {
|
|
135
|
+
const { data } = await params.apiClient.getProject(params.projectId);
|
|
136
|
+
const subscriptionType = data.project.owner?.subscription_type;
|
|
137
|
+
const orgId = data.project.org_id ?? void 0;
|
|
138
|
+
const modelIds = params.gateway ? await fetchGatewayModelIds(params.gateway.baseUrl, params.gateway.token) ?? void 0 : void 0;
|
|
139
|
+
const notice = buildAiGatewayNotice({
|
|
140
|
+
subscriptionType,
|
|
141
|
+
modelIds,
|
|
142
|
+
upgradeUrl: aiGatewayUpgradeUrl(orgId),
|
|
143
|
+
moreModelsUrl: aiGatewayModelsUrl(params.projectId, params.branchId)
|
|
144
|
+
});
|
|
145
|
+
if (notice) log.warning(notice.message);
|
|
146
|
+
} catch {}
|
|
180
147
|
};
|
|
148
|
+
//#endregion
|
|
149
|
+
export { aiGatewayModelsUrl, aiGatewayUpgradeUrl, assertAiGatewayProvisionable, buildAiGatewayNotice, extractEnabledModelIds, fetchGatewayModelIds, freePlanBlockMessage, hasFlagshipModels, isFreePlan, warnAiGateway };
|
package/dist/utils/api_enums.js
CHANGED
|
@@ -1,33 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// plain string-literal union *types*, which have no runtime value — so code that
|
|
6
|
-
// read `EndpointType.ReadWrite` or `Object.values(EndpointType)` no longer works.
|
|
7
|
-
//
|
|
8
|
-
// These `as const` objects restore that runtime surface, and each is paired with
|
|
9
|
-
// a same-named type whose union is identical to the corresponding `@neon/sdk`
|
|
10
|
-
// type, so values stay assignable in both directions.
|
|
11
|
-
export const EndpointType = {
|
|
12
|
-
ReadOnly: "read_only",
|
|
13
|
-
ReadWrite: "read_write",
|
|
1
|
+
//#region src/utils/api_enums.ts
|
|
2
|
+
const EndpointType = {
|
|
3
|
+
ReadOnly: "read_only",
|
|
4
|
+
ReadWrite: "read_write"
|
|
14
5
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
const NeonAuthOauthProviderId = {
|
|
7
|
+
Google: "google",
|
|
8
|
+
Github: "github",
|
|
9
|
+
Microsoft: "microsoft",
|
|
10
|
+
Vercel: "vercel"
|
|
20
11
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
const NeonAuthOauthProviderType = {
|
|
13
|
+
Standard: "standard",
|
|
14
|
+
Shared: "shared"
|
|
24
15
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
const NeonAuthSupportedAuthProvider = {
|
|
17
|
+
Mock: "mock",
|
|
18
|
+
Stack: "stack",
|
|
19
|
+
BetterAuth: "better_auth"
|
|
29
20
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
const NeonAuthEmailVerificationMethod = {
|
|
22
|
+
Link: "link",
|
|
23
|
+
Otp: "otp"
|
|
33
24
|
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { EndpointType, NeonAuthEmailVerificationMethod, NeonAuthOauthProviderId, NeonAuthOauthProviderType, NeonAuthSupportedAuthProvider };
|
package/dist/utils/auth.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
//#region src/utils/auth.ts
|
|
2
|
+
const extendTokenSet = (tokenSet) => {
|
|
3
|
+
const exp = /* @__PURE__ */ new Date();
|
|
4
|
+
exp.setSeconds(exp.getSeconds() + (tokenSet.expires_in ?? 0));
|
|
5
|
+
return {
|
|
6
|
+
...tokenSet,
|
|
7
|
+
expires_at: exp.getTime()
|
|
8
|
+
};
|
|
5
9
|
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { extendTokenSet };
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
1
|
import { log } from "../log.js";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
//#region src/utils/branch_notice.ts
|
|
3
4
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const suffix = branch.usedDefault ? chalk.dim(" · project default") : "";
|
|
21
|
-
log.info("%s %s %s %s%s", chalk.dim("→"), verb, chalk.cyan.bold(branch.branchName), chalk.dim(`(${branch.branchId})`), suffix);
|
|
5
|
+
* Print a one-line "this command is targeting <branch>" notice to **stderr** so
|
|
6
|
+
* the user can sanity-check they're acting on the branch they think they are —
|
|
7
|
+
* before a `status` / `plan` / `apply` / `env pull` does its work. This is the
|
|
8
|
+
* cheap guardrail that catches "I planned against the wrong branch" / "I pulled
|
|
9
|
+
* env from the wrong branch" before it bites.
|
|
10
|
+
*
|
|
11
|
+
* - Skipped for machine-readable output (`--output json|yaml`) so it never has
|
|
12
|
+
* to be reasoned about by a script; it's stderr-only regardless, keeping
|
|
13
|
+
* `--output table` stdout clean for piping too.
|
|
14
|
+
* - `verb` is the leading phrase, e.g. `'Planning against branch'` →
|
|
15
|
+
* `→ Planning against branch main (br-…)`.
|
|
16
|
+
*/
|
|
17
|
+
const announceTargetBranch = (props, branch, verb) => {
|
|
18
|
+
if (props.output === "json" || props.output === "yaml") return;
|
|
19
|
+
const suffix = branch.usedDefault ? chalk.dim(" · project default") : "";
|
|
20
|
+
log.info("%s %s %s %s%s", chalk.dim("→"), verb, chalk.cyan.bold(branch.branchName), chalk.dim(`(${branch.branchId})`), suffix);
|
|
22
21
|
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { announceTargetBranch };
|
|
@@ -1,103 +1,97 @@
|
|
|
1
|
-
import prompts from "prompts";
|
|
2
|
-
import { retryOnLock } from "../api.js";
|
|
3
1
|
import { isCi } from "../env.js";
|
|
4
2
|
import { log } from "../log.js";
|
|
3
|
+
import { retryOnLock } from "../api.js";
|
|
5
4
|
import { EndpointType } from "./api_enums.js";
|
|
5
|
+
import prompts from "prompts";
|
|
6
|
+
//#region src/utils/branch_picker.ts
|
|
6
7
|
/** Sentinel `value` for the "create a new branch" choice (no branch id can collide). */
|
|
7
8
|
const CREATE_BRANCH_CHOICE = Symbol("create-branch");
|
|
8
9
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
* Render a branch's display name with the same word labels as `neonctl branch list`
|
|
11
|
+
* (`[default]`, `[protected]`) instead of symbols, so the picker reads clearly.
|
|
12
|
+
*/
|
|
12
13
|
const branchLabel = (branch) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
labels.push("[protected]");
|
|
19
|
-
}
|
|
20
|
-
labels.push(branch.name);
|
|
21
|
-
return labels.join(" ");
|
|
14
|
+
const labels = [];
|
|
15
|
+
if (branch.default) labels.push("[default]");
|
|
16
|
+
if (branch.protected) labels.push("[protected]");
|
|
17
|
+
labels.push(branch.name);
|
|
18
|
+
return labels.join(" ");
|
|
22
19
|
};
|
|
23
20
|
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
21
|
+
* Prompt the user to pick a branch from `branches`, with a "+ Create a new branch…" option
|
|
22
|
+
* pinned to the top (mirroring the project/org pickers). The default selection is the
|
|
23
|
+
* project's default branch (the create option sits at index 0, so the default index is
|
|
24
|
+
* offset by one).
|
|
25
|
+
*
|
|
26
|
+
* Throws `opts.nonInteractiveMessage` when there is no TTY (or in CI): the caller knows the
|
|
27
|
+
* right guidance for its command, so the message is supplied rather than hard-coded here.
|
|
28
|
+
*/
|
|
29
|
+
const pickBranchInteractively = async (branches, opts) => {
|
|
30
|
+
if (isCi() || !process.stdout.isTTY) throw new Error(opts.nonInteractiveMessage);
|
|
31
|
+
const defaultBranchIndex = branches.findIndex((b) => b.default);
|
|
32
|
+
const initial = defaultBranchIndex >= 0 ? defaultBranchIndex + 1 : 0;
|
|
33
|
+
const { choice } = await prompts({
|
|
34
|
+
type: "select",
|
|
35
|
+
name: "choice",
|
|
36
|
+
message: opts.message,
|
|
37
|
+
choices: [{
|
|
38
|
+
title: "+ Create a new branch…",
|
|
39
|
+
value: CREATE_BRANCH_CHOICE
|
|
40
|
+
}, ...branches.map((b) => ({
|
|
41
|
+
title: `${branchLabel(b)} (${b.id})`,
|
|
42
|
+
value: b.id
|
|
43
|
+
}))],
|
|
44
|
+
initial
|
|
45
|
+
});
|
|
46
|
+
if (choice === void 0) throw new Error("Aborted: no branch selected.");
|
|
47
|
+
if (choice === CREATE_BRANCH_CHOICE) return {
|
|
48
|
+
kind: "create",
|
|
49
|
+
name: await promptNewBranchName(branches)
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
kind: "existing",
|
|
53
|
+
branchId: choice
|
|
54
|
+
};
|
|
58
55
|
};
|
|
59
56
|
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (trimmed === "") {
|
|
80
|
-
throw new Error("Aborted: no branch name provided.");
|
|
81
|
-
}
|
|
82
|
-
return trimmed;
|
|
57
|
+
* Prompt for a new branch name, rejecting empty input and names already taken on the
|
|
58
|
+
* project (so we never silently select a different, pre-existing branch).
|
|
59
|
+
*/
|
|
60
|
+
const promptNewBranchName = async (branches) => {
|
|
61
|
+
const existing = new Set(branches.map((b) => b.name));
|
|
62
|
+
const { name } = await prompts({
|
|
63
|
+
type: "text",
|
|
64
|
+
name: "name",
|
|
65
|
+
message: "New branch name:",
|
|
66
|
+
validate: (value) => {
|
|
67
|
+
const trimmed = value.trim();
|
|
68
|
+
if (trimmed === "") return "Branch name cannot be empty.";
|
|
69
|
+
if (existing.has(trimmed)) return `A branch named "${trimmed}" already exists.`;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const trimmed = typeof name === "string" ? name.trim() : "";
|
|
74
|
+
if (trimmed === "") throw new Error("Aborted: no branch name provided.");
|
|
75
|
+
return trimmed;
|
|
83
76
|
};
|
|
84
77
|
/**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return data.branch.id;
|
|
78
|
+
* Create a branch with the same defaults as `neonctl branch create --name <name>`:
|
|
79
|
+
* branched from the project's default branch with a read-write compute endpoint. Returns
|
|
80
|
+
* the new branch id.
|
|
81
|
+
*/
|
|
82
|
+
const createBranch = async (apiClient, projectId, name, branches) => {
|
|
83
|
+
const defaultBranch = branches.find((b) => b.default);
|
|
84
|
+
if (!defaultBranch) throw new Error("No default branch found");
|
|
85
|
+
const { data } = await retryOnLock(() => apiClient.createProjectBranch(projectId, {
|
|
86
|
+
branch: {
|
|
87
|
+
name,
|
|
88
|
+
parent_id: defaultBranch.id
|
|
89
|
+
},
|
|
90
|
+
endpoints: [{ type: EndpointType.ReadWrite }]
|
|
91
|
+
}));
|
|
92
|
+
if (defaultBranch.protected) log.warning("The parent branch is protected; a unique role password has been generated for the new branch.");
|
|
93
|
+
log.info("Created branch %s (%s).", data.branch.name, data.branch.id);
|
|
94
|
+
return data.branch.id;
|
|
103
95
|
};
|
|
96
|
+
//#endregion
|
|
97
|
+
export { createBranch, pickBranchInteractively, promptNewBranchName };
|