neon 4.10.0 → 4.10.2
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 +5 -5
- package/dist/commands/bootstrap.js +179 -79
- package/dist/commands/init.js +23 -5
- package/dist/errors.js +2 -1
- package/dist/index.js +3 -2
- package/dist/init/chrome.js +44 -0
- package/dist/init/plan.js +16 -1
- package/dist/init/tooling.js +2 -1
- package/dist/skills/catalog.js +0 -5
- package/dist/utils/inspect_queries.js +6 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -683,7 +683,7 @@ When a package cannot be bundled — a native addon with no esbuild loader, or a
|
|
|
683
683
|
|
|
684
684
|
`neon bootstrap` copies a Neon starter template into a new (or current) directory — conceptually like `degit`, but it only pulls from a small set of templates we maintain in the public [`neondatabase/examples`](https://github.com/neondatabase/examples) repo. The template copy needs no Neon login: it downloads files from GitHub.
|
|
685
685
|
|
|
686
|
-
After scaffolding, an interactive terminal
|
|
686
|
+
After scaffolding, an interactive terminal asks about dependency install, git, agent tooling (the Neon plugin, or skills and MCP separately — never both), and `neon link` before running those steps. Dependency install is last, except when the template has a `neon.ts` and you chose to link — then install runs first so link can pull env. `--default` / `-y` skips the template, install, git, and agent pickers, then runs `link --yes`. `link --yes` still asks for a project unless one is already linked. `--no-agent-setup` and `--no-link` skip those. Non-interactive without `--default` prints next steps and does not install, set up agents, or link.
|
|
687
687
|
|
|
688
688
|
Pass a target directory (or `.` for the current one). In an interactive terminal you pick the template from a list; in CI / non-interactive contexts pass `--template <id>`.
|
|
689
689
|
|
|
@@ -707,7 +707,7 @@ The target directory must be empty unless you pass `--force` (a lone `.git` is i
|
|
|
707
707
|
|
|
708
708
|
`neon init` sets up this directory for Neon.
|
|
709
709
|
|
|
710
|
-
An empty directory (nothing except `.git`) runs `neon bootstrap .` and stops. With `-y` that is `neon bootstrap . --default`. Bootstrap handles scaffolding, agent tooling, and linking.
|
|
710
|
+
An empty directory (nothing except `.git`) runs `neon bootstrap .` and stops. With `-y` that is `neon bootstrap . --default`. Bootstrap handles scaffolding, agent tooling, and linking. Interactive bootstrap prints a NEON banner, asks every setup question, then runs the work — dependency install last, except when a `neon.ts` needs deps before `link`.
|
|
711
711
|
|
|
712
712
|
An existing app installs agent tooling, then `neon link` unless `.neon` already has a projectId, then `neon config init`. Interactive `config init` opens the services picker; `-y` uses `--services none` (starter policy).
|
|
713
713
|
|
|
@@ -791,7 +791,7 @@ On a TTY the command asks which agents and which skills, then shows a summary to
|
|
|
791
791
|
|
|
792
792
|
`-y` skips those questions and installs the default skills. `--skill` / `-s` names specific skills and skips the skill picker. `--agent` and `--global` still apply. Without a TTY, pass `-y` or `--skill`. `--agent` alone is not enough.
|
|
793
793
|
|
|
794
|
-
`--skill` names by source repo: `neondatabase/agent-skills` (`
|
|
794
|
+
`--skill` names by source repo: `neondatabase/agent-skills` (`neon`, `neon-ai-gateway`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`); `neondatabase/neon-for-agent-platforms` (`neon-postgres-agent-platforms`).
|
|
795
795
|
|
|
796
796
|
`--agent` names match `neon mcp`, minus agents that cannot install skills: `antigravity`, `cline`, `cline-cli`, `claude-code`, `claude-desktop`, `codex`, `cursor`, `gemini-cli`, `goose`, `github-copilot-cli`, `grok-build`, `opencode`, `vscode`, `windsurf`, `zed`. `mcporter` is a known MCP name that is then skipped. `neon skills --help` lists the same skill and agent values, and that `-y` leaves out `neon-postgres-agent-platforms`.
|
|
797
797
|
|
|
@@ -821,7 +821,7 @@ Default scope is `project`. `--global` is `user`. On macOS and Linux, Cursor and
|
|
|
821
821
|
|
|
822
822
|
`--agent` names with a plugins mapping: `claude-code`, `claude-desktop`, `codex`, `cursor`, `github-copilot-cli`, `grok-build`, `vscode`. `claude-desktop` installs as Claude Code; naming both produces one install and lists both names in the table. `mcporter` is a known MCP name that is then skipped.
|
|
823
823
|
|
|
824
|
-
The plugins CLI installs every plugin it finds in the Neon plugin package. Today that is `neon-postgres` from `neondatabase/agent-skills`. It includes the Neon MCP server (`https://mcp.neon.tech/mcp`) and these skills: `neon`, `neon-ai-gateway`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`. It does not include `
|
|
824
|
+
The plugins CLI installs every plugin it finds in the Neon plugin package. Today that is `neon-postgres` from `neondatabase/agent-skills`. It includes the Neon MCP server (`https://mcp.neon.tech/mcp`) and these skills: `neon`, `neon-ai-gateway`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`. It does not include `neon-postgres-agent-platforms`.
|
|
825
825
|
|
|
826
826
|
`neon plugins --help` lists the plugin, those contents, and the supported `--agent` names.
|
|
827
827
|
|
|
@@ -871,7 +871,7 @@ All sub-commands honor the [global options](#global-options), including `--outpu
|
|
|
871
871
|
|
|
872
872
|
## Database diagnostics (`inspect`)
|
|
873
873
|
|
|
874
|
-
`neon inspect db stalled-queries` takes a read-only snapshot of active queries that have run for more than 30 seconds and groups parallel workers with their leader. Table output shows duration, wait event, blocking pids, role, query group, and query. `--output json` adds timestamps, query IDs, pids, database, and the rest of the row. A blocking pid can belong to an idle-in-transaction backend this command does not list; `neon inspect db locks` shows lock holders.
|
|
874
|
+
`neon inspect db stalled-queries` takes a read-only snapshot of active queries that have run for more than 30 seconds and groups parallel workers with their leader. Oldest group first. Table output shows duration, wait event, blocking pids, role, query group, and query. `--output json` adds timestamps, query IDs, pids, database, and the rest of the row. A blocking pid can belong to an idle-in-transaction backend this command does not list; `neon inspect db locks` shows lock holders.
|
|
875
875
|
|
|
876
876
|
```bash
|
|
877
877
|
neon inspect db stalled-queries
|
|
@@ -6,8 +6,10 @@ import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
|
6
6
|
import { writer } from "../writer.js";
|
|
7
7
|
import { FALLBACK_TEMPLATES, ensureTargetUsable, fetchTemplates, findTemplate, scaffoldTemplate, templateIds } from "../init/bootstrap.js";
|
|
8
8
|
import { spawnCliChild } from "../init/child.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { agentSetupDoneLabel, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner } from "../init/chrome.js";
|
|
10
|
+
import { postScaffoldActions, projectContextFile } from "../init/plan.js";
|
|
11
|
+
import { pickAgentSetupInteractively } from "../init/wizard.js";
|
|
12
|
+
import { runAgentTooling, runInitSteps } from "../init/tooling.js";
|
|
11
13
|
import { formatInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolvePackageManager, runCommand } from "../utils/package_manager.js";
|
|
12
14
|
import { existsSync } from "node:fs";
|
|
13
15
|
import { join, relative, resolve } from "node:path";
|
|
@@ -63,7 +65,7 @@ const builder = (argv) => argv.usage("$0 bootstrap [directory] [options]").posit
|
|
|
63
65
|
default: true
|
|
64
66
|
},
|
|
65
67
|
link: {
|
|
66
|
-
describe: `Run \`${getCliName()} link\`
|
|
68
|
+
describe: `Run \`${getCliName()} link\` after scaffolding. Templates with neon.ts link after install so env pull works; otherwise link runs before install. In interactive mode this is offered as a prompt; use --no-link to skip without being asked.`,
|
|
67
69
|
type: "boolean",
|
|
68
70
|
default: true
|
|
69
71
|
},
|
|
@@ -99,6 +101,7 @@ const handler = async (props) => {
|
|
|
99
101
|
}
|
|
100
102
|
return;
|
|
101
103
|
}
|
|
104
|
+
if (shouldPrintInitBanner(props.default)) printInitBanner();
|
|
102
105
|
const templates = await resolveTemplateList(props);
|
|
103
106
|
const interactive = !props.default && Boolean(process.stdout.isTTY) && !isCi();
|
|
104
107
|
const template = await resolveSelectedTemplate(props, interactive, templates);
|
|
@@ -106,7 +109,7 @@ const handler = async (props) => {
|
|
|
106
109
|
ensureTargetUsable(targetDir, props.force);
|
|
107
110
|
await scaffold(template, targetDir);
|
|
108
111
|
printScaffolded(template, targetDir);
|
|
109
|
-
await runPostScaffoldSteps(props, targetDir, interactive);
|
|
112
|
+
await runPostScaffoldSteps(props, targetDir, interactive, template);
|
|
110
113
|
};
|
|
111
114
|
/**
|
|
112
115
|
* The template list to choose from. When --template is given we try the
|
|
@@ -179,84 +182,150 @@ const scaffold = async (template, targetDir) => {
|
|
|
179
182
|
log.info("Scaffolded %d files into %s.", filesWritten, targetDir);
|
|
180
183
|
return filesWritten;
|
|
181
184
|
};
|
|
182
|
-
const runPostScaffoldSteps = async (props, targetDir, interactive) => {
|
|
185
|
+
const runPostScaffoldSteps = async (props, targetDir, interactive, template) => {
|
|
183
186
|
const inferred = inferPackageManager(targetDir);
|
|
184
187
|
const defaultPm = resolvePackageManager(targetDir);
|
|
188
|
+
const neonConfig = hasNeonConfig(targetDir);
|
|
185
189
|
if (props.default) {
|
|
186
|
-
await runDefaultSteps(props, targetDir, defaultPm);
|
|
190
|
+
await runDefaultSteps(props, targetDir, defaultPm, neonConfig, template);
|
|
187
191
|
return;
|
|
188
192
|
}
|
|
189
193
|
if (!interactive) {
|
|
190
|
-
|
|
194
|
+
printDoneSummary({
|
|
195
|
+
heading: "Project scaffolded.",
|
|
196
|
+
template,
|
|
197
|
+
targetDir,
|
|
198
|
+
pm: defaultPm,
|
|
191
199
|
installed: false,
|
|
192
|
-
|
|
200
|
+
installFailed: false,
|
|
201
|
+
gitFailed: false,
|
|
202
|
+
git: false,
|
|
203
|
+
agentSetup: "skip",
|
|
204
|
+
linked: false,
|
|
205
|
+
skippedLinkForDeps: false,
|
|
206
|
+
suggestLink: true,
|
|
207
|
+
agentsRan: false
|
|
193
208
|
});
|
|
194
209
|
return;
|
|
195
210
|
}
|
|
196
211
|
let pm = defaultPm;
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (props.
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
const kids = bootstrapChildren(props, targetDir);
|
|
206
|
-
await runScaffoldFollowUp({
|
|
207
|
-
cwd: targetDir,
|
|
212
|
+
const wantInstall = props.install && await confirm(installPrompt(inferred));
|
|
213
|
+
if (wantInstall) pm = inferred ?? await selectPackageManager();
|
|
214
|
+
const wantGit = props.git && !isGitRepo(targetDir) && await confirm("Initialize a git repository?");
|
|
215
|
+
const agentSetup = props.agentSetup === false ? "skip" : await (props.pickAgentSetup ?? pickAgentSetupInteractively)();
|
|
216
|
+
const canLink = props.link && !(neonConfig && !wantInstall);
|
|
217
|
+
if (props.link && !canLink) logSkippedLink(pm);
|
|
218
|
+
const wantLink = canLink && await confirm(`Link this project to a Neon project now? (runs ${getCliName()} link)`);
|
|
219
|
+
const outcome = await executePostScaffold(props, targetDir, {
|
|
208
220
|
yes: false,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
221
|
+
lockAgentSetup: true,
|
|
222
|
+
pm,
|
|
223
|
+
git: wantGit,
|
|
224
|
+
agentSetup,
|
|
225
|
+
install: wantInstall,
|
|
226
|
+
link: wantLink,
|
|
227
|
+
hasNeonConfig: neonConfig
|
|
215
228
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
...kids
|
|
224
|
-
});
|
|
225
|
-
printNextSteps(targetDir, pm, {
|
|
226
|
-
installed,
|
|
227
|
-
suggestLink: false
|
|
228
|
-
});
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
printNextSteps(targetDir, pm, {
|
|
232
|
-
installed,
|
|
233
|
-
suggestLink: true
|
|
229
|
+
finishPostScaffold({
|
|
230
|
+
heading: "Project scaffolded.",
|
|
231
|
+
template,
|
|
232
|
+
targetDir,
|
|
233
|
+
pm,
|
|
234
|
+
...outcome,
|
|
235
|
+
suggestLink: !outcome.linked
|
|
234
236
|
});
|
|
235
237
|
};
|
|
236
238
|
const installPrompt = (inferred) => inferred ? `Install dependencies with ${inferred}?` : "Install dependencies?";
|
|
237
239
|
/** `link --yes` still asks for a project unless one is already linked. */
|
|
238
|
-
const runDefaultSteps = async (props, targetDir, pm) => {
|
|
240
|
+
const runDefaultSteps = async (props, targetDir, pm, neonConfig, template) => {
|
|
239
241
|
log.info("Quick start (--default): skipping the template, install, git, and agent pickers. link --yes still asks for a project unless one is already linked.");
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const skipLink = shouldSkipLinkForDeps(targetDir, installed);
|
|
244
|
-
if (props.link && skipLink) logSkippedLink(pm);
|
|
245
|
-
await runScaffoldFollowUp({
|
|
246
|
-
cwd: targetDir,
|
|
242
|
+
const wantGit = props.git && !isGitRepo(targetDir);
|
|
243
|
+
const agentSetup = props.agentSetup === false ? "skip" : "skills-mcp";
|
|
244
|
+
const outcome = await executePostScaffold(props, targetDir, {
|
|
247
245
|
yes: true,
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
246
|
+
lockAgentSetup: false,
|
|
247
|
+
pm,
|
|
248
|
+
git: wantGit,
|
|
249
|
+
agentSetup,
|
|
250
|
+
install: props.install,
|
|
251
|
+
link: props.link,
|
|
252
|
+
hasNeonConfig: neonConfig
|
|
254
253
|
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
254
|
+
finishPostScaffold({
|
|
255
|
+
heading: "Project scaffolded.",
|
|
256
|
+
template,
|
|
257
|
+
targetDir,
|
|
258
|
+
pm,
|
|
259
|
+
...outcome,
|
|
260
|
+
suggestLink: !outcome.linked
|
|
258
261
|
});
|
|
259
262
|
};
|
|
263
|
+
const executePostScaffold = async (props, targetDir, choices) => {
|
|
264
|
+
const kids = bootstrapChildren(props, targetDir);
|
|
265
|
+
let installed = false;
|
|
266
|
+
let installFailed = false;
|
|
267
|
+
let gitFailed = false;
|
|
268
|
+
let git = false;
|
|
269
|
+
let linked = false;
|
|
270
|
+
let skippedLinkForDeps = false;
|
|
271
|
+
let agentSetup = choices.agentSetup;
|
|
272
|
+
let agentsRan = false;
|
|
273
|
+
const actions = postScaffoldActions({
|
|
274
|
+
git: choices.git,
|
|
275
|
+
agentSetup: choices.agentSetup,
|
|
276
|
+
install: choices.install,
|
|
277
|
+
link: choices.link,
|
|
278
|
+
hasNeonConfig: choices.hasNeonConfig
|
|
279
|
+
});
|
|
280
|
+
for (const action of actions) {
|
|
281
|
+
if (action === "git") {
|
|
282
|
+
git = await initGitRepo(targetDir);
|
|
283
|
+
if (!git) {
|
|
284
|
+
gitFailed = true;
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (action === "agent") {
|
|
290
|
+
agentSetup = await runAgentTooling({
|
|
291
|
+
cwd: targetDir,
|
|
292
|
+
yes: choices.yes,
|
|
293
|
+
...choices.lockAgentSetup ? { agentSetup: choices.agentSetup } : {},
|
|
294
|
+
...kids,
|
|
295
|
+
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
296
|
+
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
297
|
+
});
|
|
298
|
+
agentsRan = true;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (action === "install") {
|
|
302
|
+
installed = await runCommand(choices.pm, installArgs(choices.pm), targetDir);
|
|
303
|
+
if (!installed) installFailed = true;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (action !== "link") throw new Error(`Unhandled post-scaffold action: ${action}`);
|
|
307
|
+
if (choices.hasNeonConfig && !installed) {
|
|
308
|
+
skippedLinkForDeps = true;
|
|
309
|
+
logSkippedLink(choices.pm);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
await runInitSteps([choices.yes ? ["link", "--yes"] : ["link"]], {
|
|
313
|
+
cwd: targetDir,
|
|
314
|
+
...kids
|
|
315
|
+
});
|
|
316
|
+
linked = true;
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
installed,
|
|
320
|
+
installFailed,
|
|
321
|
+
gitFailed,
|
|
322
|
+
git,
|
|
323
|
+
linked,
|
|
324
|
+
skippedLinkForDeps,
|
|
325
|
+
agentSetup,
|
|
326
|
+
agentsRan
|
|
327
|
+
};
|
|
328
|
+
};
|
|
260
329
|
const isGitRepo = (dir) => existsSync(join(dir, ".git"));
|
|
261
330
|
const NEON_CONFIG_FILENAMES = [
|
|
262
331
|
"neon.ts",
|
|
@@ -265,7 +334,6 @@ const NEON_CONFIG_FILENAMES = [
|
|
|
265
334
|
"neon.mjs"
|
|
266
335
|
];
|
|
267
336
|
const hasNeonConfig = (dir) => NEON_CONFIG_FILENAMES.some((name) => existsSync(join(dir, name)));
|
|
268
|
-
const shouldSkipLinkForDeps = (dir, installed) => !installed && hasNeonConfig(dir);
|
|
269
337
|
const logSkippedLink = (pm) => {
|
|
270
338
|
log.info(`Skipping the Neon link step: \`${getCliName()} link\` reads this project's neon.ts to pull env vars, which needs its dependencies. Run \`${formatInstallCommand(pm)}\`, then \`${getCliName()} link\`.`);
|
|
271
339
|
};
|
|
@@ -288,9 +356,7 @@ const bootstrapChildren = (props, targetDir) => {
|
|
|
288
356
|
* deliberately don't auto-commit, both to avoid failing on a machine with no
|
|
289
357
|
* git identity configured and to leave the first commit to the user.
|
|
290
358
|
*/
|
|
291
|
-
const initGitRepo = async (dir) =>
|
|
292
|
-
await runCommand("git", ["init"], dir);
|
|
293
|
-
};
|
|
359
|
+
const initGitRepo = async (dir) => runCommand("git", ["init"], dir);
|
|
294
360
|
const confirm = async (message) => {
|
|
295
361
|
const { value } = await prompts({
|
|
296
362
|
onState: onPromptState,
|
|
@@ -324,22 +390,56 @@ const selectPackageManager = async () => {
|
|
|
324
390
|
return pm ?? "npm";
|
|
325
391
|
};
|
|
326
392
|
const printScaffolded = (template, targetDir) => {
|
|
327
|
-
log.info("");
|
|
328
|
-
log.info("Done. Scaffolded \"%s\" into %s.", template.title, isCurrentDir(targetDir) ? "the current directory" : displayDir(targetDir));
|
|
393
|
+
log.info("Scaffolded \"%s\" into %s.", template.title, isCurrentDir(targetDir) ? "the current directory" : displayDir(targetDir));
|
|
329
394
|
};
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (!
|
|
339
|
-
if (
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
|
|
395
|
+
const printDoneSummary = (input) => {
|
|
396
|
+
const unfinished = input.installFailed || input.gitFailed;
|
|
397
|
+
const heading = unfinished ? "Setup did not finish." : input.heading;
|
|
398
|
+
const deps = input.installFailed ? "install failed" : input.installed ? `installed with ${input.pm}` : "skipped";
|
|
399
|
+
const git = input.gitFailed ? "init failed" : input.git ? "initialized" : "skipped";
|
|
400
|
+
const project = input.linked ? "linked" : input.skippedLinkForDeps ? "skipped (needs dependencies)" : "not linked";
|
|
401
|
+
const next = [];
|
|
402
|
+
if (!isCurrentDir(input.targetDir)) next.push(`cd ${displayDir(input.targetDir)}`);
|
|
403
|
+
if (!input.installed) next.push(formatInstallCommand(input.pm));
|
|
404
|
+
if (input.suggestLink) next.push(`${getCliName()} link`);
|
|
405
|
+
if (!unfinished) next.push("See the README to run it.");
|
|
406
|
+
printInitDone(formatInitDone({
|
|
407
|
+
heading,
|
|
408
|
+
rows: [
|
|
409
|
+
{
|
|
410
|
+
label: "Template",
|
|
411
|
+
value: input.template.title
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
label: "Directory",
|
|
415
|
+
value: displayDir(input.targetDir)
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
label: "Dependencies",
|
|
419
|
+
value: deps
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
label: "Git",
|
|
423
|
+
value: git
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
label: "Agents",
|
|
427
|
+
value: agentSetupDoneLabel({
|
|
428
|
+
setup: input.agentSetup,
|
|
429
|
+
ran: input.agentsRan
|
|
430
|
+
})
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
label: "Project",
|
|
434
|
+
value: project
|
|
435
|
+
}
|
|
436
|
+
],
|
|
437
|
+
next
|
|
438
|
+
}));
|
|
439
|
+
};
|
|
440
|
+
const finishPostScaffold = (input) => {
|
|
441
|
+
printDoneSummary(input);
|
|
442
|
+
if (input.installFailed || input.gitFailed) throw new Error("Setup did not finish.");
|
|
343
443
|
};
|
|
344
444
|
const isCurrentDir = (targetDir) => relative(process.cwd(), targetDir) === "";
|
|
345
445
|
/**
|
package/dist/commands/init.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { log } from "../log.js";
|
|
3
2
|
import { readContextFile } from "../context.js";
|
|
4
3
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
4
|
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
6
5
|
import { initChildEnv, spawnCliChild } from "../init/child.js";
|
|
6
|
+
import { agentSetupLabel, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner } from "../init/chrome.js";
|
|
7
7
|
import { bootstrapInitStep, directoryIsEmpty, planExistingInit } from "../init/plan.js";
|
|
8
8
|
import { runAgentTooling, runInitSteps } from "../init/tooling.js";
|
|
9
9
|
import { helpEpilogue } from "../utils/help_text.js";
|
|
@@ -67,10 +67,11 @@ const handler = async (props) => {
|
|
|
67
67
|
forward,
|
|
68
68
|
authEnv
|
|
69
69
|
});
|
|
70
|
-
log.info("Done.");
|
|
71
70
|
return;
|
|
72
71
|
}
|
|
73
|
-
|
|
72
|
+
if (shouldPrintInitBanner(yes)) printInitBanner();
|
|
73
|
+
const alreadyLinked = isLinked(contextFile);
|
|
74
|
+
const agentSetup = await runAgentTooling({
|
|
74
75
|
cwd,
|
|
75
76
|
yes,
|
|
76
77
|
run,
|
|
@@ -80,7 +81,7 @@ const handler = async (props) => {
|
|
|
80
81
|
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
81
82
|
});
|
|
82
83
|
await runInitSteps(planExistingInit({
|
|
83
|
-
linked:
|
|
84
|
+
linked: alreadyLinked,
|
|
84
85
|
yes,
|
|
85
86
|
agentSetup: "skip"
|
|
86
87
|
}), {
|
|
@@ -89,7 +90,24 @@ const handler = async (props) => {
|
|
|
89
90
|
forward,
|
|
90
91
|
authEnv
|
|
91
92
|
});
|
|
92
|
-
|
|
93
|
+
printInitDone(formatInitDone({
|
|
94
|
+
heading: "Configured this directory for Neon.",
|
|
95
|
+
rows: [
|
|
96
|
+
{
|
|
97
|
+
label: "Agents",
|
|
98
|
+
value: agentSetupLabel(agentSetup)
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
label: "Project",
|
|
102
|
+
value: alreadyLinked ? "already linked" : "linked"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
label: "Config",
|
|
106
|
+
value: "neon.ts"
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
next: []
|
|
110
|
+
}));
|
|
93
111
|
};
|
|
94
112
|
//#endregion
|
|
95
113
|
export { builder, command, describe, handler, initChildEnv, init_exports as t };
|
package/dist/errors.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
const ERROR_MATCHERS = [
|
|
3
3
|
[/^Unknown command: (.*)$/, "UNKNOWN_COMMAND"],
|
|
4
4
|
[/^Missing required argument: (.*)$/, "MISSING_ARGUMENT"],
|
|
5
|
-
[/^Failed to open web browser. (.*)$/, "AUTH_BROWSER_FAILED"]
|
|
5
|
+
[/^Failed to open web browser. (.*)$/, "AUTH_BROWSER_FAILED"],
|
|
6
|
+
[/^Setup did not finish\.$/, "NEON_INIT_FAILED"]
|
|
6
7
|
];
|
|
7
8
|
const matchErrorCode = (message) => {
|
|
8
9
|
if (!message) return "UNKNOWN_ERROR";
|
package/dist/index.js
CHANGED
|
@@ -162,8 +162,9 @@ async function handleError(msg, err) {
|
|
|
162
162
|
}
|
|
163
163
|
} else {
|
|
164
164
|
const error = err instanceof Error ? err : new Error(msg || "Unknown error");
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
const code = matchErrorCode(error.message);
|
|
166
|
+
sendError(error, code);
|
|
167
|
+
if (code !== "NEON_INIT_FAILED") log.error(error.message);
|
|
167
168
|
return false;
|
|
168
169
|
}
|
|
169
170
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isCi } from "../env.js";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
//#region src/init/chrome.ts
|
|
4
|
+
const NEON_GREEN = "#4BB578";
|
|
5
|
+
const INIT_BANNER_LINES = [
|
|
6
|
+
" ██╗ ██╗██████╗ ██████╗ ██╗ ██╗",
|
|
7
|
+
" ███╗ ██║██╔═══╝ ██╔═══██╗███╗ ██║",
|
|
8
|
+
" ████╗██║██████╗ ██║ ██║████╗██║",
|
|
9
|
+
" ██╔████║██╔═══╝ ██║ ██║██╔████║",
|
|
10
|
+
" ██║╚███║██████╗ ╚██████╔╝██║╚███║",
|
|
11
|
+
" ╚═╝ ╚══╝╚═════╝ ╚═════╝ ╚═╝ ╚══╝"
|
|
12
|
+
];
|
|
13
|
+
const formatInitBanner = () => INIT_BANNER_LINES.join("\n");
|
|
14
|
+
const shouldPrintInitBanner = (yes) => !yes && !isCi() && Boolean(process.stdout.isTTY);
|
|
15
|
+
const printInitBanner = () => {
|
|
16
|
+
process.stdout.write(`\n${chalk.hex(NEON_GREEN)(formatInitBanner())}\n\n${chalk.dim("Let's get this directory set up with Neon.")}\n\n`);
|
|
17
|
+
};
|
|
18
|
+
const formatInitDone = (input) => {
|
|
19
|
+
const labelWidth = Math.max(0, ...input.rows.map((row) => row.label.length));
|
|
20
|
+
const body = input.rows.length === 0 ? "" : `\n\n${input.rows.map((row) => ` ${row.label.padEnd(labelWidth)} ${row.value}`).join("\n")}`;
|
|
21
|
+
const next = input.next.length === 0 ? "" : `\n\nNext:\n${input.next.map((line) => ` ${line}`).join("\n")}`;
|
|
22
|
+
const rule = "-".repeat(input.heading.length);
|
|
23
|
+
return `${input.heading}\n${rule}${body}${next}\n`;
|
|
24
|
+
};
|
|
25
|
+
const printInitDone = (text) => {
|
|
26
|
+
const painted = (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n").map((line, index) => {
|
|
27
|
+
if (index === 0) return chalk.hex(NEON_GREEN).bold(line);
|
|
28
|
+
if (/^-{3,}$/.test(line)) return chalk.hex(NEON_GREEN)(line);
|
|
29
|
+
if (line === "Next:") return chalk.bold(line);
|
|
30
|
+
return line;
|
|
31
|
+
});
|
|
32
|
+
process.stdout.write(`\n${painted.join("\n")}\n\n`);
|
|
33
|
+
};
|
|
34
|
+
const agentSetupLabel = (setup) => {
|
|
35
|
+
if (setup === "plugin") return "plugin";
|
|
36
|
+
if (setup === "skills-mcp") return "skills and MCP";
|
|
37
|
+
return "skipped";
|
|
38
|
+
};
|
|
39
|
+
const agentSetupDoneLabel = (input) => {
|
|
40
|
+
if (!input.ran && input.setup !== "skip") return "not run";
|
|
41
|
+
return agentSetupLabel(input.setup);
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { INIT_BANNER_LINES, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, formatInitBanner, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner };
|
package/dist/init/plan.js
CHANGED
|
@@ -12,6 +12,21 @@ const projectContextFile = (projectDir, contextFile) => {
|
|
|
12
12
|
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return join(projectDir, ".neon");
|
|
13
13
|
return resolved;
|
|
14
14
|
};
|
|
15
|
+
/** Linking a template with neon.ts requires installed dependencies because env pull evaluates it. */
|
|
16
|
+
const postScaffoldActions = (input) => {
|
|
17
|
+
const actions = [];
|
|
18
|
+
if (input.git) actions.push("git");
|
|
19
|
+
if (input.agentSetup !== "skip") actions.push("agent");
|
|
20
|
+
const canLink = input.link && !(input.hasNeonConfig && !input.install);
|
|
21
|
+
if (canLink && input.hasNeonConfig && input.install) {
|
|
22
|
+
actions.push("install");
|
|
23
|
+
actions.push("link");
|
|
24
|
+
return actions;
|
|
25
|
+
}
|
|
26
|
+
if (canLink) actions.push("link");
|
|
27
|
+
if (input.install) actions.push("install");
|
|
28
|
+
return actions;
|
|
29
|
+
};
|
|
15
30
|
const planAgentSteps = (input) => {
|
|
16
31
|
const y = input.yes ? ["-y"] : [];
|
|
17
32
|
if (input.agentSetup === "plugin") return [["plugins", ...y]];
|
|
@@ -44,4 +59,4 @@ const childArgv = (step, forward) => {
|
|
|
44
59
|
return args;
|
|
45
60
|
};
|
|
46
61
|
//#endregion
|
|
47
|
-
export { bootstrapInitStep, childArgv, directoryIsEmpty, planAgentSteps, planExistingInit, projectContextFile, resolveInitAgentSetup };
|
|
62
|
+
export { bootstrapInitStep, childArgv, directoryIsEmpty, planAgentSteps, planExistingInit, postScaffoldActions, projectContextFile, resolveInitAgentSetup };
|
package/dist/init/tooling.js
CHANGED
|
@@ -24,7 +24,7 @@ const runAgentTooling = async (options) => {
|
|
|
24
24
|
const yes = options.yes;
|
|
25
25
|
const hasProjectPlugins = yes ? await (options.hasProjectPlugins ?? defaultHasProjectPlugins)(options.cwd) : false;
|
|
26
26
|
const interactive = !yes && (options.pickAgentSetup !== void 0 || canPickAgentsInteractively());
|
|
27
|
-
const agentSetup = await resolveInitAgentSetup({
|
|
27
|
+
const agentSetup = options.agentSetup ?? await resolveInitAgentSetup({
|
|
28
28
|
yes,
|
|
29
29
|
interactive,
|
|
30
30
|
hasProjectPlugins,
|
|
@@ -34,6 +34,7 @@ const runAgentTooling = async (options) => {
|
|
|
34
34
|
yes,
|
|
35
35
|
agentSetup
|
|
36
36
|
}), options);
|
|
37
|
+
return agentSetup;
|
|
37
38
|
};
|
|
38
39
|
const runScaffoldFollowUp = async (options) => {
|
|
39
40
|
if (!options.skipAgentSetup) await runAgentTooling(options);
|
package/dist/skills/catalog.js
CHANGED
|
@@ -3,11 +3,6 @@ import { helpCsv } from "../utils/help_text.js";
|
|
|
3
3
|
const AGENT_SKILLS_SOURCE = "neondatabase/agent-skills";
|
|
4
4
|
const PLATFORMS_SKILLS_SOURCE = "neondatabase/neon-for-agent-platforms";
|
|
5
5
|
const NEON_SKILL_CATALOG = [
|
|
6
|
-
{
|
|
7
|
-
skill: "claimable-postgres",
|
|
8
|
-
source: AGENT_SKILLS_SOURCE,
|
|
9
|
-
defaultSelected: true
|
|
10
|
-
},
|
|
11
6
|
{
|
|
12
7
|
skill: "neon",
|
|
13
8
|
source: AGENT_SKILLS_SOURCE,
|
|
@@ -112,7 +112,7 @@ const INSPECT_QUERIES = {
|
|
|
112
112
|
`
|
|
113
113
|
},
|
|
114
114
|
"stalled-queries": {
|
|
115
|
-
describe: "Active queries running longer than 30 seconds with parallel workers, waits, and blockers (compute-wide)",
|
|
115
|
+
describe: "Active queries running longer than 30 seconds with parallel workers, waits, and blockers, oldest group first (compute-wide)",
|
|
116
116
|
scope: "compute",
|
|
117
117
|
fields: [
|
|
118
118
|
"duration",
|
|
@@ -132,9 +132,12 @@ const INSPECT_QUERIES = {
|
|
|
132
132
|
AND pid <> pg_backend_pid()
|
|
133
133
|
),
|
|
134
134
|
stalled_groups AS (
|
|
135
|
-
SELECT
|
|
135
|
+
SELECT
|
|
136
|
+
COALESCE(leader_pid, pid) AS query_group,
|
|
137
|
+
min(query_start) AS group_start
|
|
136
138
|
FROM activity
|
|
137
139
|
WHERE query_start <= statement_timestamp() - interval '30 seconds'
|
|
140
|
+
GROUP BY COALESCE(leader_pid, pid)
|
|
138
141
|
)
|
|
139
142
|
SELECT
|
|
140
143
|
statement_timestamp() AS observed_at,
|
|
@@ -159,7 +162,7 @@ const INSPECT_QUERIES = {
|
|
|
159
162
|
FROM activity a
|
|
160
163
|
JOIN stalled_groups g
|
|
161
164
|
ON g.query_group = COALESCE(a.leader_pid, a.pid)
|
|
162
|
-
ORDER BY g.query_group, a.leader_pid NULLS FIRST, a.pid;
|
|
165
|
+
ORDER BY g.group_start, g.query_group, a.leader_pid NULLS FIRST, a.pid;
|
|
163
166
|
`
|
|
164
167
|
},
|
|
165
168
|
locks: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.2",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"yargs": "17.7.2",
|
|
65
65
|
"yoctocolors": "^2.1.2",
|
|
66
66
|
"@neon/config": "1.0.5",
|
|
67
|
-
"@neon/
|
|
68
|
-
"@neon/
|
|
67
|
+
"@neon/config-runtime": "1.0.5",
|
|
68
|
+
"@neon/sdk": "3.0.0"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
71
|
"@napi-rs/keyring": "1.3.0",
|