neon 6.2.4 → 6.4.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 +32 -2
- package/dist/commands/auth.js +2 -1
- package/dist/commands/claim.js +1 -1
- package/dist/commands/config.js +204 -24
- package/dist/commands/mcp.js +4 -2
- package/dist/commands/plugins.js +7 -3
- package/dist/commands/skills.js +4 -2
- package/dist/config_edit.js +237 -0
- package/dist/config_source.js +270 -0
- package/dist/config_template.js +10 -2
- package/dist/context.js +6 -1
- package/dist/index.js +6 -1
- package/dist/init/chrome.js +26 -6
- package/dist/init/copy.js +7 -1
- package/dist/init/mode.js +1 -3
- package/dist/init/plan.js +11 -5
- package/dist/init/run.js +63 -50
- package/dist/init/tooling.js +63 -50
- package/dist/init/wizard.js +17 -18
- package/dist/plugins/plan.js +32 -4
- package/dist/plugins/targets.js +49 -9
- package/dist/sso_recovery.js +92 -0
- package/dist/utils/agent_picker.js +44 -18
- package/dist/utils/enrichers.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -665,7 +665,37 @@ export default async function hello(): Promise<Response> {
|
|
|
665
665
|
}
|
|
666
666
|
```
|
|
667
667
|
|
|
668
|
-
An existing `neon.ts` (or `hello.ts`) is never overwritten.
|
|
668
|
+
An existing `neon.ts` (or `hello.ts`) is never overwritten. To change one, use `config add`.
|
|
669
|
+
|
|
670
|
+
### Editing a `neon.ts` (`config add`)
|
|
671
|
+
|
|
672
|
+
`neon config add` declares one service in the `neon.ts` you already have and leaves the rest of the file (comments, the `branch` closure, formatting) as it was. It is purely local — no auth, no API calls — and creates the starter `neon.ts` first when there is none.
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
neon config add auth # auth: true
|
|
676
|
+
neon config add data-api # dataApi: true, and auth: true (the default provider needs it)
|
|
677
|
+
neon config add ai-gateway # aiGateway: true
|
|
678
|
+
neon config add function sendemail # functions.sendemail + functions/sendemail.ts
|
|
679
|
+
neon config add function sendemail --name "Send email" --source ./src/send.ts
|
|
680
|
+
neon config add bucket uploads # buckets.uploads = { access: 'private' }
|
|
681
|
+
neon config add bucket assets --access public_read
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
Each command prints what it changed and the next step:
|
|
685
|
+
|
|
686
|
+
```
|
|
687
|
+
$ neon config add function sendemail
|
|
688
|
+
INFO: Created functions/sendemail.ts.
|
|
689
|
+
INFO: Updated neon.ts: added functions.sendemail.
|
|
690
|
+
INFO: Next: `neon dev` to run it locally, `neon config apply` to deploy.
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
- **Functions** take a slug of 1-20 lowercase letters and digits, the same rule `plan` enforces. The handler file is written first and an existing file is never overwritten, so pointing `--source` at code you already have works. A `.js` `neon.ts` gets a `.js` handler.
|
|
694
|
+
- Enabling a service that `neon.ts` already enables changes nothing and exits 0. Adding a function slug or bucket name that already exists is an error.
|
|
695
|
+
- `--config <path>` edits a specific file; otherwise the closest `neon.ts` above the current directory is used, and the suggested `plan` / `apply` commands repeat the flag.
|
|
696
|
+
- `--no-install` applies when the command had to create `neon.ts`; editing an existing file never installs anything.
|
|
697
|
+
|
|
698
|
+
`config add` edits the file as text and checks its own output: it re-applies the edit to the result and fails if that is not a no-op. When the file is in a shape it cannot edit safely (a single-line `defineConfig({ auth: true })`, a service declared under `preview`, a value that is not a literal), it changes nothing and prints the exact lines to paste.
|
|
669
699
|
|
|
670
700
|
Four sub-commands plus two top-level aliases drive it:
|
|
671
701
|
|
|
@@ -792,7 +822,7 @@ Recommended installs the Neon plugin for every detected agent (global config, pr
|
|
|
792
822
|
|
|
793
823
|
Unauthenticated `-y` skips linking and prints the next step: sign up at https://neon.com/signup, then `neon auth`, `neon link`, or `neon claim create` (no account, expires in 72 hours unless claimed). `-y` never opens a browser. `-y` with `--project-id` or other account flags links when the CLI is already signed in; otherwise it errors and names `neon auth` or `--claimable`.
|
|
794
824
|
|
|
795
|
-
Custom asks how to add Neon to coding agents (plugin, skills and MCP separately, or skip), then how to get a project (sign in and link, or a claimable project when you are not signed in), then which services `neon.ts` should declare. Postgres is listed first as always included. The package manager is inferred from the directory; Custom asks only when none is detected. `--skill` selects skills (not the plugin) and skips that picker. MCP flags (`--mcp-config-location`, `--mcp-auth`, `--mcp-project-scoped`) select skills and MCP. `--mcp-config-location` is where the config is written: `global` maps to `neon mcp`, and `project` maps to `neon mcp --project`. With `-y`, the location defaults to `global`; interactive Custom asks. If some selected agents cannot write that location, init warns and skips MCP for those agents while continuing with the supported agents. It fails when none of the selected agents support the location. `--mcp-project-scoped` limits MCP tools to the linked Neon project by passing its ID to `neon mcp --project-id`. Omitted is false, including in Custom; there is no prompt. `--no-agent-setup` skips agent setup.
|
|
825
|
+
Custom asks how to add Neon to coding agents (plugin, skills and MCP separately, or skip), then how to get a project (sign in and link, or a claimable project when you are not signed in), then which services `neon.ts` should declare. Skills and MCP use separate agent pickers, and either can be skipped. Postgres is listed first as always included. The package manager is inferred from the directory; Custom asks only when none is detected. `--skill` selects skills (not the plugin) and skips that picker. MCP flags (`--mcp-config-location`, `--mcp-auth`, `--mcp-project-scoped`) select skills and MCP. `--mcp-config-location` is where the config is written: `global` maps to `neon mcp`, and `project` maps to `neon mcp --project`. With `-y`, the location defaults to `global`; interactive Custom asks. If some selected agents cannot write that location, init warns and skips MCP for those agents while continuing with the supported agents. It fails when none of the selected agents support the location. `--mcp-project-scoped` limits MCP tools to the linked Neon project by passing its ID to `neon mcp --project-id`. Omitted is false, including in Custom; there is no prompt. `--no-agent-setup` skips agent setup.
|
|
796
826
|
|
|
797
827
|
Empty directories are set up in place. `neon init` does not scaffold starter files.
|
|
798
828
|
|
package/dist/commands/auth.js
CHANGED
|
@@ -5,7 +5,7 @@ import { log } from "../log.js";
|
|
|
5
5
|
import { getApiClient, isNeonApiError } from "../api.js";
|
|
6
6
|
import { setAuthContext } from "../auth_context.js";
|
|
7
7
|
import { a as isOwnedCredentialPath } from "../_chunks/paths-DMq0Lt7a.js";
|
|
8
|
-
import { currentContextFile, isAskCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile } from "../context.js";
|
|
8
|
+
import { currentContextFile, isAskCommand, isClaimCommand, isConfigAdd, isConfigInit, isCurrentBranchProbe, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile } from "../context.js";
|
|
9
9
|
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-CvnFEQyd.js";
|
|
10
10
|
import { extendTokenSet } from "../utils/auth.js";
|
|
11
11
|
import { AuthRefreshError, auth, classifyRefreshFailure, refreshToken } from "../auth.js";
|
|
@@ -296,6 +296,7 @@ const ensureAuth = async (props) => {
|
|
|
296
296
|
if (props._.length === 0 || props.help) return;
|
|
297
297
|
if (isCurrentBranchProbe(props)) return;
|
|
298
298
|
if (isConfigInit(props)) return;
|
|
299
|
+
if (isConfigAdd(props)) return;
|
|
299
300
|
if (isProfileCommand(props)) return;
|
|
300
301
|
if (isGitLocalCommand(props)) return;
|
|
301
302
|
if (isUnfollowedGitHookSync(props)) return;
|
package/dist/commands/claim.js
CHANGED
|
@@ -6,8 +6,8 @@ import { applyContext, contextBranch, readContextFile } from "../context.js";
|
|
|
6
6
|
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
7
7
|
import { ClaimableClient, ClaimableServiceError } from "../claimable/api.js";
|
|
8
8
|
import { assertionHasExpired, listClaimableCredentials, readClaimableCredentials, readLinkedClaimableCredentials, removeClaimableCredentials, writeClaimableCredentials } from "../claimable/state.js";
|
|
9
|
-
import { claimableDataApiCreateBody, declaredNeonServices } from "../config_services.js";
|
|
10
9
|
import { NEON_SERVICES, deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
10
|
+
import { claimableDataApiCreateBody, declaredNeonServices } from "../config_services.js";
|
|
11
11
|
import { resolveEnvFilePath } from "../env_file.js";
|
|
12
12
|
import { writer } from "../writer.js";
|
|
13
13
|
import { noPassthrough } from "../utils/flags.js";
|
package/dist/commands/config.js
CHANGED
|
@@ -4,10 +4,11 @@ import { log } from "../log.js";
|
|
|
4
4
|
import { getApiClient } from "../api.js";
|
|
5
5
|
import { contextBranch, ensureDirectoryGitignored, readContextFile } from "../context.js";
|
|
6
6
|
import { getCliName } from "../utils/cli_name.js";
|
|
7
|
+
import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
8
|
+
import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE, FUNCTION_FILENAME, FUNCTION_SLUG, FUNCTION_TEMPLATE, REQUIRED_PACKAGES, renderFunctionSource, renderNeonConfig, renderNeonConfigFromView } from "../config_template.js";
|
|
9
|
+
import { editNeonConfig } from "../config_edit.js";
|
|
7
10
|
import { toNeonConfigView } from "../config_format.js";
|
|
8
11
|
import { declaredNeonServices } from "../config_services.js";
|
|
9
|
-
import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
10
|
-
import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE, FUNCTION_FILENAME, FUNCTION_SLUG, FUNCTION_TEMPLATE, REQUIRED_PACKAGES, renderNeonConfig, renderNeonConfigFromView } from "../config_template.js";
|
|
11
12
|
import { loadEnvFileIntoProcess } from "../env_file.js";
|
|
12
13
|
import { writer } from "../writer.js";
|
|
13
14
|
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
@@ -21,14 +22,16 @@ import { resolveHookEnv, runDeployAfterHook, runDeployBeforeHook } from "../util
|
|
|
21
22
|
import { pickServicesInteractively } from "../utils/service_picker.js";
|
|
22
23
|
import { zipBundle } from "../utils/zip.js";
|
|
23
24
|
import { autoPullEnvAfterPin } from "./env.js";
|
|
24
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
25
|
-
import { dirname, join } from "node:path";
|
|
25
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
26
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
27
|
+
import { homedir } from "node:os";
|
|
26
28
|
import chalk from "chalk";
|
|
27
29
|
import { packagesToStage, previewGaWarningForConfig, resolveConfig } from "@neon/config";
|
|
28
30
|
import { PushConflictError, apply, assertZipWithinLimits, bundleAsIs, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, loadConfigFromFile as loadConfigFromFile$1, plan, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle } from "@neon/config-runtime";
|
|
29
31
|
//#region src/commands/config.ts
|
|
30
32
|
var config_exports = /* @__PURE__ */ __exportAll({
|
|
31
33
|
ConfigInstallFailed: () => ConfigInstallFailed,
|
|
34
|
+
addCmd: () => addCmd,
|
|
32
35
|
applyCmd: () => applyCmd,
|
|
33
36
|
applyFlags: () => applyFlags,
|
|
34
37
|
applyPolicyOnCreate: () => applyPolicyOnCreate,
|
|
@@ -213,6 +216,28 @@ const seedFromBranch = async (props) => {
|
|
|
213
216
|
branchName: live.branch.name
|
|
214
217
|
};
|
|
215
218
|
};
|
|
219
|
+
/** Install the config packages the project is missing, or say how to. */
|
|
220
|
+
const ensureConfigPackages = async (cwd, props) => {
|
|
221
|
+
const run = props.run ?? runCommand;
|
|
222
|
+
const missing = missingDependencies(cwd);
|
|
223
|
+
if (missing.length === 0) {
|
|
224
|
+
if (!props.silent) log.info("%s are already installed.", REQUIRED_PACKAGES.join(" and "));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const pm = props.packageManager ?? resolvePackageManager(cwd);
|
|
228
|
+
const args = installArgs(pm, missing);
|
|
229
|
+
if (props.install === false) {
|
|
230
|
+
if (!props.silent) log.info("Install the Neon config packages to use neon.ts: %s", formatInstallCommand(pm, missing));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
ensureDirectoryGitignored(join(cwd, "node_modules"));
|
|
234
|
+
if (!props.silent) log.info("Installing %s with %s…", missing.join(", "), pm);
|
|
235
|
+
if (!await run(pm, args, cwd)) {
|
|
236
|
+
const command = formatInstallCommand(pm, missing);
|
|
237
|
+
if (props.requireInstall) throw new ConfigInstallFailed(command);
|
|
238
|
+
log.warning("Could not install the config packages automatically. Run by hand: %s", command);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
216
241
|
/**
|
|
217
242
|
* Scaffold a `neon.ts` policy and make sure the Neon config packages are
|
|
218
243
|
* installed, so a project can go straight to `neon config plan` / `apply`.
|
|
@@ -220,10 +245,12 @@ const seedFromBranch = async (props) => {
|
|
|
220
245
|
*/
|
|
221
246
|
const initCmd = async (props) => {
|
|
222
247
|
const cwd = props.cwd ?? process.cwd();
|
|
223
|
-
const run = props.run ?? runCommand;
|
|
224
248
|
const existing = neonConfigFilename(cwd);
|
|
225
249
|
if (existing) {
|
|
226
|
-
if (!props.silent)
|
|
250
|
+
if (!props.silent) {
|
|
251
|
+
log.info("Found an existing %s — leaving it untouched.", existing);
|
|
252
|
+
if (props.services !== void 0) log.warning("--services was ignored. To declare services in the existing %s, use `%s config add`.", existing, getCliName());
|
|
253
|
+
}
|
|
227
254
|
} else if (props.fromBranch) {
|
|
228
255
|
const { source, seeded, branchName } = await seedFromBranch(props);
|
|
229
256
|
writeFileSync(join(cwd, "neon.ts"), source);
|
|
@@ -240,26 +267,139 @@ const initCmd = async (props) => {
|
|
|
240
267
|
}
|
|
241
268
|
if (services.includes("functions")) scaffoldFunction(cwd);
|
|
242
269
|
}
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
270
|
+
await ensureConfigPackages(cwd, props);
|
|
271
|
+
if (!props.silent) log.info("Next: edit neon.ts, then run `neon config plan` to preview and `neon config apply`.");
|
|
272
|
+
};
|
|
273
|
+
/** The rule the platform enforces on function slugs, so a bad one fails before any write. */
|
|
274
|
+
const FUNCTION_SLUG_PATTERN = /^[a-z0-9]{1,20}$/;
|
|
275
|
+
const SCRIPT_EXTENSION = /\.(?:ts|mts|cts|js|mjs|cjs)$/;
|
|
276
|
+
const TYPESCRIPT_EXTENSION = /\.(?:ts|mts|cts)$/;
|
|
277
|
+
/**
|
|
278
|
+
* The nearest neon.ts, found the way the config loader finds it: walk up from `start`, stop
|
|
279
|
+
* at the first directory holding `.git` (or at the home directory).
|
|
280
|
+
*/
|
|
281
|
+
const findNeonConfigUpward = (start) => {
|
|
282
|
+
let dir = resolve(start);
|
|
283
|
+
for (;;) {
|
|
284
|
+
const name = neonConfigFilename(dir);
|
|
285
|
+
if (name) return join(dir, name);
|
|
286
|
+
if (existsSync(join(dir, ".git")) || dir === homedir()) return void 0;
|
|
287
|
+
const parent = dirname(dir);
|
|
288
|
+
if (parent === dir) return void 0;
|
|
289
|
+
dir = parent;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const asRelativeModulePath = (path) => path.startsWith("./") || path.startsWith("../") || path.startsWith("/") ? path : `./${path}`;
|
|
293
|
+
const shellQuote = (value) => /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
294
|
+
/**
|
|
295
|
+
* `explicitConfig` is the `--config` the user passed. The commands suggested afterwards must
|
|
296
|
+
* carry it, or they would act on whichever `neon.ts` a walk up from cwd finds instead.
|
|
297
|
+
*/
|
|
298
|
+
const planAdd = (target, configPath, explicitConfig) => {
|
|
299
|
+
const language = TYPESCRIPT_EXTENSION.test(configPath) ? "ts" : "js";
|
|
300
|
+
const cli = getCliName();
|
|
301
|
+
const flag = explicitConfig ? ` --config ${shellQuote(explicitConfig)}` : "";
|
|
302
|
+
const provision = `Next: \`${cli} config plan${flag}\` to preview, \`${cli} config apply${flag}\` to provision.`;
|
|
303
|
+
switch (target.kind) {
|
|
304
|
+
case "service": return {
|
|
305
|
+
edit: {
|
|
306
|
+
kind: "service",
|
|
307
|
+
service: target.service
|
|
308
|
+
},
|
|
309
|
+
next: provision
|
|
310
|
+
};
|
|
311
|
+
case "bucket":
|
|
312
|
+
if (target.name.length < 1 || target.name.length > 255) throw new Error("A bucket name must be 1-255 characters.");
|
|
313
|
+
if (target.name === "__proto__") throw new Error("\"__proto__\" cannot be used as a bucket name.");
|
|
314
|
+
return {
|
|
315
|
+
edit: {
|
|
316
|
+
kind: "bucket",
|
|
317
|
+
name: target.name,
|
|
318
|
+
access: target.access ?? "private"
|
|
319
|
+
},
|
|
320
|
+
next: provision
|
|
321
|
+
};
|
|
322
|
+
case "function": {
|
|
323
|
+
if (!FUNCTION_SLUG_PATTERN.test(target.slug)) {
|
|
324
|
+
const suggestion = target.slug.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 20);
|
|
325
|
+
throw new Error(`"${target.slug}" is not a valid function slug: use 1-20 lowercase letters and digits, no hyphens.` + (suggestion ? ` Try "${suggestion}".` : ""));
|
|
258
326
|
}
|
|
327
|
+
if (target.source !== void 0 && !SCRIPT_EXTENSION.test(target.source)) throw new Error(`--source must be a .ts or .js file, got "${target.source}". Declare a directory source in neon.ts by hand.`);
|
|
328
|
+
const source = asRelativeModulePath(target.source ?? `functions/${target.slug}.${language}`);
|
|
329
|
+
return {
|
|
330
|
+
edit: {
|
|
331
|
+
kind: "function",
|
|
332
|
+
slug: target.slug,
|
|
333
|
+
name: target.name ?? target.slug,
|
|
334
|
+
source
|
|
335
|
+
},
|
|
336
|
+
sourceFile: resolve(dirname(configPath), source),
|
|
337
|
+
next: explicitConfig ? `Next: \`${cli} config apply${flag}\` to deploy.` : `Next: \`${cli} dev\` to run it locally, \`${cli} config apply\` to deploy.`
|
|
338
|
+
};
|
|
259
339
|
}
|
|
260
340
|
}
|
|
261
|
-
if (!props.silent) log.info("Next: edit neon.ts, then run `neon config plan` to preview and `neon config apply`.");
|
|
262
341
|
};
|
|
342
|
+
/**
|
|
343
|
+
* Declare a service, function, or bucket in the project's `neon.ts` with a text edit that
|
|
344
|
+
* leaves the rest of the file alone, creating the file (and installing the config packages)
|
|
345
|
+
* when the project has none. Local only, like `config init`.
|
|
346
|
+
*/
|
|
347
|
+
const addCmd = async (props) => {
|
|
348
|
+
const cwd = props.cwd ?? process.cwd();
|
|
349
|
+
let existing;
|
|
350
|
+
if (props.config) {
|
|
351
|
+
existing = resolve(cwd, props.config);
|
|
352
|
+
if (!existsSync(existing)) throw new Error(`--config ${props.config} does not exist.`);
|
|
353
|
+
} else existing = findNeonConfigUpward(cwd);
|
|
354
|
+
const configPath = existing ?? join(cwd, "neon.ts");
|
|
355
|
+
const shown = (path) => relative(cwd, path) || path;
|
|
356
|
+
const plan = planAdd(props.target, configPath, props.config);
|
|
357
|
+
const { source, changes } = editNeonConfig(existing ? readFileSync(existing, "utf8") : renderNeonConfig([]), plan.edit);
|
|
358
|
+
if (changes.length === 0) {
|
|
359
|
+
if (props.target.kind === "service") {
|
|
360
|
+
log.info("%s already enables that; nothing to change.", shown(configPath));
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
throw new Error(`${shown(configPath)} already declares ${props.target.kind} "${props.target.kind === "function" ? props.target.slug : props.target.name}". Edit it there, or pick another name.`);
|
|
364
|
+
}
|
|
365
|
+
if (plan.sourceFile) {
|
|
366
|
+
if (existsSync(plan.sourceFile)) log.info("Found an existing %s — leaving it untouched.", shown(plan.sourceFile));
|
|
367
|
+
else {
|
|
368
|
+
mkdirSync(dirname(plan.sourceFile), { recursive: true });
|
|
369
|
+
const slug = props.target.kind === "function" ? props.target.slug : "";
|
|
370
|
+
writeFileSync(plan.sourceFile, renderFunctionSource(slug, TYPESCRIPT_EXTENSION.test(plan.sourceFile) ? "ts" : "js"));
|
|
371
|
+
log.info("Created %s.", shown(plan.sourceFile));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
writeFileSync(configPath, source);
|
|
375
|
+
log.info("%s %s: %s.", existing ? "Updated" : "Created", shown(configPath), changes.join(", "));
|
|
376
|
+
if (!existing) await ensureConfigPackages(dirname(configPath), props);
|
|
377
|
+
log.info("%s", plan.next);
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Options every `config add` sub-command takes. `--project-id` and `--branch` are inherited
|
|
381
|
+
* from `config` but mean nothing to a command that never leaves the working tree, so they are
|
|
382
|
+
* hidden from its help.
|
|
383
|
+
*/
|
|
384
|
+
const addSharedOptions = (yargs) => yargs.strict().options({
|
|
385
|
+
"project-id": { hidden: true },
|
|
386
|
+
branch: { hidden: true },
|
|
387
|
+
config: {
|
|
388
|
+
describe: "Path to the neon.ts to edit (defaults to walking up from cwd; created in cwd when there is none)",
|
|
389
|
+
type: "string"
|
|
390
|
+
},
|
|
391
|
+
install: {
|
|
392
|
+
describe: "Install @neon/config and @neon/env when creating neon.ts. On by default; use --no-install to just print the command.",
|
|
393
|
+
type: "boolean",
|
|
394
|
+
default: true
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
/** The `config add` props for a sub-command's parsed flags. */
|
|
398
|
+
const addProps = (args, target) => ({
|
|
399
|
+
target,
|
|
400
|
+
install: args.install,
|
|
401
|
+
...args.config !== void 0 ? { config: args.config } : {}
|
|
402
|
+
});
|
|
263
403
|
const command = "config";
|
|
264
404
|
const describe = "Manage a branch with a neon.ts policy";
|
|
265
405
|
const builder = (argv) => argv.usage("$0 config <sub-command> [options]").options({
|
|
@@ -296,7 +436,47 @@ const builder = (argv) => argv.usage("$0 config <sub-command> [options]").option
|
|
|
296
436
|
...envFlag,
|
|
297
437
|
...applyFlags,
|
|
298
438
|
...envPullFlag
|
|
299
|
-
}), (args) => applyCmd(args)).command("
|
|
439
|
+
}), (args) => applyCmd(args)).command("add", "Add a service, function, or bucket to neon.ts (creates it if missing)", (yargs) => yargs.command("auth", "Enable Neon Auth (auth: true)", addSharedOptions, (args) => addCmd(addProps(args, {
|
|
440
|
+
kind: "service",
|
|
441
|
+
service: "auth"
|
|
442
|
+
}))).command("data-api", "Enable the Data API (dataApi: true) and Neon Auth, which the default provider requires", addSharedOptions, (args) => addCmd(addProps(args, {
|
|
443
|
+
kind: "service",
|
|
444
|
+
service: "data-api"
|
|
445
|
+
}))).command("ai-gateway", "Enable the AI Gateway (aiGateway: true)", addSharedOptions, (args) => addCmd(addProps(args, {
|
|
446
|
+
kind: "service",
|
|
447
|
+
service: "ai-gateway"
|
|
448
|
+
}))).command("function <slug>", "Declare a Neon Function and create its handler file", (yargs) => addSharedOptions(yargs).example("$0 config add function sendemail", "Creates functions/sendemail.ts and declares it").example("$0 config add function sendemail --source src/send.ts", "Declares an existing handler").positional("slug", {
|
|
449
|
+
describe: "Function slug: 1-20 lowercase letters and digits",
|
|
450
|
+
type: "string",
|
|
451
|
+
demandOption: true
|
|
452
|
+
}).options({
|
|
453
|
+
name: {
|
|
454
|
+
describe: "Display name. Defaults to the slug",
|
|
455
|
+
type: "string"
|
|
456
|
+
},
|
|
457
|
+
source: {
|
|
458
|
+
describe: "Handler file, relative to neon.ts. Defaults to functions/<slug>.ts. Created when missing, left alone when it exists",
|
|
459
|
+
type: "string"
|
|
460
|
+
}
|
|
461
|
+
}), (args) => addCmd(addProps(args, {
|
|
462
|
+
kind: "function",
|
|
463
|
+
slug: args.slug,
|
|
464
|
+
...args.name !== void 0 ? { name: args.name } : {},
|
|
465
|
+
...args.source !== void 0 ? { source: args.source } : {}
|
|
466
|
+
}))).command("bucket <name>", "Declare an Object Storage bucket", (yargs) => addSharedOptions(yargs).example("$0 config add bucket assets --access public_read", "Declares a bucket that allows anonymous reads").positional("name", {
|
|
467
|
+
describe: "Bucket name",
|
|
468
|
+
type: "string",
|
|
469
|
+
demandOption: true
|
|
470
|
+
}).options({ access: {
|
|
471
|
+
describe: "Anonymous access: private requires credentials, public_read allows anonymous reads",
|
|
472
|
+
type: "string",
|
|
473
|
+
choices: ["private", "public_read"],
|
|
474
|
+
default: "private"
|
|
475
|
+
} }), (args) => addCmd(addProps(args, {
|
|
476
|
+
kind: "bucket",
|
|
477
|
+
name: args.name,
|
|
478
|
+
access: args.access
|
|
479
|
+
}))).demandCommand(1, "Specify what to add: auth, data-api, ai-gateway, function, or bucket.")).command("init", "Scaffold a neon.ts policy and install the Neon config packages", (yargs) => yargs.options({
|
|
300
480
|
install: {
|
|
301
481
|
describe: "Install @neon/config and @neon/env if they're missing. On by default; use --no-install to just print the command.",
|
|
302
482
|
type: "boolean",
|
|
@@ -803,4 +983,4 @@ const createBranchFromPolicyOnCheckout = async (props) => {
|
|
|
803
983
|
}
|
|
804
984
|
};
|
|
805
985
|
//#endregion
|
|
806
|
-
export { ConfigInstallFailed, applyCmd, applyFlags, applyPolicyOnCreate, builder, command, createBranchFromPolicyOnCheckout, describe, envFlag, envPullFlag, handler, hasNeonConfigFile, initCmd, neonConfigFilename, planCmd, status, config_exports as t };
|
|
986
|
+
export { ConfigInstallFailed, addCmd, applyCmd, applyFlags, applyPolicyOnCreate, builder, command, createBranchFromPolicyOnCheckout, describe, envFlag, envPullFlag, handler, hasNeonConfigFile, initCmd, neonConfigFilename, planCmd, status, config_exports as t };
|
package/dist/commands/mcp.js
CHANGED
|
@@ -6,10 +6,11 @@ import { getCliName } from "../utils/cli_name.js";
|
|
|
6
6
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
7
7
|
import { writer } from "../writer.js";
|
|
8
8
|
import { noPassthrough, single } from "../utils/flags.js";
|
|
9
|
+
import { AGENT_MCP_SKIP_MESSAGE } from "../init/copy.js";
|
|
9
10
|
import { NEON_MCP_CATEGORIES, NEON_MCP_URL, existingNeonApiKey, installNeonMcpServer, neonMcpUrl, parseMcpCategories, trackedProjectMcpConfig } from "../mcp/install.js";
|
|
10
11
|
import { mcpInstallableAgents, resolveInstallTargets } from "../mcp/targets.js";
|
|
11
12
|
import { mintMcpApiKey, mintedKeyRevokeCommand, withdrawMintedKey } from "../mcp/mint.js";
|
|
12
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
13
|
+
import { canPickAgentsInteractively, skippableAgentPicker } from "../utils/agent_picker.js";
|
|
13
14
|
import { resolveMcpPlan } from "../mcp/plan.js";
|
|
14
15
|
//#region src/commands/mcp.ts
|
|
15
16
|
var mcp_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -113,7 +114,8 @@ const setupNeonMcp = async (options) => {
|
|
|
113
114
|
readOnly: options.readOnly === true,
|
|
114
115
|
projectId: options.projectId,
|
|
115
116
|
categories: options.category ?? [],
|
|
116
|
-
linkedProjectId
|
|
117
|
+
linkedProjectId,
|
|
118
|
+
pickAgents: skippableAgentPicker(options.allowAgentSkip, AGENT_MCP_SKIP_MESSAGE)
|
|
117
119
|
});
|
|
118
120
|
const { install, skipped } = resolveInstallTargets({
|
|
119
121
|
agents: plan.agents,
|
package/dist/commands/plugins.js
CHANGED
|
@@ -4,11 +4,12 @@ import { recordCommandSuccessExtras } from "../analytics.js";
|
|
|
4
4
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
5
5
|
import { writer } from "../writer.js";
|
|
6
6
|
import { noPassthrough } from "../utils/flags.js";
|
|
7
|
+
import { AGENT_PLUGIN_SKIP_MESSAGE } from "../init/copy.js";
|
|
7
8
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
8
9
|
import { NEON_MCP_URL } from "../mcp/install.js";
|
|
9
10
|
import "../init/agents.js";
|
|
10
11
|
import { pluginsInstallableAgents } from "../plugins/targets.js";
|
|
11
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
12
|
+
import { canPickAgentsInteractively, skippableAgentPicker } from "../utils/agent_picker.js";
|
|
12
13
|
import { resolvePluginsPlan } from "../plugins/plan.js";
|
|
13
14
|
import { NEON_PLUGIN_NAME, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, runPluginsCli } from "../plugins/run.js";
|
|
14
15
|
//#region src/commands/plugins.ts
|
|
@@ -71,10 +72,12 @@ const installPlugins = async (options) => {
|
|
|
71
72
|
agents: options.agents ?? [],
|
|
72
73
|
yes,
|
|
73
74
|
cwd,
|
|
74
|
-
interactive
|
|
75
|
+
interactive,
|
|
76
|
+
pickAgents: skippableAgentPicker(options.allowAgentSkip, AGENT_PLUGIN_SKIP_MESSAGE)
|
|
75
77
|
});
|
|
76
78
|
for (const agent of plan.skipped) log.warning("Skipping %s: no plugins mapping.", getAgentDisplayName(agent));
|
|
77
79
|
for (const agent of plan.userScopeSkipped) log.warning("Skipping %s: plugins are user-level. Pass --global.", getAgentDisplayName(agent));
|
|
80
|
+
for (const { agent, command } of plan.missingCommands) log.warning("Skipping %s: the plugin installs through \"%s\", which was not found on PATH.", getAgentDisplayName(agent), command);
|
|
78
81
|
const rows = [];
|
|
79
82
|
const failed = [];
|
|
80
83
|
const scope = scopeLabel(plan.scope);
|
|
@@ -147,7 +150,8 @@ const reportPluginsInstall = (props, outcome) => {
|
|
|
147
150
|
title: "Plugins"
|
|
148
151
|
});
|
|
149
152
|
out.end();
|
|
150
|
-
if (outcome.
|
|
153
|
+
if (outcome.rows.length === 0) log.info("No Neon plugins installed.");
|
|
154
|
+
else if (outcome.failed.length === 0) log.info(outcome.scope === "project" ? "Installed the Neon plugin (project)." : "Installed the Neon plugin (user).");
|
|
151
155
|
};
|
|
152
156
|
const handler = async (props) => {
|
|
153
157
|
const outcome = await installPlugins({
|
package/dist/commands/skills.js
CHANGED
|
@@ -4,10 +4,11 @@ import { recordCommandSuccessExtras } from "../analytics.js";
|
|
|
4
4
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
5
5
|
import { writer } from "../writer.js";
|
|
6
6
|
import { noPassthrough } from "../utils/flags.js";
|
|
7
|
+
import { AGENT_SKILLS_SKIP_MESSAGE } from "../init/copy.js";
|
|
7
8
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
8
9
|
import "../init/agents.js";
|
|
9
10
|
import { mappedSkillsAgentNames, skillsInstallableAgents } from "../skills/targets.js";
|
|
10
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
11
|
+
import { canPickAgentsInteractively, skippableAgentPicker } from "../utils/agent_picker.js";
|
|
11
12
|
import { skillsHelpValues, skillsYesHelp } from "../skills/catalog.js";
|
|
12
13
|
import { confirmSkillsUpdate } from "../skills/wizard.js";
|
|
13
14
|
import { assertSkillsCanRun, resolveSkillsPlan } from "../skills/plan.js";
|
|
@@ -102,7 +103,8 @@ const installSkills = async (options) => {
|
|
|
102
103
|
skills: options.skills ?? [],
|
|
103
104
|
yes,
|
|
104
105
|
cwd,
|
|
105
|
-
interactive
|
|
106
|
+
interactive,
|
|
107
|
+
pickAgents: skippableAgentPicker(options.allowAgentSkip, AGENT_SKILLS_SKIP_MESSAGE)
|
|
106
108
|
});
|
|
107
109
|
const mapped = mappedSkillsAgentNames(plan.agents);
|
|
108
110
|
for (const agent of plan.skipped) log.warning("Skipping %s: no skills mapping.", getAgentDisplayName(agent));
|