neon 4.7.0 → 4.9.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 +49 -59
- 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/branches.js +29 -14
- package/dist/commands/claim.js +1 -1
- package/dist/commands/init.js +80 -79
- package/dist/commands/link.js +78 -63
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/plugins.js +4 -4
- package/dist/commands/profile.js +4 -4
- package/dist/commands/projects.js +17 -6
- package/dist/commands/skills.js +1 -1
- 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/middlewares.js +1 -1
- package/dist/utils/package_manager.js +10 -5
- package/package.json +15 -16
- 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
|
@@ -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";
|
|
@@ -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.9.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/sdk": "
|
|
68
|
-
"@neon/config
|
|
66
|
+
"@neon/config-runtime": "1.0.5",
|
|
67
|
+
"@neon/sdk": "3.0.0",
|
|
68
|
+
"@neon/config": "1.0.5"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
71
|
"@napi-rs/keyring": "1.3.0",
|
|
@@ -96,9 +96,9 @@
|
|
|
96
96
|
"tsx": "4.22.3",
|
|
97
97
|
"typescript": "^5.9.0",
|
|
98
98
|
"vitest": "^3.0.9",
|
|
99
|
-
"@neon-internals/
|
|
100
|
-
"@neon
|
|
101
|
-
"@neon/
|
|
99
|
+
"@neon-internals/env-core": "0.0.5",
|
|
100
|
+
"@neon/e2e-harness": "0.0.0",
|
|
101
|
+
"@neon-internals/cli-core": "0.0.0"
|
|
102
102
|
},
|
|
103
103
|
"publishConfig": {
|
|
104
104
|
"access": "public",
|
|
@@ -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 };
|
package/dist/init/extension.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { NEON_EXTENSION_ID, downloadVsix } from "./vsix.js";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { unlink } from "node:fs/promises";
|
|
4
|
-
import { execa } from "execa";
|
|
5
|
-
//#region src/init/extension.ts
|
|
6
|
-
/**
|
|
7
|
-
* Uses macOS mdfind to locate an app by bundle identifier
|
|
8
|
-
*/
|
|
9
|
-
async function findAppWithMdfind(bundleId) {
|
|
10
|
-
try {
|
|
11
|
-
return (await execa("mdfind", [`kMDItemCFBundleIdentifier == '${bundleId}'`], { timeout: 5e3 })).stdout.trim().split("\n").filter(Boolean)[0] || null;
|
|
12
|
-
} catch {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Known installation paths for VS Code CLI
|
|
18
|
-
*/
|
|
19
|
-
function getVSCodePaths() {
|
|
20
|
-
const platform = process.platform;
|
|
21
|
-
const home = process.env.HOME || "";
|
|
22
|
-
if (platform === "darwin") return [
|
|
23
|
-
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
|
|
24
|
-
"/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code-insiders",
|
|
25
|
-
`${home}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`,
|
|
26
|
-
`${home}/Downloads/Visual Studio Code.app/Contents/Resources/app/bin/code`
|
|
27
|
-
];
|
|
28
|
-
if (platform === "linux") return [
|
|
29
|
-
"/usr/share/code/bin/code",
|
|
30
|
-
"/usr/bin/code",
|
|
31
|
-
"/snap/bin/code",
|
|
32
|
-
"/usr/share/code-insiders/bin/code-insiders"
|
|
33
|
-
];
|
|
34
|
-
if (platform === "win32") {
|
|
35
|
-
const localAppData = process.env.LOCALAPPDATA || "";
|
|
36
|
-
const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
|
|
37
|
-
return [
|
|
38
|
-
`${localAppData}\\Programs\\Microsoft VS Code\\bin\\code.cmd`,
|
|
39
|
-
`${programFiles}\\Microsoft VS Code\\bin\\code.cmd`,
|
|
40
|
-
`${localAppData}\\Programs\\Microsoft VS Code Insiders\\bin\\code-insiders.cmd`
|
|
41
|
-
];
|
|
42
|
-
}
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Known installation paths for Cursor CLI
|
|
47
|
-
*/
|
|
48
|
-
function getCursorPaths() {
|
|
49
|
-
const platform = process.platform;
|
|
50
|
-
const home = process.env.HOME || "";
|
|
51
|
-
if (platform === "darwin") return [
|
|
52
|
-
"/Applications/Cursor.app/Contents/Resources/app/bin/cursor",
|
|
53
|
-
`${home}/Applications/Cursor.app/Contents/Resources/app/bin/cursor`,
|
|
54
|
-
`${home}/Downloads/Cursor.app/Contents/Resources/app/bin/cursor`
|
|
55
|
-
];
|
|
56
|
-
if (platform === "linux") return [
|
|
57
|
-
"/usr/share/cursor/bin/cursor",
|
|
58
|
-
"/usr/bin/cursor",
|
|
59
|
-
`${home}/.local/bin/cursor`,
|
|
60
|
-
"/opt/cursor/bin/cursor"
|
|
61
|
-
];
|
|
62
|
-
if (platform === "win32") {
|
|
63
|
-
const localAppData = process.env.LOCALAPPDATA || "";
|
|
64
|
-
const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
|
|
65
|
-
return [
|
|
66
|
-
`${localAppData}\\Programs\\Cursor\\resources\\app\\bin\\cursor.cmd`,
|
|
67
|
-
`${localAppData}\\cursor\\Cursor.exe`,
|
|
68
|
-
`${programFiles}\\Cursor\\resources\\app\\bin\\cursor.cmd`
|
|
69
|
-
];
|
|
70
|
-
}
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Finds the CLI command for an editor by checking known installation paths
|
|
75
|
-
* On macOS, also uses mdfind to locate the app if standard paths fail
|
|
76
|
-
* Falls back to the simple command name if no full path is found (in case it's in PATH)
|
|
77
|
-
*/
|
|
78
|
-
async function findEditorCommand(editor) {
|
|
79
|
-
let paths;
|
|
80
|
-
let fallbackCommand;
|
|
81
|
-
let bundleId = null;
|
|
82
|
-
if (editor === "VS Code") {
|
|
83
|
-
paths = getVSCodePaths();
|
|
84
|
-
fallbackCommand = "code";
|
|
85
|
-
bundleId = "com.microsoft.VSCode";
|
|
86
|
-
} else if (editor === "Cursor") {
|
|
87
|
-
paths = getCursorPaths();
|
|
88
|
-
fallbackCommand = "cursor";
|
|
89
|
-
bundleId = "com.todesktop.230313mzl4w4u92";
|
|
90
|
-
} else return null;
|
|
91
|
-
for (const path of paths) if (existsSync(path)) return path;
|
|
92
|
-
if (process.platform === "darwin" && bundleId) {
|
|
93
|
-
const appPath = await findAppWithMdfind(bundleId);
|
|
94
|
-
if (appPath) {
|
|
95
|
-
const cliPath = `${appPath}/Contents/Resources/app/bin/${fallbackCommand}`;
|
|
96
|
-
if (existsSync(cliPath)) return cliPath;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return fallbackCommand;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Checks if the extension is installed by querying the editor's extension list
|
|
103
|
-
*/
|
|
104
|
-
async function isExtensionInstalled(editor) {
|
|
105
|
-
const command = await findEditorCommand(editor);
|
|
106
|
-
if (!command) return false;
|
|
107
|
-
try {
|
|
108
|
-
return (await execa(command, ["--list-extensions"], { timeout: 5e3 })).stdout.includes(NEON_EXTENSION_ID);
|
|
109
|
-
} catch {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Installs the Neon Local Connect extension for VS Code or Cursor.
|
|
115
|
-
*
|
|
116
|
-
* Strategy:
|
|
117
|
-
* 1. Try `--install-extension <id>` directly (uses the editor's configured marketplace)
|
|
118
|
-
* 2. If that fails, download .vsix (from proxy or Open VSX) and install locally
|
|
119
|
-
* 3. Set NEON_VSX_GALLERY_URL to use a corporate proxy for the download
|
|
120
|
-
*/
|
|
121
|
-
async function installExtension(editor) {
|
|
122
|
-
const command = await findEditorCommand(editor);
|
|
123
|
-
if (!command) return false;
|
|
124
|
-
try {
|
|
125
|
-
await execa(command, ["--install-extension", NEON_EXTENSION_ID], {
|
|
126
|
-
stdio: "pipe",
|
|
127
|
-
timeout: 6e4
|
|
128
|
-
});
|
|
129
|
-
return true;
|
|
130
|
-
} catch {}
|
|
131
|
-
const vsixPath = await downloadVsix();
|
|
132
|
-
if (!vsixPath) return false;
|
|
133
|
-
try {
|
|
134
|
-
await execa(command, ["--install-extension", vsixPath], {
|
|
135
|
-
stdio: "pipe",
|
|
136
|
-
timeout: 6e4
|
|
137
|
-
});
|
|
138
|
-
return true;
|
|
139
|
-
} catch {
|
|
140
|
-
return false;
|
|
141
|
-
} finally {
|
|
142
|
-
try {
|
|
143
|
-
await unlink(vsixPath);
|
|
144
|
-
} catch {}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Returns the editor types that should use extension installation (vs MCP)
|
|
149
|
-
*/
|
|
150
|
-
function usesExtension(editor) {
|
|
151
|
-
return editor === "VS Code" || editor === "Cursor";
|
|
152
|
-
}
|
|
153
|
-
//#endregion
|
|
154
|
-
export { findEditorCommand, installExtension, isExtensionInstalled, usesExtension };
|