neon 8.11.0 → 8.11.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 +1 -1
- package/dist/_chunks/api-GbEaBZJ5.js +388 -0
- package/dist/_chunks/api_keys-CNnnugbQ.js +281 -0
- package/dist/_chunks/ask-DVwy0eUL.js +155 -0
- package/dist/_chunks/auth-CIUtXByB.js +495 -0
- package/dist/_chunks/bootstrap-wPZWa1sT.js +499 -0
- package/dist/_chunks/branches-BNa1xX1L.js +460 -0
- package/dist/_chunks/bucket-BCdryTkD.js +409 -0
- package/dist/_chunks/checkout-CXIOydqZ.js +348 -0
- package/dist/_chunks/claim-DqAvAvDm.js +473 -0
- package/dist/_chunks/config-BH_qRHiG.js +1113 -0
- package/dist/_chunks/connection_string-DB4awPzZ.js +154 -0
- package/dist/_chunks/credentials-BSjE9fRR.js +146 -0
- package/dist/_chunks/data_api-Djxj5ZQD.js +263 -0
- package/dist/_chunks/databases-Dok4wru7.js +84 -0
- package/dist/_chunks/deploy-D7B0Xfjg.js +33 -0
- package/dist/_chunks/dev-CSvxAecE.js +945 -0
- package/dist/_chunks/diff-DhxxbdQE.js +178 -0
- package/dist/_chunks/docs-CrVybkTP.js +197 -0
- package/dist/_chunks/env-5HS5qQGd.js +284 -0
- package/dist/_chunks/feedback-CMayyTa8.js +88 -0
- package/dist/_chunks/functions-Q0zuvtnk.js +464 -0
- package/dist/_chunks/git-H32TNt6M.js +356 -0
- package/dist/_chunks/init-BkA6RilG.js +111 -0
- package/dist/_chunks/inspect-g0wekylj.js +90 -0
- package/dist/_chunks/ip_allow-5lLFvIuE.js +106 -0
- package/dist/_chunks/link-BgtkOBws.js +936 -0
- package/dist/_chunks/logs-DWkpjzIH.js +291 -0
- package/dist/_chunks/mcp-Bo-7TKc7.js +253 -0
- package/dist/_chunks/neon_auth-CBs1_3qu.js +822 -0
- package/dist/_chunks/open-CX3BT3Tp.js +33 -0
- package/dist/_chunks/operations-D4I-AxVs.js +37 -0
- package/dist/_chunks/orgs-xGfANwHQ.js +52 -0
- package/dist/_chunks/plugins-DqD7BDO1.js +169 -0
- package/dist/_chunks/profile-yXOiKk9R.js +672 -0
- package/dist/_chunks/projects-BwI2Uo0z.js +439 -0
- package/dist/_chunks/psql-BM-doe8e.js +64 -0
- package/dist/_chunks/realtime-BRqIyGR-.js +79 -0
- package/dist/_chunks/roles-B4w3YT_7.js +79 -0
- package/dist/_chunks/set_context-DbrwoImy.js +38 -0
- package/dist/_chunks/skills-DyiBXKS3.js +255 -0
- package/dist/_chunks/snapshots-RJBzW8GM.js +338 -0
- package/dist/_chunks/status-DV8Z2kw3.js +44 -0
- package/dist/_chunks/triggers-BrRo1z4b.js +302 -0
- package/dist/_chunks/user-dlOGXbX9.js +60 -0
- package/dist/_chunks/vpc_endpoints-CNb0YfMo.js +105 -0
- package/dist/auth_recovery.js +1 -1
- package/dist/commands/api.js +1 -388
- package/dist/commands/api_keys.js +1 -281
- package/dist/commands/ask.js +1 -155
- package/dist/commands/auth.js +1 -495
- package/dist/commands/bootstrap.js +1 -499
- package/dist/commands/branches.js +1 -460
- package/dist/commands/bucket.js +1 -409
- package/dist/commands/checkout.js +1 -348
- package/dist/commands/claim.js +1 -473
- package/dist/commands/config.js +1 -1113
- package/dist/commands/connection_string.js +1 -154
- package/dist/commands/credentials.js +1 -146
- package/dist/commands/data_api.js +1 -263
- package/dist/commands/databases.js +1 -84
- package/dist/commands/deploy.js +1 -33
- package/dist/commands/dev.js +1 -945
- package/dist/commands/diff.js +1 -178
- package/dist/commands/docs.js +1 -197
- package/dist/commands/env.js +1 -284
- package/dist/commands/feedback.js +1 -88
- package/dist/commands/functions.js +1 -464
- package/dist/commands/functions_new.js +6 -6
- package/dist/commands/git.js +1 -356
- package/dist/commands/index.js +45 -45
- package/dist/commands/init.js +1 -111
- package/dist/commands/inspect.js +1 -90
- package/dist/commands/ip_allow.js +1 -106
- package/dist/commands/link.js +1 -936
- package/dist/commands/logs.js +1 -291
- package/dist/commands/mcp.js +1 -253
- package/dist/commands/neon_auth.js +1 -822
- package/dist/commands/open.js +1 -33
- package/dist/commands/operations.js +1 -37
- package/dist/commands/orgs.js +1 -52
- package/dist/commands/plugins.js +1 -169
- package/dist/commands/profile.js +1 -672
- package/dist/commands/projects.js +1 -439
- package/dist/commands/psql.js +1 -64
- package/dist/commands/realtime.js +1 -79
- package/dist/commands/roles.js +1 -79
- package/dist/commands/set_context.js +1 -38
- package/dist/commands/skills.js +1 -255
- package/dist/commands/snapshots.js +1 -338
- package/dist/commands/status.js +1 -44
- package/dist/commands/subscribe.js +1 -1
- package/dist/commands/triggers.js +1 -302
- package/dist/commands/user.js +1 -60
- package/dist/commands/vpc_endpoints.js +1 -105
- package/dist/functions/registry.js +29 -151
- package/dist/functions/source.js +42 -2
- package/dist/index.js +1 -1
- package/dist/init/auth.js +3 -3
- package/dist/init/link.js +2 -2
- package/dist/init/run.js +3 -3
- package/dist/init/tooling.js +3 -3
- package/dist/mcp/mint.js +1 -1
- package/dist/test_utils/registry.js +1 -2
- package/dist/utils/package_manager.js +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
+
import { isCi } from "../env.js";
|
|
3
|
+
import { log } from "../log.js";
|
|
4
|
+
import { recordCommandSuccessExtras, recordScaffoldedTemplate } from "../analytics.js";
|
|
5
|
+
import { getCliName } from "../utils/cli_name.js";
|
|
6
|
+
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
7
|
+
import { coerceAgentFlag } from "../utils/agent_flag.js";
|
|
8
|
+
import { assertNamedAgentTooling, chooseYesAgentTooling, initPluginAgents, initSkillsMcpAgents, postScaffoldActions, projectContextFile, resolveNamedAgents } from "../init/plan.js";
|
|
9
|
+
import { formatInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolvePackageManager, runCommand } from "../utils/package_manager.js";
|
|
10
|
+
import { writer } from "../writer.js";
|
|
11
|
+
import { FALLBACK_TEMPLATES, ensureTargetUsable, fetchTemplates, findTemplate, scaffoldTemplate, templateIds } from "../init/bootstrap.js";
|
|
12
|
+
import { agentSetupDoneLabel, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner } from "../init/chrome.js";
|
|
13
|
+
import { runAuthenticatedLink } from "../init/link.js";
|
|
14
|
+
import { formatTemplateTitle } from "../init/template_title.js";
|
|
15
|
+
import { pickAgentSetupInteractively, pickInitLinkInteractively } from "../init/wizard.js";
|
|
16
|
+
import { runAgentTooling } from "../init/tooling.js";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import { join, relative, resolve } from "node:path";
|
|
19
|
+
import prompts from "prompts";
|
|
20
|
+
//#region src/commands/bootstrap.ts
|
|
21
|
+
var bootstrap_exports = /* @__PURE__ */ __exportAll({
|
|
22
|
+
builder: () => builder,
|
|
23
|
+
command: () => command,
|
|
24
|
+
describe: () => describe,
|
|
25
|
+
handler: () => handler
|
|
26
|
+
});
|
|
27
|
+
const command = "bootstrap [directory]";
|
|
28
|
+
const describe = "Scaffold a new project from a Neon starter template, then install agent tooling and link a Neon project";
|
|
29
|
+
const builder = (argv) => argv.usage("$0 bootstrap [directory] [options]").positional("directory", {
|
|
30
|
+
describe: "Directory to scaffold into. Use \".\" for the current directory. Omit to be prompted.",
|
|
31
|
+
type: "string"
|
|
32
|
+
}).options({
|
|
33
|
+
template: {
|
|
34
|
+
describe: "Template to use (skips the interactive picker). Run with --list-templates to see available templates.",
|
|
35
|
+
type: "string"
|
|
36
|
+
},
|
|
37
|
+
"list-templates": {
|
|
38
|
+
alias: ["list", "ls"],
|
|
39
|
+
describe: "List available templates and exit. --output json and --output yaml print a machine-readable catalog.",
|
|
40
|
+
type: "boolean",
|
|
41
|
+
default: false
|
|
42
|
+
},
|
|
43
|
+
force: {
|
|
44
|
+
describe: "Scaffold into the target directory even if it is not empty (colliding files are overwritten).",
|
|
45
|
+
type: "boolean",
|
|
46
|
+
default: false
|
|
47
|
+
},
|
|
48
|
+
default: {
|
|
49
|
+
alias: "y",
|
|
50
|
+
describe: "Quick start: scaffold the default template (or --template), then install, git, agent tooling (project folders, else the host CLI agent; if none, pass --agent or omit --default in a terminal), and link with defaults. If several organizations or projects exist, link prints IDs and exits",
|
|
51
|
+
type: "boolean",
|
|
52
|
+
default: false
|
|
53
|
+
},
|
|
54
|
+
install: {
|
|
55
|
+
describe: "Install dependencies after scaffolding. In interactive mode this is offered as a prompt; use --no-install to skip without being asked.",
|
|
56
|
+
type: "boolean",
|
|
57
|
+
default: true
|
|
58
|
+
},
|
|
59
|
+
git: {
|
|
60
|
+
describe: "Initialize a git repository after scaffolding. In interactive mode this is offered as a prompt; use --no-git to skip without being asked.",
|
|
61
|
+
type: "boolean",
|
|
62
|
+
default: true
|
|
63
|
+
},
|
|
64
|
+
link: {
|
|
65
|
+
describe: "Link a Neon project after scaffolding. Templates with neon.ts link after install so env pull works; otherwise linking happens before install. In interactive mode this is offered as a prompt; use --no-link to skip without being asked.",
|
|
66
|
+
type: "boolean",
|
|
67
|
+
default: true
|
|
68
|
+
},
|
|
69
|
+
"agent-setup": {
|
|
70
|
+
type: "boolean",
|
|
71
|
+
default: true,
|
|
72
|
+
describe: "After scaffolding, install the Neon plugin or skills and MCP. Use --no-agent-setup to skip"
|
|
73
|
+
},
|
|
74
|
+
agent: {
|
|
75
|
+
alias: "a",
|
|
76
|
+
type: "array",
|
|
77
|
+
string: true,
|
|
78
|
+
describe: "Coding agent to install into (repeatable). Forwarded to plugins, or to skills and mcp. Skips agent selection. Values listed below",
|
|
79
|
+
coerce: coerceAgentFlag
|
|
80
|
+
}
|
|
81
|
+
}).example("$0 bootstrap my-app", "Create ./my-app from an interactively chosen template").example("$0 bootstrap . --template hono", "Scaffold the Hono template into the current directory").example("$0 bootstrap my-app --default", "Skip the pickers; if several orgs or projects exist, link prints IDs and exits").example("$0 bootstrap --list-templates --output json", "Print the template catalog as JSON").example("$0 bootstrap my-app --agent cursor --agent claude-code", "Skip agent selection; install the plugin for those agents").epilogue(helpEpilogue("--agent / -a is forwarded to plugins, or to skills and mcp, not both. It skips agent selection, including with --default.", helpCsv("Plugin agents", initPluginAgents()), helpCsv("Skills and MCP agents", initSkillsMcpAgents()))).strict();
|
|
82
|
+
const handler = async (props) => {
|
|
83
|
+
if (props.listTemplates) {
|
|
84
|
+
const templates = await fetchTemplates();
|
|
85
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
86
|
+
writer(props).end(templates.map((t) => ({
|
|
87
|
+
id: t.id,
|
|
88
|
+
title: t.title,
|
|
89
|
+
description: t.description,
|
|
90
|
+
services: t.services ?? []
|
|
91
|
+
})), { fields: [
|
|
92
|
+
"id",
|
|
93
|
+
"title",
|
|
94
|
+
"description",
|
|
95
|
+
"services"
|
|
96
|
+
] });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
for (const t of templates) {
|
|
100
|
+
const services = t.services && t.services.length > 0 ? ` [${t.services.join(" · ")}]` : "";
|
|
101
|
+
process.stdout.write(`${t.id} — ${t.description}${services}\n`);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (props.printBanner !== false && shouldPrintInitBanner(props.default)) printInitBanner();
|
|
106
|
+
const named = resolveNamedAgents(props.agent ?? []);
|
|
107
|
+
if (props.agentSetup !== false) assertNamedAgentTooling(named, "bootstrap", {
|
|
108
|
+
...props.directory !== void 0 && props.directory.length > 0 ? { directory: props.directory } : {},
|
|
109
|
+
...props.default ? { yes: true } : {}
|
|
110
|
+
});
|
|
111
|
+
const interactive = !props.default && Boolean(process.stdout.isTTY) && !isCi();
|
|
112
|
+
const template = props.selectedTemplate ?? await resolveSelectedTemplate(props, interactive, await resolveTemplateList(props));
|
|
113
|
+
const targetDir = await resolveTargetDir(props, interactive, template);
|
|
114
|
+
ensureTargetUsable(targetDir, props.force);
|
|
115
|
+
await scaffold(template, targetDir);
|
|
116
|
+
recordScaffoldedTemplate(template.id);
|
|
117
|
+
printScaffolded(template, targetDir);
|
|
118
|
+
const result = await runPostScaffoldSteps(props, targetDir, interactive, template, named);
|
|
119
|
+
recordCommandSuccessExtras({ agent_setup: result.agentSetup });
|
|
120
|
+
return result;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* The template list to choose from. When --template is given we try the
|
|
124
|
+
* built-in fallback list first to avoid a network round-trip, only fetching the
|
|
125
|
+
* remote manifest if the id isn't one of the defaults.
|
|
126
|
+
*/
|
|
127
|
+
const resolveTemplateList = async (props) => props.template && findTemplate(FALLBACK_TEMPLATES, props.template) ? FALLBACK_TEMPLATES : fetchTemplates();
|
|
128
|
+
const resolveSelectedTemplate = async (props, interactive, templates) => {
|
|
129
|
+
if (props.template) {
|
|
130
|
+
const template = findTemplate(templates, props.template);
|
|
131
|
+
if (!template) throw new Error(`Unknown template "${props.template}". Available templates: ${templateIds(templates)}.`);
|
|
132
|
+
return template;
|
|
133
|
+
}
|
|
134
|
+
if (props.default) {
|
|
135
|
+
const fallback = templates[0];
|
|
136
|
+
if (!fallback) throw new Error("No templates available to scaffold from.");
|
|
137
|
+
return fallback;
|
|
138
|
+
}
|
|
139
|
+
if (!interactive) throw new Error(`No template selected. Re-run in an interactive terminal to pick one, or pass --template <id>. Available templates: ${templateIds(templates)}.`);
|
|
140
|
+
const { id } = await prompts({
|
|
141
|
+
onState: onPromptState,
|
|
142
|
+
type: "select",
|
|
143
|
+
name: "id",
|
|
144
|
+
message: "Which template would you like to use?",
|
|
145
|
+
choices: templates.map((template) => ({
|
|
146
|
+
title: formatTemplateTitle(template),
|
|
147
|
+
value: template.id
|
|
148
|
+
})),
|
|
149
|
+
initial: 0
|
|
150
|
+
});
|
|
151
|
+
const template = findTemplate(templates, id);
|
|
152
|
+
if (!template) throw new Error("No template selected.");
|
|
153
|
+
return template;
|
|
154
|
+
};
|
|
155
|
+
const resolveTargetDir = async (props, interactive, template) => {
|
|
156
|
+
let dir = props.directory;
|
|
157
|
+
if (dir === void 0) {
|
|
158
|
+
if (props.default) return resolve(process.cwd(), defaultDirName(template));
|
|
159
|
+
if (!interactive) throw new Error(`No target directory given. Pass one, e.g. \`${getCliName()} bootstrap my-app\` (or "." for the current directory).`);
|
|
160
|
+
const { value } = await prompts({
|
|
161
|
+
onState: onPromptState,
|
|
162
|
+
type: "text",
|
|
163
|
+
name: "value",
|
|
164
|
+
message: "Where should we scaffold your project?",
|
|
165
|
+
initial: defaultDirName(template),
|
|
166
|
+
validate: (input) => input && input.trim().length > 0 ? true : "Enter a directory (use \".\" for the current directory)."
|
|
167
|
+
});
|
|
168
|
+
dir = String(value).trim();
|
|
169
|
+
}
|
|
170
|
+
return resolve(process.cwd(), dir === "." ? "" : dir);
|
|
171
|
+
};
|
|
172
|
+
const defaultDirName = (template) => template.source.subdir.split("/").pop() || template.id;
|
|
173
|
+
const scaffold = async (template, targetDir) => {
|
|
174
|
+
log.info("Fetching template \"%s\" from GitHub…", template.id);
|
|
175
|
+
const filesWritten = await scaffoldTemplate(template, targetDir, { onWarn: (message) => {
|
|
176
|
+
log.warning(message);
|
|
177
|
+
} });
|
|
178
|
+
log.info("Scaffolded %d files into %s.", filesWritten, targetDir);
|
|
179
|
+
return filesWritten;
|
|
180
|
+
};
|
|
181
|
+
const nestedResult = (template, targetDir, hasNeonConfig, outcome) => ({
|
|
182
|
+
templateTitle: template.title,
|
|
183
|
+
targetDir,
|
|
184
|
+
hasNeonConfig,
|
|
185
|
+
...outcome
|
|
186
|
+
});
|
|
187
|
+
const runPostScaffoldSteps = async (props, targetDir, interactive, template, named) => {
|
|
188
|
+
const inferred = inferPackageManager(targetDir);
|
|
189
|
+
const defaultPm = resolvePackageManager(targetDir);
|
|
190
|
+
const neonConfig = hasNeonConfig(targetDir);
|
|
191
|
+
if (props.default) return runDefaultSteps(props, targetDir, defaultPm, neonConfig, template, named);
|
|
192
|
+
if (!interactive) {
|
|
193
|
+
const outcome = {
|
|
194
|
+
installed: false,
|
|
195
|
+
installFailed: false,
|
|
196
|
+
gitFailed: false,
|
|
197
|
+
git: false,
|
|
198
|
+
agentSetup: "skip",
|
|
199
|
+
linked: false,
|
|
200
|
+
skippedLinkForDeps: false,
|
|
201
|
+
agentsRan: false
|
|
202
|
+
};
|
|
203
|
+
finishPostScaffold({
|
|
204
|
+
heading: "Project scaffolded.",
|
|
205
|
+
template,
|
|
206
|
+
targetDir,
|
|
207
|
+
pm: defaultPm,
|
|
208
|
+
...outcome,
|
|
209
|
+
suggestLink: true,
|
|
210
|
+
skipPrint: props.skipDoneSummary === true
|
|
211
|
+
});
|
|
212
|
+
return nestedResult(template, targetDir, neonConfig, outcome);
|
|
213
|
+
}
|
|
214
|
+
let pm = defaultPm;
|
|
215
|
+
const wantInstall = props.install && await confirm(installPrompt(inferred));
|
|
216
|
+
if (wantInstall) pm = inferred ?? await selectPackageManager();
|
|
217
|
+
const wantGit = props.git && !isGitRepo(targetDir) && await confirm("Initialize a git repository?");
|
|
218
|
+
const agentSetup = props.agentSetup === false ? "skip" : named.length > 0 ? chooseYesAgentTooling(named).setup : await (props.pickAgentSetup ?? pickAgentSetupInteractively)();
|
|
219
|
+
const canLink = props.link && !(neonConfig && !wantInstall);
|
|
220
|
+
if (props.link && !canLink) logSkippedLink(pm);
|
|
221
|
+
const outcome = await executePostScaffold(props, targetDir, {
|
|
222
|
+
yes: false,
|
|
223
|
+
lockAgentSetup: named.length === 0,
|
|
224
|
+
pm,
|
|
225
|
+
git: wantGit,
|
|
226
|
+
agentSetup,
|
|
227
|
+
install: wantInstall,
|
|
228
|
+
link: canLink,
|
|
229
|
+
hasNeonConfig: neonConfig,
|
|
230
|
+
named
|
|
231
|
+
});
|
|
232
|
+
finishPostScaffold({
|
|
233
|
+
heading: "Project scaffolded.",
|
|
234
|
+
template,
|
|
235
|
+
targetDir,
|
|
236
|
+
pm,
|
|
237
|
+
...outcome,
|
|
238
|
+
suggestLink: !outcome.linked,
|
|
239
|
+
skipPrint: props.skipDoneSummary === true
|
|
240
|
+
});
|
|
241
|
+
return nestedResult(template, targetDir, neonConfig, outcome);
|
|
242
|
+
};
|
|
243
|
+
const installPrompt = (inferred) => inferred ? `Install dependencies with ${inferred}?` : "Install dependencies?";
|
|
244
|
+
const runDefaultSteps = async (props, targetDir, pm, neonConfig, template, named) => {
|
|
245
|
+
log.info("Quick start (--default): skipping the template, install, git, and agent pickers. If several organizations or projects exist, link prints IDs and exits.");
|
|
246
|
+
const wantGit = props.git && !isGitRepo(targetDir);
|
|
247
|
+
const agentSetup = props.agentSetup === false ? "skip" : named.length > 0 ? chooseYesAgentTooling(named).setup : "skills-mcp";
|
|
248
|
+
const outcome = await executePostScaffold(props, targetDir, {
|
|
249
|
+
yes: true,
|
|
250
|
+
lockAgentSetup: false,
|
|
251
|
+
pm,
|
|
252
|
+
git: wantGit,
|
|
253
|
+
agentSetup,
|
|
254
|
+
install: props.install,
|
|
255
|
+
link: props.link,
|
|
256
|
+
hasNeonConfig: neonConfig,
|
|
257
|
+
named
|
|
258
|
+
});
|
|
259
|
+
finishPostScaffold({
|
|
260
|
+
heading: "Project scaffolded.",
|
|
261
|
+
template,
|
|
262
|
+
targetDir,
|
|
263
|
+
pm,
|
|
264
|
+
...outcome,
|
|
265
|
+
suggestLink: !outcome.linked,
|
|
266
|
+
skipPrint: props.skipDoneSummary === true
|
|
267
|
+
});
|
|
268
|
+
return nestedResult(template, targetDir, neonConfig, outcome);
|
|
269
|
+
};
|
|
270
|
+
const executePostScaffold = async (props, targetDir, choices) => {
|
|
271
|
+
const kids = {
|
|
272
|
+
output: "table",
|
|
273
|
+
auth: bootstrapAuth(props, targetDir),
|
|
274
|
+
operations: props.operations,
|
|
275
|
+
...props.narrate ? { narrate: props.narrate } : {}
|
|
276
|
+
};
|
|
277
|
+
let installed = false;
|
|
278
|
+
let installFailed = false;
|
|
279
|
+
let gitFailed = false;
|
|
280
|
+
let git = false;
|
|
281
|
+
let linked = false;
|
|
282
|
+
let skippedLinkForDeps = false;
|
|
283
|
+
let agentSetup = choices.agentSetup;
|
|
284
|
+
let agentsRan = false;
|
|
285
|
+
const actions = postScaffoldActions({
|
|
286
|
+
git: choices.git,
|
|
287
|
+
agentSetup: choices.agentSetup,
|
|
288
|
+
install: choices.install,
|
|
289
|
+
link: choices.link,
|
|
290
|
+
hasNeonConfig: choices.hasNeonConfig
|
|
291
|
+
});
|
|
292
|
+
for (const action of actions) {
|
|
293
|
+
if (action === "git") {
|
|
294
|
+
git = await initGitRepo(targetDir);
|
|
295
|
+
if (!git) {
|
|
296
|
+
gitFailed = true;
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (action === "agent") {
|
|
302
|
+
agentSetup = await runAgentTooling({
|
|
303
|
+
cwd: targetDir,
|
|
304
|
+
yes: choices.yes,
|
|
305
|
+
...choices.lockAgentSetup ? { agentSetup: choices.agentSetup } : {},
|
|
306
|
+
...kids,
|
|
307
|
+
...choices.named.length > 0 ? { agents: choices.named } : {},
|
|
308
|
+
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
309
|
+
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {},
|
|
310
|
+
...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
|
|
311
|
+
...props.detectAgent ? { detectAgent: props.detectAgent } : {},
|
|
312
|
+
command: "bootstrap"
|
|
313
|
+
});
|
|
314
|
+
agentsRan = true;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (action === "install") {
|
|
318
|
+
log.info("Installing dependencies with %s…", choices.pm);
|
|
319
|
+
installed = await runCommand(choices.pm, installArgs(choices.pm), targetDir);
|
|
320
|
+
if (!installed) installFailed = true;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (action !== "link") throw new Error(`Unhandled post-scaffold action: ${action}`);
|
|
324
|
+
if (choices.hasNeonConfig && !installed) {
|
|
325
|
+
skippedLinkForDeps = true;
|
|
326
|
+
logSkippedLink(choices.pm);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (!(choices.yes || await (props.pickLink ?? pickInitLinkInteractively)())) continue;
|
|
330
|
+
await (props.linkProject ?? runAuthenticatedLink)({
|
|
331
|
+
apiClient: props.apiClient,
|
|
332
|
+
apiKey: props.apiKey,
|
|
333
|
+
apiHost: props.apiHost,
|
|
334
|
+
output: props.output,
|
|
335
|
+
contextFile: projectContextFile(targetDir, props.contextFile),
|
|
336
|
+
yes: choices.yes,
|
|
337
|
+
clear: false,
|
|
338
|
+
checks: true,
|
|
339
|
+
envPull: true,
|
|
340
|
+
config: props.linkNoConfig !== true,
|
|
341
|
+
cwd: targetDir,
|
|
342
|
+
...props.linkInputs,
|
|
343
|
+
...props.configDir ? { configDir: props.configDir } : {},
|
|
344
|
+
...props.profile ? { profile: props.profile } : {},
|
|
345
|
+
...props.oauthHost ? { oauthHost: props.oauthHost } : {},
|
|
346
|
+
...props.clientId ? { clientId: props.clientId } : {},
|
|
347
|
+
...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
|
|
348
|
+
...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
|
|
349
|
+
});
|
|
350
|
+
linked = true;
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
installed,
|
|
354
|
+
installFailed,
|
|
355
|
+
gitFailed,
|
|
356
|
+
git,
|
|
357
|
+
linked,
|
|
358
|
+
skippedLinkForDeps,
|
|
359
|
+
agentSetup,
|
|
360
|
+
agentsRan
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
const isGitRepo = (dir) => existsSync(join(dir, ".git"));
|
|
364
|
+
const NEON_CONFIG_FILENAMES = [
|
|
365
|
+
"neon.ts",
|
|
366
|
+
"neon.mts",
|
|
367
|
+
"neon.js",
|
|
368
|
+
"neon.mjs"
|
|
369
|
+
];
|
|
370
|
+
const hasNeonConfig = (dir) => NEON_CONFIG_FILENAMES.some((name) => existsSync(join(dir, name)));
|
|
371
|
+
const logSkippedLink = (pm) => {
|
|
372
|
+
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\`.`);
|
|
373
|
+
};
|
|
374
|
+
/**
|
|
375
|
+
* plugins/skills need no Neon auth at all; mcp resolves it itself, in-process, exactly as
|
|
376
|
+
* the standalone `neon mcp` does (see `init/auth.ts`) — this is that shared context.
|
|
377
|
+
*/
|
|
378
|
+
const bootstrapAuth = (props, targetDir) => ({
|
|
379
|
+
apiClient: props.apiClient,
|
|
380
|
+
apiKey: props.apiKey,
|
|
381
|
+
apiHost: props.apiHost,
|
|
382
|
+
contextFile: projectContextFile(targetDir, props.contextFile),
|
|
383
|
+
...props.configDir ? { configDir: props.configDir } : {},
|
|
384
|
+
...props.profile ? { profile: props.profile } : {},
|
|
385
|
+
...props.oauthHost ? { oauthHost: props.oauthHost } : {},
|
|
386
|
+
...props.clientId ? { clientId: props.clientId } : {},
|
|
387
|
+
...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
|
|
388
|
+
...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
|
|
389
|
+
});
|
|
390
|
+
/**
|
|
391
|
+
* Initialize a git repository in the scaffolded directory. Just `git init` — we
|
|
392
|
+
* deliberately don't auto-commit, both to avoid failing on a machine with no
|
|
393
|
+
* git identity configured and to leave the first commit to the user.
|
|
394
|
+
*/
|
|
395
|
+
const initGitRepo = async (dir) => runCommand("git", ["init"], dir);
|
|
396
|
+
const confirm = async (message) => {
|
|
397
|
+
const { value } = await prompts({
|
|
398
|
+
onState: onPromptState,
|
|
399
|
+
type: "confirm",
|
|
400
|
+
name: "value",
|
|
401
|
+
message,
|
|
402
|
+
initial: true
|
|
403
|
+
});
|
|
404
|
+
return value === true;
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Ask which package manager to install with when we couldn't infer one from the
|
|
408
|
+
* invocation. Offers the managers actually installed (npm preselected); with
|
|
409
|
+
* one or none installed there's nothing to choose, so it returns that one (or
|
|
410
|
+
* npm) without prompting. A cancelled prompt falls back to npm.
|
|
411
|
+
*/
|
|
412
|
+
const selectPackageManager = async () => {
|
|
413
|
+
const installed = installedPackageManagers();
|
|
414
|
+
if (installed.length <= 1) return installed[0] ?? "npm";
|
|
415
|
+
const { pm } = await prompts({
|
|
416
|
+
onState: onPromptState,
|
|
417
|
+
type: "select",
|
|
418
|
+
name: "pm",
|
|
419
|
+
message: "Which package manager should we use?",
|
|
420
|
+
choices: installed.map((manager) => ({
|
|
421
|
+
title: manager,
|
|
422
|
+
value: manager
|
|
423
|
+
})),
|
|
424
|
+
initial: Math.max(0, installed.indexOf("npm"))
|
|
425
|
+
});
|
|
426
|
+
return pm ?? "npm";
|
|
427
|
+
};
|
|
428
|
+
const printScaffolded = (template, targetDir) => {
|
|
429
|
+
log.info("Scaffolded \"%s\" into %s.", template.title, isCurrentDir(targetDir) ? "the current directory" : displayDir(targetDir));
|
|
430
|
+
};
|
|
431
|
+
const printDoneSummary = (input) => {
|
|
432
|
+
const unfinished = input.installFailed || input.gitFailed;
|
|
433
|
+
const heading = unfinished ? "Setup did not finish." : input.heading;
|
|
434
|
+
const deps = input.installFailed ? "install failed" : input.installed ? `installed with ${input.pm}` : "skipped";
|
|
435
|
+
const git = input.gitFailed ? "init failed" : input.git ? "initialized" : "skipped";
|
|
436
|
+
const project = input.linked ? "linked" : input.skippedLinkForDeps ? "skipped (needs dependencies)" : "not linked";
|
|
437
|
+
const next = [];
|
|
438
|
+
if (!isCurrentDir(input.targetDir)) next.push(`cd ${displayDir(input.targetDir)}`);
|
|
439
|
+
if (!input.installed) next.push(formatInstallCommand(input.pm));
|
|
440
|
+
if (input.suggestLink) next.push(`${getCliName()} link`);
|
|
441
|
+
if (!unfinished) next.push("See the README to run it.");
|
|
442
|
+
printInitDone(formatInitDone({
|
|
443
|
+
heading,
|
|
444
|
+
rows: [
|
|
445
|
+
{
|
|
446
|
+
label: "Template",
|
|
447
|
+
value: input.template.title
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
label: "Directory",
|
|
451
|
+
value: displayDir(input.targetDir)
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
label: "Dependencies",
|
|
455
|
+
value: deps
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
label: "Git",
|
|
459
|
+
value: git
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
label: "Agents",
|
|
463
|
+
value: agentSetupDoneLabel({
|
|
464
|
+
setup: input.agentSetup,
|
|
465
|
+
ran: input.agentsRan
|
|
466
|
+
})
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
label: "Project",
|
|
470
|
+
value: project
|
|
471
|
+
}
|
|
472
|
+
],
|
|
473
|
+
next
|
|
474
|
+
}));
|
|
475
|
+
};
|
|
476
|
+
const finishPostScaffold = (input) => {
|
|
477
|
+
if (!input.skipPrint) printDoneSummary(input);
|
|
478
|
+
if (input.installFailed || input.gitFailed) throw new Error("Setup did not finish.");
|
|
479
|
+
};
|
|
480
|
+
const isCurrentDir = (targetDir) => relative(process.cwd(), targetDir) === "";
|
|
481
|
+
/**
|
|
482
|
+
* The path to show the user: the bare relative path for the common
|
|
483
|
+
* `bootstrap my-app` case, the absolute path when the target sits outside the
|
|
484
|
+
* cwd (a deep `../../..` is noise), and "." for the current directory.
|
|
485
|
+
*/
|
|
486
|
+
const displayDir = (targetDir) => {
|
|
487
|
+
const rel = relative(process.cwd(), targetDir);
|
|
488
|
+
if (rel === "") return ".";
|
|
489
|
+
return rel.startsWith("..") ? targetDir : rel;
|
|
490
|
+
};
|
|
491
|
+
const onPromptState = (state) => {
|
|
492
|
+
if (state.aborted) {
|
|
493
|
+
process.stdout.write("\x1B[?25h");
|
|
494
|
+
process.stdout.write("\n");
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
//#endregion
|
|
499
|
+
export { handler as a, describe as i, builder as n, command as r, bootstrap_exports as t };
|