neon 4.6.0 → 4.8.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 +48 -113
- package/dist/_chunks/{auth_selection-ktL6uoEe.js → auth_selection-pitMFzDe.js} +1 -1
- package/dist/_chunks/{credential_io-B1JxWELN.js → credential_io-YeAxg9Xn.js} +2 -2
- package/dist/_chunks/{paths-g3DRqJiD.js → paths-DMq0Lt7a.js} +1 -1
- package/dist/_chunks/{profiles-D95AeZ5v.js → profiles-CvnFEQyd.js} +1 -1
- package/dist/analytics.js +2 -2
- package/dist/auth_context.js +1 -1
- package/dist/commands/auth.js +12 -9
- package/dist/commands/bootstrap.js +72 -43
- package/dist/commands/claim.js +1 -1
- package/dist/commands/env.js +3 -20
- package/dist/commands/init.js +80 -79
- package/dist/commands/link.js +110 -292
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/plugins.js +3 -3
- package/dist/commands/profile.js +4 -4
- package/dist/config.js +1 -1
- package/dist/credential_io.js +1 -1
- package/dist/index.js +1 -1
- package/dist/init/child.js +36 -0
- package/dist/init/plan.js +47 -0
- package/dist/init/tooling.js +43 -0
- package/dist/init/wizard.js +42 -0
- package/dist/mcp/targets.js +1 -1
- package/dist/plugins/plan.js +1 -1
- package/dist/plugins/targets.js +5 -1
- package/dist/retire_credential.js +1 -1
- package/dist/utils/cli_name.js +1 -12
- package/dist/utils/middlewares.js +1 -1
- package/dist/utils/package_manager.js +10 -5
- package/package.json +13 -14
- package/dist/init/auth.js +0 -53
- package/dist/init/build_config.js +0 -4
- package/dist/init/detect_agent.js +0 -63
- package/dist/init/editors.js +0 -19
- package/dist/init/enrich_output.js +0 -61
- package/dist/init/extension.js +0 -154
- package/dist/init/inspect.js +0 -249
- package/dist/init/interactive.js +0 -529
- package/dist/init/neonctl.js +0 -139
- package/dist/init/orchestrate.js +0 -189
- package/dist/init/phases/auth.js +0 -195
- package/dist/init/phases/cleanup.js +0 -27
- package/dist/init/phases/db.js +0 -257
- package/dist/init/phases/getting_started.js +0 -218
- package/dist/init/phases/mcp.js +0 -219
- package/dist/init/phases/migrations.js +0 -250
- package/dist/init/phases/neon_auth.js +0 -116
- package/dist/init/phases/setup.js +0 -582
- package/dist/init/phases/skills.js +0 -86
- package/dist/init/phases/status.js +0 -82
- package/dist/init/profile_cli.js +0 -56
- package/dist/init/resolve_context.js +0 -105
- package/dist/init/route_command.js +0 -93
- package/dist/init/skills.js +0 -206
- package/dist/init/types.js +0 -1
- package/dist/init/vsix.js +0 -89
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { log } from "./log.js";
|
|
2
2
|
import pkg_default from "./pkg.js";
|
|
3
3
|
import { isNeonApiError, messageFromBody } from "./api.js";
|
|
4
|
-
import {
|
|
4
|
+
import { r as defaultDir } from "./_chunks/paths-DMq0Lt7a.js";
|
|
5
5
|
import { ensureConfigDir } from "./config.js";
|
|
6
6
|
import { authFailureMessage, credentialsToClearOn401, getAuthContext } from "./auth_context.js";
|
|
7
7
|
import { currentContextFile, enrichFromContext } from "./context.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
//#region src/init/child.ts
|
|
3
|
+
const AUTH_CHILD = /* @__PURE__ */ new Set([
|
|
4
|
+
"bootstrap",
|
|
5
|
+
"link",
|
|
6
|
+
"mcp"
|
|
7
|
+
]);
|
|
8
|
+
const initChildEnv = (command, overlay, base = process.env) => {
|
|
9
|
+
const child = { ...base };
|
|
10
|
+
if (command === void 0 || !AUTH_CHILD.has(command)) {
|
|
11
|
+
for (const key of Object.keys(child)) if (key.toUpperCase() === "NEON_API_KEY") delete child[key];
|
|
12
|
+
}
|
|
13
|
+
return overlay ? {
|
|
14
|
+
...child,
|
|
15
|
+
...overlay
|
|
16
|
+
} : child;
|
|
17
|
+
};
|
|
18
|
+
const spawnCliChild = async (argv, cwd, overlay) => {
|
|
19
|
+
const cli = process.argv[1];
|
|
20
|
+
if (!cli) throw new Error("Cannot re-exec the Neon CLI: process.argv[1] is missing.");
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
const child = spawn(process.execPath, [cli, ...argv], {
|
|
23
|
+
cwd,
|
|
24
|
+
stdio: "inherit",
|
|
25
|
+
env: initChildEnv(argv[0], overlay)
|
|
26
|
+
});
|
|
27
|
+
child.on("error", () => {
|
|
28
|
+
resolve(false);
|
|
29
|
+
});
|
|
30
|
+
child.on("close", (code) => {
|
|
31
|
+
resolve(code === 0);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { AUTH_CHILD, initChildEnv, spawnCliChild };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
2
|
+
//#region src/init/plan.ts
|
|
3
|
+
const directoryIsEmpty = (names) => names.filter((name) => name !== ".git").length === 0;
|
|
4
|
+
const bootstrapInitStep = (yes) => yes ? [
|
|
5
|
+
"bootstrap",
|
|
6
|
+
".",
|
|
7
|
+
"--default"
|
|
8
|
+
] : ["bootstrap", "."];
|
|
9
|
+
const projectContextFile = (projectDir, contextFile) => {
|
|
10
|
+
const resolved = isAbsolute(contextFile) ? contextFile : resolve(projectDir, contextFile);
|
|
11
|
+
const rel = relative(projectDir, resolved);
|
|
12
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return join(projectDir, ".neon");
|
|
13
|
+
return resolved;
|
|
14
|
+
};
|
|
15
|
+
const planAgentSteps = (input) => {
|
|
16
|
+
const y = input.yes ? ["-y"] : [];
|
|
17
|
+
if (input.agentSetup === "plugin") return [["plugins", ...y]];
|
|
18
|
+
if (input.agentSetup === "skills-mcp") return [["skills", ...y], ["mcp", ...y]];
|
|
19
|
+
return [];
|
|
20
|
+
};
|
|
21
|
+
const planExistingInit = (input) => {
|
|
22
|
+
const steps = [...planAgentSteps(input)];
|
|
23
|
+
if (!input.linked) steps.push(input.yes ? ["link", "--yes"] : ["link"]);
|
|
24
|
+
steps.push(input.yes ? [
|
|
25
|
+
"config",
|
|
26
|
+
"init",
|
|
27
|
+
"--services",
|
|
28
|
+
"none"
|
|
29
|
+
] : ["config", "init"]);
|
|
30
|
+
return steps;
|
|
31
|
+
};
|
|
32
|
+
const resolveInitAgentSetup = async (input) => {
|
|
33
|
+
if (input.yes) return input.hasProjectPlugins ? "plugin" : "skills-mcp";
|
|
34
|
+
if (input.interactive) return input.pick();
|
|
35
|
+
return "skills-mcp";
|
|
36
|
+
};
|
|
37
|
+
const childArgv = (step, forward) => {
|
|
38
|
+
const args = [...step];
|
|
39
|
+
if (forward.configDir) args.push("--config-dir", forward.configDir);
|
|
40
|
+
if (forward.profile) args.push("--profile", forward.profile);
|
|
41
|
+
args.push("--api-host", forward.apiHost);
|
|
42
|
+
args.push("--context-file", forward.contextFile);
|
|
43
|
+
if (forward.analytics === false) args.push("--no-analytics");
|
|
44
|
+
return args;
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
47
|
+
export { bootstrapInitStep, childArgv, directoryIsEmpty, planAgentSteps, planExistingInit, projectContextFile, resolveInitAgentSetup };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { log } from "../log.js";
|
|
2
|
+
import { getCliName } from "../utils/cli_name.js";
|
|
3
|
+
import { AUTH_CHILD } from "./child.js";
|
|
4
|
+
import { childArgv, planAgentSteps, resolveInitAgentSetup } from "./plan.js";
|
|
5
|
+
import { detectInstallablePluginsAgents } from "../plugins/targets.js";
|
|
6
|
+
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
7
|
+
import { pickAgentSetupInteractively } from "./wizard.js";
|
|
8
|
+
//#region src/init/tooling.ts
|
|
9
|
+
const defaultHasProjectPlugins = async (cwd) => {
|
|
10
|
+
return (await detectInstallablePluginsAgents({
|
|
11
|
+
scope: "project",
|
|
12
|
+
cwd
|
|
13
|
+
})).length > 0;
|
|
14
|
+
};
|
|
15
|
+
const runInitSteps = async (steps, options) => {
|
|
16
|
+
for (const step of steps) {
|
|
17
|
+
log.info("Running `%s %s`", getCliName(), step.join(" "));
|
|
18
|
+
const argv = childArgv(step, options.forward);
|
|
19
|
+
const command = step[0];
|
|
20
|
+
if (!await options.run(argv, options.cwd, command !== void 0 && AUTH_CHILD.has(command) ? options.authEnv : void 0)) throw new Error(`\`${getCliName()} ${step.join(" ")}\` failed.`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const runAgentTooling = async (options) => {
|
|
24
|
+
const yes = options.yes;
|
|
25
|
+
const hasProjectPlugins = yes ? await (options.hasProjectPlugins ?? defaultHasProjectPlugins)(options.cwd) : false;
|
|
26
|
+
const interactive = !yes && (options.pickAgentSetup !== void 0 || canPickAgentsInteractively());
|
|
27
|
+
const agentSetup = await resolveInitAgentSetup({
|
|
28
|
+
yes,
|
|
29
|
+
interactive,
|
|
30
|
+
hasProjectPlugins,
|
|
31
|
+
pick: options.pickAgentSetup ?? pickAgentSetupInteractively
|
|
32
|
+
});
|
|
33
|
+
await runInitSteps(planAgentSteps({
|
|
34
|
+
yes,
|
|
35
|
+
agentSetup
|
|
36
|
+
}), options);
|
|
37
|
+
};
|
|
38
|
+
const runScaffoldFollowUp = async (options) => {
|
|
39
|
+
if (!options.skipAgentSetup) await runAgentTooling(options);
|
|
40
|
+
if (options.shouldLink) await runInitSteps([options.linkYes ? ["link", "--yes"] : ["link"]], options);
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { runAgentTooling, runInitSteps, runScaffoldFollowUp };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
2
|
+
import { NEON_PLUGIN_NAME } from "../plugins/run.js";
|
|
3
|
+
import prompts from "prompts";
|
|
4
|
+
//#region src/init/wizard.ts
|
|
5
|
+
const restoreCursorOnAbort = (state) => {
|
|
6
|
+
if (state.aborted) {
|
|
7
|
+
process.stdout.write("\x1B[?25h");
|
|
8
|
+
process.stdout.write("\n");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const pickAgentSetupInteractively = async () => {
|
|
13
|
+
if (!canPickAgentsInteractively()) throw new Error("No interactive terminal. Pass -y to use defaults, or run this command in a terminal.");
|
|
14
|
+
const { setup } = await prompts({
|
|
15
|
+
onState: restoreCursorOnAbort,
|
|
16
|
+
type: "select",
|
|
17
|
+
name: "setup",
|
|
18
|
+
message: "How should coding agents get Neon in this project?",
|
|
19
|
+
initial: 0,
|
|
20
|
+
choices: [
|
|
21
|
+
{
|
|
22
|
+
title: "Plugin (recommended)",
|
|
23
|
+
value: "plugin",
|
|
24
|
+
description: `Install ${NEON_PLUGIN_NAME} (skills and MCP in one)`
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
title: "Skills and MCP separately",
|
|
28
|
+
value: "skills-mcp",
|
|
29
|
+
description: "Install skills, then the MCP server"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
title: "Skip agent setup",
|
|
33
|
+
value: "skip",
|
|
34
|
+
description: "Continue without a plugin, skills, or MCP"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
if (setup !== "plugin" && setup !== "skills-mcp" && setup !== "skip") throw new Error("Aborted.");
|
|
39
|
+
return setup;
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
export { pickAgentSetupInteractively };
|
package/dist/mcp/targets.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mcpUnsupportedReason } from "./install.js";
|
|
2
1
|
import { detectInstalledAgents, listMcpAgentIds, resolveAddMcpAgentId, uniqueAgentIds } from "./agents.js";
|
|
2
|
+
import { mcpUnsupportedReason } from "./install.js";
|
|
3
3
|
import { detectProjectAgents } from "add-mcp";
|
|
4
4
|
//#region src/mcp/targets.ts
|
|
5
5
|
function mcpInstallableAgents(scope) {
|
package/dist/plugins/plan.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getAgentDisplayName, tryResolveAddMcpAgentId } from "../mcp/agents.js";
|
|
2
2
|
import "../init/agents.js";
|
|
3
|
-
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
4
3
|
import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents } from "./targets.js";
|
|
4
|
+
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
5
5
|
//#region src/plugins/plan.ts
|
|
6
6
|
const assertPluginsCanRun = (options) => {
|
|
7
7
|
if (options.yes || options.interactive || options.hasAgents) return;
|
package/dist/plugins/targets.js
CHANGED
|
@@ -38,6 +38,10 @@ const detectPluginsAgents = async (options) => {
|
|
|
38
38
|
const detected = options.scope === "project" ? detectProjectAgents(options.cwd) : await detectInstalledAgents();
|
|
39
39
|
return uniqueAgentIds(detected).filter((id) => supportsPlugins(id));
|
|
40
40
|
};
|
|
41
|
+
const detectInstallablePluginsAgents = async (options) => {
|
|
42
|
+
const installable = new Set(pluginsInstallableAgents(options.scope));
|
|
43
|
+
return (await detectPluginsAgents(options)).filter((id) => installable.has(id));
|
|
44
|
+
};
|
|
41
45
|
const mappedPluginsTargets = (agents, scope) => {
|
|
42
46
|
const mapped = [];
|
|
43
47
|
const byTarget = /* @__PURE__ */ new Map();
|
|
@@ -61,4 +65,4 @@ const mappedPluginsTargets = (agents, scope) => {
|
|
|
61
65
|
return mapped;
|
|
62
66
|
};
|
|
63
67
|
//#endregion
|
|
64
|
-
export { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents, pluginsMappedAgents, supportsPlugins };
|
|
68
|
+
export { detectInstallablePluginsAgents, detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents, pluginsMappedAgents, supportsPlugins };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-
|
|
1
|
+
import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-YeAxg9Xn.js";
|
|
2
2
|
import { log } from "./log.js";
|
|
3
3
|
import { getApiClient } from "./api.js";
|
|
4
4
|
import { revokeToken } from "./auth.js";
|
package/dist/utils/cli_name.js
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
//#region src/utils/cli_name.ts
|
|
3
|
-
/**
|
|
4
|
-
* The name this CLI was invoked as: `neon` (current) or `neonctl` (legacy alias).
|
|
5
|
-
*
|
|
6
|
-
* Use this for any user-facing string that suggests a command to run — help text,
|
|
7
|
-
* error hints, and especially `--agent` `next_command_template` values, which an agent
|
|
8
|
-
* executes verbatim. Hardcoding `neonctl` breaks those on installs of the `neon` package,
|
|
9
|
-
* which no longer ships a `neonctl` binary (removed in `neon@2.38.0`).
|
|
10
|
-
*
|
|
11
|
-
* Derived from `process.argv[1]`, which is fixed for the process lifetime, so the result
|
|
12
|
-
* is stable whether this is called at module load or per invocation. Mirrors the name used
|
|
13
|
-
* for yargs `.scriptName()` in `index.ts`.
|
|
14
|
-
*/
|
|
3
|
+
/** Command hints must use the invoked binary so users can run them verbatim. */
|
|
15
4
|
const getCliName = () => basename(process.argv[1] ?? "") === "neonctl" ? "neonctl" : "neon";
|
|
16
5
|
//#endregion
|
|
17
6
|
export { getCliName };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as recordCredentialInputs } from "../_chunks/auth_selection-
|
|
1
|
+
import { r as recordCredentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
2
2
|
//#region src/utils/middlewares.ts
|
|
3
3
|
/**
|
|
4
4
|
* Forwarding Yargs's default would turn ambient configuration into an explicit child override.
|
|
@@ -295,6 +295,14 @@ const DO_NOT_SUBSTITUTE_HINT = "Run this step's `command` exactly as written —
|
|
|
295
295
|
* All four managers spell the whole-manifest install `install`.
|
|
296
296
|
*/
|
|
297
297
|
const formatInstallCommand = (pm, packages, options) => `${pm} ${installArgs(pm, packages, options).join(" ")}`;
|
|
298
|
+
const commandEnv = (overlay, base = process.env) => {
|
|
299
|
+
const child = { ...base };
|
|
300
|
+
for (const key of Object.keys(child)) if (key.toUpperCase() === "NEON_API_KEY") delete child[key];
|
|
301
|
+
return overlay ? {
|
|
302
|
+
...child,
|
|
303
|
+
...overlay
|
|
304
|
+
} : child;
|
|
305
|
+
};
|
|
298
306
|
/**
|
|
299
307
|
* Run a command inheriting our stdio so the user sees install / link output
|
|
300
308
|
* live and can answer any prompts the child raises. Resolves to whether it
|
|
@@ -306,10 +314,7 @@ const runCommand = (cmd, args, cwd, env) => new Promise((resolvePromise) => {
|
|
|
306
314
|
cwd,
|
|
307
315
|
stdio: "inherit",
|
|
308
316
|
shell: process.platform === "win32",
|
|
309
|
-
|
|
310
|
-
...process.env,
|
|
311
|
-
...env
|
|
312
|
-
} } : {}
|
|
317
|
+
env: commandEnv(env)
|
|
313
318
|
});
|
|
314
319
|
child.on("error", (err) => {
|
|
315
320
|
log.warning("Could not run `%s %s`: %s", cmd, args.join(" "), err instanceof Error ? err.message : String(err));
|
|
@@ -321,4 +326,4 @@ const runCommand = (cmd, args, cwd, env) => new Promise((resolvePromise) => {
|
|
|
321
326
|
});
|
|
322
327
|
});
|
|
323
328
|
//#endregion
|
|
324
|
-
export { DO_NOT_SUBSTITUTE_HINT, MISSING_BINARY_HINT, detectInvokingPackageManager, detectProjectPackageManager, formatExecCommand, formatInstallCommand, globalInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolveInvokingPackageManager, resolvePackageManager, runCommand };
|
|
329
|
+
export { DO_NOT_SUBSTITUTE_HINT, MISSING_BINARY_HINT, commandEnv, detectInvokingPackageManager, detectProjectPackageManager, formatExecCommand, formatInstallCommand, globalInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolveInvokingPackageManager, resolvePackageManager, runCommand };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"yaml": "^2.9.0",
|
|
64
64
|
"yargs": "17.7.2",
|
|
65
65
|
"yoctocolors": "^2.1.2",
|
|
66
|
-
"@neon/config": "1.0.
|
|
67
|
-
"@neon/config-runtime": "1.0.
|
|
68
|
-
"@neon/sdk": "
|
|
66
|
+
"@neon/config": "1.0.5",
|
|
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",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"typescript": "^5.9.0",
|
|
98
98
|
"vitest": "^3.0.9",
|
|
99
99
|
"@neon-internals/cli-core": "0.0.0",
|
|
100
|
-
"@neon-internals/env-core": "0.0.
|
|
100
|
+
"@neon-internals/env-core": "0.0.5",
|
|
101
101
|
"@neon/e2e-harness": "0.0.0"
|
|
102
102
|
},
|
|
103
103
|
"publishConfig": {
|
|
@@ -120,17 +120,16 @@
|
|
|
120
120
|
},
|
|
121
121
|
"scripts": {
|
|
122
122
|
"generateParams": "tsx generateOptionsFromSpec.ts",
|
|
123
|
-
"codegen": "node scripts/set-vsx-gallery.mjs",
|
|
124
123
|
"clean": "rm -rf dist",
|
|
125
|
-
"build": "pnpm
|
|
126
|
-
"build:internal": "
|
|
124
|
+
"build": "pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist",
|
|
125
|
+
"build:internal": "pnpm build",
|
|
127
126
|
"bundle": "node pkg.js",
|
|
128
|
-
"typecheck": "
|
|
129
|
-
"lint": "pnpm
|
|
127
|
+
"typecheck": "tsc --noEmit",
|
|
128
|
+
"lint": "pnpm typecheck && biome check src",
|
|
130
129
|
"lint:fix": "pnpm typecheck && biome check src --write",
|
|
131
|
-
"test": "pnpm
|
|
132
|
-
"test:ci": "pnpm
|
|
133
|
-
"test:e2e": "pnpm
|
|
134
|
-
"test:conformance": "
|
|
130
|
+
"test": "pnpm --filter neonctl... build && vitest run",
|
|
131
|
+
"test:ci": "pnpm build && vitest run",
|
|
132
|
+
"test:e2e": "pnpm build && vitest run --config vitest.e2e.config.ts",
|
|
133
|
+
"test:conformance": "vitest run --config tests/psql-conformance/vitest.config.ts"
|
|
135
134
|
}
|
|
136
135
|
}
|
package/dist/init/auth.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { l as interpretCredentials, t as storeFor } from "../_chunks/credential_io-B1JxWELN.js";
|
|
2
|
-
import { l as locationForName } from "../_chunks/profiles-D95AeZ5v.js";
|
|
3
|
-
import { npxNeonArgs, selectedConfigDir, selectedProfileName } from "./profile_cli.js";
|
|
4
|
-
import { log } from "@clack/prompts";
|
|
5
|
-
import { execa } from "execa";
|
|
6
|
-
//#region src/init/auth.ts
|
|
7
|
-
/**
|
|
8
|
-
* Ensures the Neon CLI is authenticated by running a command that triggers auth if needed
|
|
9
|
-
* This will automatically start the OAuth flow if the user isn't already authenticated
|
|
10
|
-
*/
|
|
11
|
-
async function ensureNeonctlAuth(options) {
|
|
12
|
-
const quiet = options?.json === true;
|
|
13
|
-
if (await getNeonctlAccessToken()) return true;
|
|
14
|
-
try {
|
|
15
|
-
await execa("npx", npxNeonArgs(["me"]), {
|
|
16
|
-
stdio: "inherit",
|
|
17
|
-
env: {
|
|
18
|
-
...process.env,
|
|
19
|
-
CI: void 0
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
return true;
|
|
23
|
-
} catch (error) {
|
|
24
|
-
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
25
|
-
if (!quiet) {
|
|
26
|
-
if (msg.includes("interactive auth") || msg.includes("CI")) log.error("Auth requires an interactive terminal. Run neon init in your system terminal (outside the chat) to sign in.");
|
|
27
|
-
else log.error(`Authentication failed: ${msg}`);
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Checks whether the Neon CLI has stored OAuth credentials.
|
|
34
|
-
*/
|
|
35
|
-
async function isAuthenticated() {
|
|
36
|
-
return await getNeonctlAccessToken() !== null;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Sharing the reader keeps init aligned with CLI path and API-key handling.
|
|
40
|
-
* Unreadable credentials throw so browser sign-in cannot overwrite them.
|
|
41
|
-
*/
|
|
42
|
-
async function getNeonctlAccessToken() {
|
|
43
|
-
const dir = selectedConfigDir();
|
|
44
|
-
const at = locationForName(dir, selectedProfileName());
|
|
45
|
-
const loaded = storeFor(dir).read(at);
|
|
46
|
-
if (loaded === null) return null;
|
|
47
|
-
const credential = interpretCredentials(loaded.credentials, at, loaded.backend);
|
|
48
|
-
if (credential.kind === "api_key") return credential.apiKey;
|
|
49
|
-
const token = loaded.credentials.access_token;
|
|
50
|
-
return typeof token === "string" && token.trim() !== "" ? token : null;
|
|
51
|
-
}
|
|
52
|
-
//#endregion
|
|
53
|
-
export { ensureNeonctlAuth, isAuthenticated };
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
//#region src/init/detect_agent.ts
|
|
3
|
-
/**
|
|
4
|
-
* Detects the IDE/editor the terminal is running in, regardless of which
|
|
5
|
-
* agent is active. Used for extension installation — the extension goes
|
|
6
|
-
* into the IDE, not the agent.
|
|
7
|
-
*
|
|
8
|
-
* Returns an Editor-compatible string: "Cursor", "VS Code", "Windsurf", or null.
|
|
9
|
-
*/
|
|
10
|
-
function detectIde() {
|
|
11
|
-
const env = process.env;
|
|
12
|
-
if (env.TERM_PROGRAM === "cursor" || env.CURSOR_TRACE_ID !== void 0 || env.CURSOR_EXTENSION_HOST_ROLE !== void 0 || env.CURSOR_LAYOUT !== void 0 || env.CURSOR_SPAWNED_BY_EXTENSION_ID !== void 0) return "Cursor";
|
|
13
|
-
if (env.GIT_ASKPASS?.includes("Cursor.app") || env.VSCODE_GIT_ASKPASS_NODE?.includes("Cursor.app") || env.GIT_ASKPASS?.includes("cursor") || env.VSCODE_GIT_ASKPASS_NODE?.includes("cursor")) return "Cursor";
|
|
14
|
-
if (env.TERM_PROGRAM === "windsurf") return "Windsurf";
|
|
15
|
-
if (env.TERM_PROGRAM === "vscode" || env.VSCODE_PID !== void 0 || env.VSCODE_CWD !== void 0) {
|
|
16
|
-
const envValues = Object.values(env).join("\n").toLowerCase();
|
|
17
|
-
if (envValues.includes("cursor.app") || envValues.includes("/cursor/")) return "Cursor";
|
|
18
|
-
if (isCursorInstalled() && !isVSCodeInstalled()) return "Cursor";
|
|
19
|
-
return "VS Code";
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
function isCursorInstalled() {
|
|
24
|
-
if (process.platform === "darwin") return existsSync("/Applications/Cursor.app");
|
|
25
|
-
if (process.platform === "linux") return existsSync("/usr/share/cursor") || existsSync("/usr/bin/cursor");
|
|
26
|
-
if (process.platform === "win32") {
|
|
27
|
-
const localAppData = process.env.LOCALAPPDATA || "";
|
|
28
|
-
return existsSync(`${localAppData}\\Programs\\Cursor\\Cursor.exe`) || existsSync(`${localAppData}\\cursor\\Cursor.exe`);
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
function isVSCodeInstalled() {
|
|
33
|
-
if (process.platform === "darwin") return existsSync("/Applications/Visual Studio Code.app");
|
|
34
|
-
if (process.platform === "linux") return existsSync("/usr/share/code") || existsSync("/usr/bin/code");
|
|
35
|
-
if (process.platform === "win32") {
|
|
36
|
-
const localAppData = process.env.LOCALAPPDATA || "";
|
|
37
|
-
const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
|
|
38
|
-
return existsSync(`${localAppData}\\Programs\\Microsoft VS Code\\Code.exe`) || existsSync(`${programFiles}\\Microsoft VS Code\\Code.exe`);
|
|
39
|
-
}
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Detects which agent/IDE is running the CLI from environment variables.
|
|
44
|
-
* Returns the add-mcp compatible agent ID (e.g. "cursor", "claude-code").
|
|
45
|
-
*
|
|
46
|
-
* Delegates IDE detection to detectIde() to avoid duplicating Cursor/VS Code
|
|
47
|
-
* heuristics, then checks for agent-specific env vars.
|
|
48
|
-
*/
|
|
49
|
-
function detectAgent() {
|
|
50
|
-
const env = process.env;
|
|
51
|
-
if (env.CLAUDECODE === "1" || env.CLAUDE_CODE === "1" || env.CLAUDE_CLI === "1") return "claude-code";
|
|
52
|
-
if (env.CODEX === "1") return "codex";
|
|
53
|
-
if (env.CLINE === "1") return "cline";
|
|
54
|
-
const ide = detectIde();
|
|
55
|
-
if (ide) return {
|
|
56
|
-
Cursor: "cursor",
|
|
57
|
-
"VS Code": "vscode",
|
|
58
|
-
Windsurf: "windsurf"
|
|
59
|
-
}[ide] ?? null;
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
//#endregion
|
|
63
|
-
export { detectAgent, detectIde, isCursorInstalled, isVSCodeInstalled };
|
package/dist/init/editors.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { detectInstalledAgents } from "../mcp/agents.js";
|
|
2
|
-
import "./agents.js";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
//#region src/init/editors.ts
|
|
5
|
-
function getVSCodeGlobalConfigDir(homeDir) {
|
|
6
|
-
const platform = process.platform;
|
|
7
|
-
if (platform === "darwin") return resolve(homeDir, "Library", "Application Support", "Code", "User");
|
|
8
|
-
if (platform === "linux") return resolve(homeDir, ".config", "Code", "User");
|
|
9
|
-
if (platform === "win32") {
|
|
10
|
-
const appData = process.env.APPDATA;
|
|
11
|
-
if (appData) return resolve(appData, "Code", "User");
|
|
12
|
-
}
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
async function detectAvailableEditors(_homeDir) {
|
|
16
|
-
return detectInstalledAgents();
|
|
17
|
-
}
|
|
18
|
-
//#endregion
|
|
19
|
-
export { detectAvailableEditors, getVSCodeGlobalConfigDir };
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { neonInitAgentCmd } from "./profile_cli.js";
|
|
2
|
-
//#region src/init/enrich_output.ts
|
|
3
|
-
/**
|
|
4
|
-
* Converts a phase's `args` (e.g. ["neon-auth", "--json", "--setup"]) into the
|
|
5
|
-
* `neon init --agent --data` invocation that reaches the same handler.
|
|
6
|
-
*/
|
|
7
|
-
function argsToCommand(args) {
|
|
8
|
-
const data = {};
|
|
9
|
-
let i = 0;
|
|
10
|
-
if (args.length > 0 && !args[0].startsWith("-")) {
|
|
11
|
-
data.step = args[0];
|
|
12
|
-
i = 1;
|
|
13
|
-
}
|
|
14
|
-
while (i < args.length) {
|
|
15
|
-
const arg = args[i];
|
|
16
|
-
if (arg.startsWith("--")) {
|
|
17
|
-
const key = arg.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
18
|
-
const next = args[i + 1];
|
|
19
|
-
if (key === "json") {
|
|
20
|
-
i += 1;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (key === "profile" || key === "configDir") {
|
|
24
|
-
i += next !== void 0 && !next.startsWith("-") ? 2 : 1;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (next !== void 0 && !next.startsWith("-")) {
|
|
28
|
-
data[key] = next;
|
|
29
|
-
i += 2;
|
|
30
|
-
} else {
|
|
31
|
-
data[key] = true;
|
|
32
|
-
i += 1;
|
|
33
|
-
}
|
|
34
|
-
} else i += 1;
|
|
35
|
-
}
|
|
36
|
-
return neonInitAgentCmd(data);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Walks a phase response object and:
|
|
40
|
-
* 1. Replaces `args` arrays with `command` strings (neon init --data format)
|
|
41
|
-
* 2. Renames `run_neon_init` → `run_shell_command`
|
|
42
|
-
* 3. Adds a description to finalize steps
|
|
43
|
-
*/
|
|
44
|
-
function enrichResponse(obj) {
|
|
45
|
-
if (obj === null || typeof obj !== "object") return obj;
|
|
46
|
-
if (Array.isArray(obj)) return obj.map(enrichResponse);
|
|
47
|
-
const record = obj;
|
|
48
|
-
const result = {};
|
|
49
|
-
for (const [key, value] of Object.entries(record)) result[key] = enrichResponse(value);
|
|
50
|
-
if (Array.isArray(result.args)) {
|
|
51
|
-
result.command = argsToCommand(result.args);
|
|
52
|
-
delete result.args;
|
|
53
|
-
}
|
|
54
|
-
if (result.type === "run_neon_init") {
|
|
55
|
-
result.type = "run_shell_command";
|
|
56
|
-
if (typeof result.command === "string" && result.command.includes("\"step\":\"finalize\"")) result.description = "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this.";
|
|
57
|
-
}
|
|
58
|
-
return result;
|
|
59
|
-
}
|
|
60
|
-
//#endregion
|
|
61
|
-
export { enrichResponse };
|