neon 5.0.1 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -27
- package/dist/commands/bootstrap.js +19 -17
- package/dist/commands/config.js +38 -17
- package/dist/commands/dev.js +1 -1
- package/dist/commands/index.js +3 -3
- package/dist/commands/init.js +45 -302
- package/dist/commands/mcp.js +68 -41
- package/dist/commands/plugins.js +56 -35
- package/dist/commands/skills.js +56 -35
- package/dist/commands/snapshots.js +23 -7
- package/dist/index.js +8 -0
- package/dist/init/auth.js +84 -0
- package/dist/init/cancelled.js +41 -0
- package/dist/init/choices.js +10 -21
- package/dist/init/chrome.js +59 -24
- package/dist/init/copy.js +133 -0
- package/dist/init/detect.js +58 -0
- package/dist/init/funnel.js +115 -0
- package/dist/init/mode.js +30 -0
- package/dist/init/plan.js +135 -32
- package/dist/init/run.js +645 -0
- package/dist/init/tooling.js +104 -22
- package/dist/init/wizard.js +207 -48
- package/dist/mcp/plan.js +1 -1
- package/dist/mcp/wizard.js +1 -27
- package/dist/plugins/plan.js +1 -1
- package/dist/skills/plan.js +1 -1
- package/dist/skills/wizard.js +1 -25
- package/dist/test_utils/npm_env.js +26 -0
- package/dist/update_check_worker.js +47 -0
- package/dist/update_notifier.js +145 -0
- package/dist/utils/package_manager.js +8 -1
- package/dist/utils/service_picker.js +34 -8
- package/package.json +7 -5
- package/dist/init/child.js +0 -37
- package/dist/plugins/wizard.js +0 -36
package/dist/commands/init.js
CHANGED
|
@@ -1,62 +1,57 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { log } from "../log.js";
|
|
3
|
-
import { contextBranch, readContextFile } from "../context.js";
|
|
4
|
-
import { recordCommandSuccessExtras } from "../analytics.js";
|
|
5
|
-
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
6
2
|
import { getCliName } from "../utils/cli_name.js";
|
|
7
3
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
8
|
-
import {
|
|
9
|
-
import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES
|
|
10
|
-
import { fetchTemplates } from "../init/bootstrap.js";
|
|
11
|
-
import { initChildEnv, spawnCliChild } from "../init/child.js";
|
|
12
|
-
import { agentSetupLabel, configSummaryLabel, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner } from "../init/chrome.js";
|
|
13
|
-
import { hasNeonConfigFile } from "./config.js";
|
|
14
|
-
import { runAuthenticatedLink } from "../init/link.js";
|
|
4
|
+
import { servicesOption } from "../neon_services.js";
|
|
5
|
+
import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES } from "../config_template.js";
|
|
15
6
|
import { coerceAgentFlag } from "../utils/agent_flag.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { runAgentTooling, runInitSteps } from "../init/tooling.js";
|
|
20
|
-
import { handler as handler$1 } from "./bootstrap.js";
|
|
21
|
-
import { INIT_CONFIG_SERVICES_CONFLICT, INIT_TEMPLATE_KEEPS_CONFIG, configPlanFromResolution, resolveInitConfigChoice, resolveInitTemplateChoice, shouldRefreshEnvAfterNewConfig } from "../init/choices.js";
|
|
22
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
23
|
-
import { resolve } from "node:path";
|
|
7
|
+
import { initPluginAgents, initSkillsMcpAgents } from "../init/plan.js";
|
|
8
|
+
import { listSkillIds } from "../skills/catalog.js";
|
|
9
|
+
import { runInit } from "../init/run.js";
|
|
24
10
|
//#region src/commands/init.ts
|
|
25
11
|
var init_exports = /* @__PURE__ */ __exportAll({
|
|
26
12
|
builder: () => builder,
|
|
27
13
|
command: () => command,
|
|
28
14
|
describe: () => describe,
|
|
29
|
-
handler: () => handler
|
|
30
|
-
initChildEnv: () => initChildEnv
|
|
15
|
+
handler: () => handler
|
|
31
16
|
});
|
|
32
17
|
const command = "init";
|
|
33
|
-
const describe = "Set up this directory for Neon
|
|
18
|
+
const describe = "Set up coding agents and this directory for Neon. -y is Recommended. -y with Custom flags applies those answers without prompts.";
|
|
34
19
|
const removedProtocol = () => `\`${getCliName()} init --data\` was removed. Run \`${getCliName()} init\` or \`${getCliName()} init -y\`.`;
|
|
35
20
|
const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file", { hidden: true }).option("yes", {
|
|
36
21
|
alias: "y",
|
|
37
22
|
type: "boolean",
|
|
38
23
|
default: false,
|
|
39
|
-
describe: "
|
|
40
|
-
}).option("
|
|
24
|
+
describe: "Skip prompts. Alone: Recommended (detected agents, link when authenticated, default neon.ts). With --skill, MCP flags, --no-agent-setup, or --claimable: Custom using those flags"
|
|
25
|
+
}).option("agent-setup", {
|
|
26
|
+
type: "boolean",
|
|
27
|
+
default: true,
|
|
28
|
+
describe: "Install Neon into coding agents. Use --no-agent-setup to skip. Plugin vs skills is inferred from --skill and MCP flags"
|
|
29
|
+
}).option("claimable", {
|
|
41
30
|
type: "boolean",
|
|
42
31
|
default: false,
|
|
43
|
-
describe: "
|
|
44
|
-
}).option("
|
|
32
|
+
describe: "Create a claimable project that expires in 72 hours unless claimed. Selects Custom"
|
|
33
|
+
}).option("package-manager", {
|
|
45
34
|
type: "string",
|
|
46
|
-
|
|
35
|
+
choices: [
|
|
36
|
+
"npm",
|
|
37
|
+
"pnpm",
|
|
38
|
+
"yarn",
|
|
39
|
+
"bun"
|
|
40
|
+
],
|
|
41
|
+
describe: "Package manager for neon.ts dependencies. -y uses this without asking. Custom asks only when none is detected"
|
|
47
42
|
}).option("link", {
|
|
48
43
|
type: "boolean",
|
|
49
44
|
default: true,
|
|
50
45
|
describe: "Link a Neon project during setup. Use --no-link to skip without being asked"
|
|
51
46
|
}).option("config", {
|
|
52
47
|
type: "boolean",
|
|
53
|
-
describe: "
|
|
48
|
+
describe: "Create neon.ts after linking. Use --no-config to skip. Omitted in Custom: you will be asked"
|
|
54
49
|
}).option("services", servicesOption({
|
|
55
50
|
key: "services",
|
|
56
51
|
allowed: CONFIG_INIT_SERVICES,
|
|
57
|
-
describe: "
|
|
52
|
+
describe: "Services to declare in neon.ts",
|
|
58
53
|
noneMeans: CONFIG_INIT_NONE_MEANS,
|
|
59
|
-
also: "Implies creating neon.ts. Cannot be combined with --no-config.
|
|
54
|
+
also: "Implies creating neon.ts. Cannot be combined with --no-config."
|
|
60
55
|
})).option("org-id", {
|
|
61
56
|
describe: "Forwarded to link: organization ID to link to",
|
|
62
57
|
type: "string"
|
|
@@ -77,8 +72,24 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
|
|
|
77
72
|
alias: "a",
|
|
78
73
|
type: "array",
|
|
79
74
|
string: true,
|
|
80
|
-
describe: "Coding agent to install into (repeatable).
|
|
75
|
+
describe: "Coding agent to install into (repeatable). Skips agent selection. Values listed below",
|
|
81
76
|
coerce: coerceAgentFlag
|
|
77
|
+
}).option("skill", {
|
|
78
|
+
type: "array",
|
|
79
|
+
string: true,
|
|
80
|
+
describe: "Neon skill to install (repeatable). Selects skills setup (not the plugin) and skips the skills picker. With MCP flags, also configures MCP. Values listed below"
|
|
81
|
+
}).option("mcp-config-location", {
|
|
82
|
+
type: "string",
|
|
83
|
+
choices: ["global", "project"],
|
|
84
|
+
describe: "Where to configure the Neon MCP server: global (user config, same as neon mcp) or project (this directory, same as neon mcp --project). Selects skills and MCP setup"
|
|
85
|
+
}).option("mcp-auth", {
|
|
86
|
+
type: "string",
|
|
87
|
+
choices: ["oauth", "api-key"],
|
|
88
|
+
describe: "MCP authentication. Selects skills and MCP setup. oauth is neon mcp --oauth"
|
|
89
|
+
}).option("mcp-project-scoped", {
|
|
90
|
+
type: "boolean",
|
|
91
|
+
default: false,
|
|
92
|
+
describe: "Limit MCP tools to the linked project, same as neon mcp --project-id. Selects skills and MCP setup"
|
|
82
93
|
}).option("data", {
|
|
83
94
|
hidden: true,
|
|
84
95
|
type: "string"
|
|
@@ -86,281 +97,13 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
|
|
|
86
97
|
alias: "o",
|
|
87
98
|
hidden: true,
|
|
88
99
|
describe: "Not supported; the commands init runs print their own output"
|
|
89
|
-
}).example("$0 init", "
|
|
100
|
+
}).example("$0 init", "Recommended or Custom setup").example("$0 init -y", "Recommended setup without prompts").example("$0 init -y --skill neon --mcp-auth oauth --mcp-config-location project", "Custom: those skills and MCP, Recommended defaults for the rest").example("$0 init --no-agent-setup --claimable", "Skip agent setup and create a claimable project").example("$0 init --agent cursor --agent claude-code", "Skip agent selection; install tooling for those agents").epilogue(helpEpilogue("Recommended setup installs tooling for detected coding agents, links a Neon project when the CLI is authenticated or the session is interactive, and writes a default neon.ts.", "init sets up the current directory in place. It does not scaffold starter files.", "-y alone is Recommended. -y with --skill, MCP flags, --no-agent-setup, or --claimable is Custom: those flags, Recommended defaults for unanswered questions. -y never opens a browser.", "--skill selects skills (not the plugin). MCP flags select skills and MCP. --no-agent-setup skips agent setup. --agent without those flags installs the plugin (and skills/MCP for agents the plugin cannot cover).", "Without a TTY, pass -y or enough flags to answer every question.", "--agent / -a is forwarded to plugins, or to skills and mcp. It skips agent selection, including with -y.", helpCsv("Plugin agents", initPluginAgents()), helpCsv("Skills and MCP agents", initSkillsMcpAgents()), helpCsv("Skills", listSkillIds()))).check((argv) => {
|
|
90
101
|
if (argv.data !== void 0) throw new Error(removedProtocol());
|
|
91
102
|
if (argv.help !== true && (argv.output === "json" || argv.output === "yaml")) throw new Error(`\`${getCliName()} init\` does not support --output. The commands it runs print their own output.`);
|
|
92
103
|
return true;
|
|
93
104
|
}).strict();
|
|
94
|
-
const isLinked = (contextFile) => {
|
|
95
|
-
const projectId = readContextFile(contextFile).projectId;
|
|
96
|
-
return typeof projectId === "string" && projectId.length > 0;
|
|
97
|
-
};
|
|
98
|
-
const nestedBootstrapDirectory = (cwd) => resolve(cwd) === resolve(process.cwd()) ? "." : cwd;
|
|
99
|
-
const parsedInitServices = (raw) => {
|
|
100
|
-
const values = servicesFlagValue(raw);
|
|
101
|
-
if (values === void 0) return;
|
|
102
|
-
return parseServices(values, {
|
|
103
|
-
allowed: CONFIG_INIT_SERVICES,
|
|
104
|
-
whyUnavailable: CONFIG_INIT_UNAVAILABLE,
|
|
105
|
-
flag: "--services",
|
|
106
|
-
noneMeans: CONFIG_INIT_NONE_MEANS,
|
|
107
|
-
onDeprecated: (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical))
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
const nestedBootstrapProps = (props, cwd, contextFile, template, linkInputs) => ({
|
|
111
|
-
apiClient: props.apiClient,
|
|
112
|
-
apiKey: props.apiKey,
|
|
113
|
-
apiHost: props.apiHost,
|
|
114
|
-
output: props.output,
|
|
115
|
-
contextFile,
|
|
116
|
-
directory: nestedBootstrapDirectory(cwd),
|
|
117
|
-
force: false,
|
|
118
|
-
listTemplates: false,
|
|
119
|
-
default: template.useDefault,
|
|
120
|
-
install: true,
|
|
121
|
-
git: true,
|
|
122
|
-
link: props.link !== false,
|
|
123
|
-
printBanner: false,
|
|
124
|
-
skipDoneSummary: true,
|
|
125
|
-
linkNoConfig: true,
|
|
126
|
-
narrate: "human",
|
|
127
|
-
...template.selected ? { selectedTemplate: template.selected } : {},
|
|
128
|
-
...!template.selected && template.id !== void 0 ? { template: template.id } : {},
|
|
129
|
-
linkInputs,
|
|
130
|
-
...props.agent !== void 0 ? { agent: props.agent } : {},
|
|
131
|
-
...props.configDir ? { configDir: props.configDir } : {},
|
|
132
|
-
...props.profile ? { profile: props.profile } : {},
|
|
133
|
-
...props.oauthHost ? { oauthHost: props.oauthHost } : {},
|
|
134
|
-
...props.clientId ? { clientId: props.clientId } : {},
|
|
135
|
-
...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
|
|
136
|
-
...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {},
|
|
137
|
-
...props.analytics === false ? { analytics: false } : {},
|
|
138
|
-
...props.run ? { run: props.run } : {},
|
|
139
|
-
...props.linkProject ? { linkProject: props.linkProject } : {},
|
|
140
|
-
...props.pickLink ? { pickLink: props.pickLink } : {},
|
|
141
|
-
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
142
|
-
...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
|
|
143
|
-
...props.detectAgent ? { detectAgent: props.detectAgent } : {},
|
|
144
|
-
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
145
|
-
});
|
|
146
|
-
const projectRow = (input) => {
|
|
147
|
-
if (input.alreadyLinked) return "already linked";
|
|
148
|
-
if (input.linkedNow) return "linked";
|
|
149
|
-
return "not linked";
|
|
150
|
-
};
|
|
151
|
-
const configSummary = (input) => {
|
|
152
|
-
if (input.plan.kind === "skip") return "skipped";
|
|
153
|
-
if (input.existingConfig) return "existing";
|
|
154
|
-
return "created";
|
|
155
|
-
};
|
|
156
|
-
const printNestedBootstrapDone = (result, cwd, fallbackTitle) => {
|
|
157
|
-
const hasConfig = result?.hasNeonConfig === true || hasNeonConfigFile(cwd);
|
|
158
|
-
printInitDone(formatInitDone({
|
|
159
|
-
heading: "Neon setup complete.",
|
|
160
|
-
rows: [
|
|
161
|
-
{
|
|
162
|
-
label: "Template",
|
|
163
|
-
value: result?.templateTitle ?? fallbackTitle
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
label: "Agents",
|
|
167
|
-
value: agentSetupLabel(result?.agentSetup ?? "skip")
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
label: "Project",
|
|
171
|
-
value: projectRow({
|
|
172
|
-
alreadyLinked: false,
|
|
173
|
-
linkedNow: result?.linked === true
|
|
174
|
-
})
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
label: "Config",
|
|
178
|
-
value: configSummaryLabel(hasConfig ? "template" : "skipped")
|
|
179
|
-
}
|
|
180
|
-
],
|
|
181
|
-
next: []
|
|
182
|
-
}));
|
|
183
|
-
};
|
|
184
|
-
const noteTemplateKeepsShippedConfig = (config, services) => {
|
|
185
|
-
if (config === void 0 && services === void 0) return;
|
|
186
|
-
log.warning(INIT_TEMPLATE_KEEPS_CONFIG);
|
|
187
|
-
};
|
|
188
|
-
const recordInitSuccess = (kind, agentSetup) => {
|
|
189
|
-
recordCommandSuccessExtras({
|
|
190
|
-
init_kind: kind,
|
|
191
|
-
...agentSetup !== void 0 ? { agent_setup: agentSetup } : {}
|
|
192
|
-
});
|
|
193
|
-
};
|
|
194
105
|
const handler = async (props) => {
|
|
195
|
-
|
|
196
|
-
const cwd = props.cwd ?? process.cwd();
|
|
197
|
-
const names = existsSync(cwd) ? readdirSync(cwd) : [];
|
|
198
|
-
const contextFile = resolve(cwd, props.contextFile);
|
|
199
|
-
const yes = props.yes === true;
|
|
200
|
-
const named = resolveNamedAgents(props.agent ?? []);
|
|
201
|
-
assertNamedAgentTooling(named, "init", yes ? { yes: true } : void 0);
|
|
202
|
-
const run = props.run ?? spawnCliChild;
|
|
203
|
-
const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
|
|
204
|
-
const authEnv = explicitKey ? { NEON_API_KEY: explicitKey } : void 0;
|
|
205
|
-
const forward = {
|
|
206
|
-
...props.configDir ? { configDir: props.configDir } : {},
|
|
207
|
-
...props.profile ? { profile: props.profile } : {},
|
|
208
|
-
apiHost: props.apiHost,
|
|
209
|
-
contextFile,
|
|
210
|
-
...props.analytics === false ? { analytics: false } : {}
|
|
211
|
-
};
|
|
212
|
-
const services = parsedInitServices(props.services);
|
|
213
|
-
if (props.config === false && services !== void 0) throw new Error(INIT_CONFIG_SERVICES_CONFLICT);
|
|
214
|
-
const linkInputs = {
|
|
215
|
-
...props.orgId ? { orgId: props.orgId } : {},
|
|
216
|
-
...props.projectId ? { projectId: props.projectId } : {},
|
|
217
|
-
...props.projectName ? { projectName: props.projectName } : {},
|
|
218
|
-
...props.regionId ? { regionId: props.regionId } : {},
|
|
219
|
-
...props.branch ? { branch: props.branch } : {}
|
|
220
|
-
};
|
|
221
|
-
const hasExplicitLinkInputs = props.orgId !== void 0 || props.projectId !== void 0 || props.projectName !== void 0 || props.regionId !== void 0 || props.branch !== void 0;
|
|
222
|
-
const templateChoice = resolveInitTemplateChoice({
|
|
223
|
-
empty: directoryIsEmpty(names),
|
|
224
|
-
yes,
|
|
225
|
-
skipTemplate: props.skipTemplate === true,
|
|
226
|
-
template: props.template
|
|
227
|
-
});
|
|
228
|
-
if (shouldPrintInitBanner(yes)) printInitBanner();
|
|
229
|
-
if (templateChoice.kind === "default" || templateChoice.kind === "template") {
|
|
230
|
-
if (templateChoice.kind === "template" && !yes && props.runBootstrap === void 0 && !canPickAgentsInteractively()) throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
|
|
231
|
-
const runBootstrap = props.runBootstrap ?? handler$1;
|
|
232
|
-
noteTemplateKeepsShippedConfig(props.config, services);
|
|
233
|
-
const result = await runBootstrap(nestedBootstrapProps(props, cwd, contextFile, {
|
|
234
|
-
useDefault: yes,
|
|
235
|
-
...templateChoice.kind === "template" ? { id: templateChoice.id } : {}
|
|
236
|
-
}, linkInputs));
|
|
237
|
-
printNestedBootstrapDone(result, cwd, templateChoice.kind === "template" ? templateChoice.id : "default");
|
|
238
|
-
recordInitSuccess("empty-template", result?.agentSetup);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
if (templateChoice.kind === "ask") {
|
|
242
|
-
if (props.pickTemplate === void 0 && !canPickAgentsInteractively()) throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
|
|
243
|
-
const templates = await (props.fetchTemplates ?? fetchTemplates)();
|
|
244
|
-
const picked = await (props.pickTemplate ?? pickInitTemplateInteractively)(templates);
|
|
245
|
-
if (picked.kind === "template") {
|
|
246
|
-
const runBootstrap = props.runBootstrap ?? handler$1;
|
|
247
|
-
noteTemplateKeepsShippedConfig(props.config, services);
|
|
248
|
-
const result = await runBootstrap(nestedBootstrapProps(props, cwd, contextFile, {
|
|
249
|
-
selected: picked.template,
|
|
250
|
-
useDefault: false
|
|
251
|
-
}, linkInputs));
|
|
252
|
-
printNestedBootstrapDone(result, cwd, picked.template.id);
|
|
253
|
-
recordInitSuccess("empty-template", result?.agentSetup);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const alreadyLinked = isLinked(contextFile);
|
|
258
|
-
const shouldLink = props.link !== false && (!alreadyLinked || hasExplicitLinkInputs);
|
|
259
|
-
const existingConfig = hasNeonConfigFile(cwd);
|
|
260
|
-
const canAskConfig = props.pickConfig !== void 0 || canPickAgentsInteractively();
|
|
261
|
-
const agentSetup = await runAgentTooling({
|
|
262
|
-
cwd,
|
|
263
|
-
yes,
|
|
264
|
-
run,
|
|
265
|
-
forward,
|
|
266
|
-
authEnv,
|
|
267
|
-
narrate: "human",
|
|
268
|
-
command: "init",
|
|
269
|
-
...named.length > 0 ? { agents: named } : {},
|
|
270
|
-
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
271
|
-
...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
|
|
272
|
-
...props.detectAgent ? { detectAgent: props.detectAgent } : {},
|
|
273
|
-
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
274
|
-
});
|
|
275
|
-
const canAskLink = props.pickLink !== void 0 || canPickAgentsInteractively();
|
|
276
|
-
const acceptedLink = shouldLink && (yes || !canAskLink || await (props.pickLink ?? pickInitLinkInteractively)());
|
|
277
|
-
if (acceptedLink) await (props.linkProject ?? runAuthenticatedLink)({
|
|
278
|
-
apiClient: props.apiClient,
|
|
279
|
-
apiKey: props.apiKey,
|
|
280
|
-
apiHost: props.apiHost,
|
|
281
|
-
output: props.output,
|
|
282
|
-
contextFile,
|
|
283
|
-
yes,
|
|
284
|
-
clear: false,
|
|
285
|
-
checks: true,
|
|
286
|
-
envPull: true,
|
|
287
|
-
config: false,
|
|
288
|
-
cwd,
|
|
289
|
-
...linkInputs,
|
|
290
|
-
...props.configDir ? { configDir: props.configDir } : {},
|
|
291
|
-
...props.profile ? { profile: props.profile } : {},
|
|
292
|
-
...props.oauthHost ? { oauthHost: props.oauthHost } : {},
|
|
293
|
-
...props.clientId ? { clientId: props.clientId } : {},
|
|
294
|
-
...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
|
|
295
|
-
...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
|
|
296
|
-
});
|
|
297
|
-
const configResolution = resolveInitConfigChoice({
|
|
298
|
-
flag: props.config,
|
|
299
|
-
yes,
|
|
300
|
-
canAsk: canAskConfig,
|
|
301
|
-
existingConfig,
|
|
302
|
-
...services !== void 0 ? { services } : {}
|
|
303
|
-
});
|
|
304
|
-
const accepted = configResolution.kind === "ask" ? await (props.pickConfig ?? pickInitConfigInteractively)() : void 0;
|
|
305
|
-
const configPlan = configPlanFromResolution(configResolution, accepted);
|
|
306
|
-
if (configPlan.kind === "write") {
|
|
307
|
-
await runInitSteps(planExistingInit({
|
|
308
|
-
linked: true,
|
|
309
|
-
yes,
|
|
310
|
-
agentSetup: "skip",
|
|
311
|
-
config: configPlan
|
|
312
|
-
}), {
|
|
313
|
-
cwd,
|
|
314
|
-
run,
|
|
315
|
-
forward,
|
|
316
|
-
authEnv,
|
|
317
|
-
narrate: "human"
|
|
318
|
-
});
|
|
319
|
-
const context = readContextFile(contextFile);
|
|
320
|
-
const branch = contextBranch(context);
|
|
321
|
-
if (shouldRefreshEnvAfterNewConfig({
|
|
322
|
-
wroteNewFile: !existingConfig,
|
|
323
|
-
projectId: context.projectId,
|
|
324
|
-
branch
|
|
325
|
-
})) try {
|
|
326
|
-
await runInitSteps([["env", "pull"]], {
|
|
327
|
-
cwd,
|
|
328
|
-
run,
|
|
329
|
-
forward,
|
|
330
|
-
authEnv,
|
|
331
|
-
narrate: "human"
|
|
332
|
-
});
|
|
333
|
-
} catch (err) {
|
|
334
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
335
|
-
log.warning(`Created neon.ts, but pulling its Neon env vars failed: %s
|
|
336
|
-
Run \`${getCliName()} env pull\` once resolved.`, message);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
printInitDone(formatInitDone({
|
|
340
|
-
heading: "Neon setup complete.",
|
|
341
|
-
rows: [
|
|
342
|
-
{
|
|
343
|
-
label: "Agents",
|
|
344
|
-
value: agentSetupLabel(agentSetup)
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
label: "Project",
|
|
348
|
-
value: projectRow({
|
|
349
|
-
alreadyLinked,
|
|
350
|
-
linkedNow: acceptedLink
|
|
351
|
-
})
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
label: "Config",
|
|
355
|
-
value: configSummaryLabel(configSummary({
|
|
356
|
-
plan: configPlan,
|
|
357
|
-
existingConfig
|
|
358
|
-
}))
|
|
359
|
-
}
|
|
360
|
-
],
|
|
361
|
-
next: []
|
|
362
|
-
}));
|
|
363
|
-
recordInitSuccess(templateChoice.kind === "existing" ? "existing" : "empty-skip", agentSetup);
|
|
106
|
+
await runInit(props);
|
|
364
107
|
};
|
|
365
108
|
//#endregion
|
|
366
|
-
export { builder, command, describe, handler,
|
|
109
|
+
export { builder, command, describe, handler, init_exports as t };
|
package/dist/commands/mcp.js
CHANGED
|
@@ -8,16 +8,18 @@ import { writer } from "../writer.js";
|
|
|
8
8
|
import { noPassthrough, single } from "../utils/flags.js";
|
|
9
9
|
import { NEON_MCP_CATEGORIES, NEON_MCP_URL, existingNeonApiKey, installNeonMcpServer, neonMcpUrl, parseMcpCategories, trackedProjectMcpConfig } from "../mcp/install.js";
|
|
10
10
|
import { mcpInstallableAgents, resolveInstallTargets } from "../mcp/targets.js";
|
|
11
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
12
11
|
import { mintMcpApiKey, mintedKeyRevokeCommand, withdrawMintedKey } from "../mcp/mint.js";
|
|
13
|
-
import {
|
|
12
|
+
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
14
13
|
import { resolveMcpPlan } from "../mcp/plan.js";
|
|
15
14
|
//#region src/commands/mcp.ts
|
|
16
15
|
var mcp_exports = /* @__PURE__ */ __exportAll({
|
|
17
16
|
builder: () => builder,
|
|
18
17
|
command: () => "mcp",
|
|
19
18
|
describe: () => describe,
|
|
20
|
-
handler: () => handler
|
|
19
|
+
handler: () => handler,
|
|
20
|
+
mcpInstallError: () => mcpInstallError,
|
|
21
|
+
reportMcpInstall: () => reportMcpInstall,
|
|
22
|
+
setupNeonMcp: () => setupNeonMcp
|
|
21
23
|
});
|
|
22
24
|
const command = "mcp";
|
|
23
25
|
const describe = "Install the Neon MCP server into coding agents";
|
|
@@ -87,21 +89,30 @@ const builder = (argv) => argv.usage("$0 mcp [options]").options({
|
|
|
87
89
|
return parseMcpCategories(parts);
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
|
-
}).example("$0 mcp", "Interactive: config location, agents,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
}).example("$0 mcp", "Interactive: config location, agents, then auth").example("$0 mcp -y", "Global config, installed apps else the host CLI agent, reuse or mint an API key").example("$0 mcp --oauth", "Install with OAuth; the agent signs in on first use").example("$0 mcp --agent cursor --agent claude-code", "Install into specific agents").example("$0 mcp --project", "Write project-level config").example("$0 mcp --read-only", "Hide write tools via ?readonly=true").example("$0 mcp --project-id <id>", "Pin tools to one project via ?projectId=").example("$0 mcp --category querying --category schema", "Limit tools to those categories").epilogue(helpEpilogue(`Installs ${NEON_MCP_URL}`, helpCsv("Supported agents at global scope", mcpGlobalAgents), helpCsv("--project does not support", mcpProjectDroppedAgents), helpCsv("Supported categories", NEON_MCP_CATEGORIES), "neon mcp -y:", " global config", " globally installed apps, else the host CLI agent", " reuse an existing Neon MCP API key, else mint an account-wide key", " write tools on, all categories", " no project pin (including from .neon)")).strict().check(noPassthrough("mcp"));
|
|
93
|
+
/**
|
|
94
|
+
* Resolves auth, mints or reuses an API key, and writes the Neon MCP server into every
|
|
95
|
+
* resolved agent. No CLI-only concerns here (no `writer` table, no
|
|
96
|
+
* `recordCommandSuccessExtras`): a caller that only wants the outcome — `neon init` /
|
|
97
|
+
* `neon bootstrap` — gets it without rendering a table.
|
|
98
|
+
*
|
|
99
|
+
* Throws directly when zero agents succeed, since that path never reaches a results table
|
|
100
|
+
* worth showing (same as the CLI handler always did).
|
|
101
|
+
*/
|
|
102
|
+
const setupNeonMcp = async (options) => {
|
|
103
|
+
const cwd = options.cwd ?? process.cwd();
|
|
104
|
+
const interactive = canPickAgentsInteractively() && options.yes !== true;
|
|
105
|
+
const linkedProjectId = readContextFile(options.contextFile).projectId;
|
|
95
106
|
const plan = await resolveMcpPlan({
|
|
96
|
-
project:
|
|
97
|
-
oauth:
|
|
98
|
-
agents:
|
|
99
|
-
yes:
|
|
107
|
+
project: options.project === true,
|
|
108
|
+
oauth: options.oauth === true,
|
|
109
|
+
agents: options.agent ?? [],
|
|
110
|
+
yes: options.yes === true,
|
|
100
111
|
cwd,
|
|
101
112
|
interactive,
|
|
102
|
-
readOnly:
|
|
103
|
-
projectId:
|
|
104
|
-
categories:
|
|
113
|
+
readOnly: options.readOnly === true,
|
|
114
|
+
projectId: options.projectId,
|
|
115
|
+
categories: options.category ?? [],
|
|
105
116
|
linkedProjectId
|
|
106
117
|
});
|
|
107
118
|
const { install, skipped } = resolveInstallTargets({
|
|
@@ -126,22 +137,8 @@ const handler = async (props) => {
|
|
|
126
137
|
cwd
|
|
127
138
|
}) : void 0;
|
|
128
139
|
if (plan.auth === "api-key" && !existing) {
|
|
129
|
-
if (!
|
|
130
|
-
if (!canPickAgentsInteractively() &&
|
|
131
|
-
}
|
|
132
|
-
if (interactive) {
|
|
133
|
-
if (!await confirmMcpInstall({
|
|
134
|
-
scope: plan.scope,
|
|
135
|
-
install,
|
|
136
|
-
skipped,
|
|
137
|
-
auth: plan.auth,
|
|
138
|
-
reuse: existing !== void 0,
|
|
139
|
-
url,
|
|
140
|
-
mintProjectId: plan.urlProjectId
|
|
141
|
-
})) {
|
|
142
|
-
log.info("Aborted. Nothing was written.");
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
140
|
+
if (!options.apiClient || !options.apiKey) throw new Error(`Authentication required. Run \`${getCliName()} auth\`, pass --api-key or use --oauth to install without a Neon credential.`);
|
|
141
|
+
if (!canPickAgentsInteractively() && options.yes !== true && (options.agent ?? []).length === 0) throw new Error("No interactive terminal. Pass -y to mint into every detected agent, --agent <name> to name them or --oauth to install without minting.");
|
|
145
142
|
}
|
|
146
143
|
let auth;
|
|
147
144
|
let minted;
|
|
@@ -155,7 +152,7 @@ const handler = async (props) => {
|
|
|
155
152
|
if (plan.urlProjectId) log.warning("That key keeps its existing scope. Remove the Neon MCP entry to mint a project-scoped key, or pass --oauth to pin tools without a key.");
|
|
156
153
|
} else {
|
|
157
154
|
minted = await mintMcpApiKey({
|
|
158
|
-
apiClient:
|
|
155
|
+
apiClient: options.apiClient,
|
|
159
156
|
projectId: plan.urlProjectId
|
|
160
157
|
});
|
|
161
158
|
auth = {
|
|
@@ -198,15 +195,28 @@ const handler = async (props) => {
|
|
|
198
195
|
error: row.error
|
|
199
196
|
});
|
|
200
197
|
if (successes === 0) {
|
|
201
|
-
if (minted &&
|
|
202
|
-
const withdrawn = await withdrawMintedKey(
|
|
198
|
+
if (minted && options.apiClient) {
|
|
199
|
+
const withdrawn = await withdrawMintedKey(options.apiClient, minted);
|
|
203
200
|
throw new Error(withdrawn ? "Failed to write Neon MCP config to any agent. The minted API key has been revoked." : `Failed to write Neon MCP config to any agent. The minted API key could NOT be revoked. Remove it with \`${mintedKeyRevokeCommand(minted)}\`.`);
|
|
204
201
|
}
|
|
205
202
|
throw new Error("Failed to write Neon MCP config to any agent.");
|
|
206
203
|
}
|
|
207
|
-
|
|
204
|
+
return {
|
|
205
|
+
scope: plan.scope,
|
|
206
|
+
rows,
|
|
207
|
+
failedAgents,
|
|
208
|
+
url,
|
|
209
|
+
auth: auth.kind,
|
|
210
|
+
...minted ? { minted } : {}
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
/** The exact error `neon mcp` throws when some (not all) agents failed, or `undefined` on full success. */
|
|
214
|
+
const mcpInstallError = (outcome) => outcome.failedAgents.length > 0 ? /* @__PURE__ */ new Error(`Failed to write Neon MCP config for: ${outcome.failedAgents.join(", ")}.`) : void 0;
|
|
215
|
+
/** Writes the results table plus the URL/minted-key/OAuth notices. Shared by `neon mcp` and any in-process caller that wants the same human output. */
|
|
216
|
+
const reportMcpInstall = (props, outcome) => {
|
|
217
|
+
log.info("URL: %s", outcome.url);
|
|
208
218
|
const out = writer(props);
|
|
209
|
-
out.write(rows, {
|
|
219
|
+
out.write(outcome.rows, {
|
|
210
220
|
fields: [
|
|
211
221
|
"agent",
|
|
212
222
|
"status",
|
|
@@ -215,10 +225,27 @@ const handler = async (props) => {
|
|
|
215
225
|
title: "MCP"
|
|
216
226
|
});
|
|
217
227
|
out.end();
|
|
218
|
-
if (minted) log.info("Minted API key %s (id %d, %s). Revoke with: %s", minted.name, minted.id, minted.projectId ? "project" : "account", mintedKeyRevokeCommand(minted));
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
228
|
+
if (outcome.minted) log.info("Minted API key %s (id %d, %s). Revoke with: %s", outcome.minted.name, outcome.minted.id, outcome.minted.projectId ? "project" : "account", mintedKeyRevokeCommand(outcome.minted));
|
|
229
|
+
if (outcome.auth === "oauth") log.info("The agent will prompt for Neon sign-in on first use.");
|
|
230
|
+
};
|
|
231
|
+
const handler = async (props) => {
|
|
232
|
+
const outcome = await setupNeonMcp({
|
|
233
|
+
apiClient: props.apiClient,
|
|
234
|
+
apiKey: props.apiKey,
|
|
235
|
+
contextFile: props.contextFile,
|
|
236
|
+
cwd: props.cwd,
|
|
237
|
+
yes: props.yes,
|
|
238
|
+
oauth: props.oauth,
|
|
239
|
+
project: props.project,
|
|
240
|
+
agent: props.agent,
|
|
241
|
+
readOnly: props.readOnly,
|
|
242
|
+
projectId: props.projectId,
|
|
243
|
+
category: props.category
|
|
244
|
+
});
|
|
245
|
+
reportMcpInstall(props, outcome);
|
|
246
|
+
const error = mcpInstallError(outcome);
|
|
247
|
+
if (error) throw error;
|
|
248
|
+
recordCommandSuccessExtras({ scope: outcome.scope });
|
|
222
249
|
};
|
|
223
250
|
//#endregion
|
|
224
|
-
export { builder, command, describe, handler, mcp_exports as t };
|
|
251
|
+
export { builder, command, describe, handler, mcpInstallError, reportMcpInstall, setupNeonMcp, mcp_exports as t };
|