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.
Files changed (57) hide show
  1. package/README.md +48 -113
  2. package/dist/_chunks/{auth_selection-ktL6uoEe.js → auth_selection-pitMFzDe.js} +1 -1
  3. package/dist/_chunks/{credential_io-B1JxWELN.js → credential_io-YeAxg9Xn.js} +2 -2
  4. package/dist/_chunks/{paths-g3DRqJiD.js → paths-DMq0Lt7a.js} +1 -1
  5. package/dist/_chunks/{profiles-D95AeZ5v.js → profiles-CvnFEQyd.js} +1 -1
  6. package/dist/analytics.js +2 -2
  7. package/dist/auth_context.js +1 -1
  8. package/dist/commands/auth.js +12 -9
  9. package/dist/commands/bootstrap.js +72 -43
  10. package/dist/commands/claim.js +1 -1
  11. package/dist/commands/env.js +3 -20
  12. package/dist/commands/init.js +80 -79
  13. package/dist/commands/link.js +110 -292
  14. package/dist/commands/mcp.js +2 -2
  15. package/dist/commands/plugins.js +3 -3
  16. package/dist/commands/profile.js +4 -4
  17. package/dist/config.js +1 -1
  18. package/dist/credential_io.js +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/init/child.js +36 -0
  21. package/dist/init/plan.js +47 -0
  22. package/dist/init/tooling.js +43 -0
  23. package/dist/init/wizard.js +42 -0
  24. package/dist/mcp/targets.js +1 -1
  25. package/dist/plugins/plan.js +1 -1
  26. package/dist/plugins/targets.js +5 -1
  27. package/dist/retire_credential.js +1 -1
  28. package/dist/utils/cli_name.js +1 -12
  29. package/dist/utils/middlewares.js +1 -1
  30. package/dist/utils/package_manager.js +10 -5
  31. package/package.json +13 -14
  32. package/dist/init/auth.js +0 -53
  33. package/dist/init/build_config.js +0 -4
  34. package/dist/init/detect_agent.js +0 -63
  35. package/dist/init/editors.js +0 -19
  36. package/dist/init/enrich_output.js +0 -61
  37. package/dist/init/extension.js +0 -154
  38. package/dist/init/inspect.js +0 -249
  39. package/dist/init/interactive.js +0 -529
  40. package/dist/init/neonctl.js +0 -139
  41. package/dist/init/orchestrate.js +0 -189
  42. package/dist/init/phases/auth.js +0 -195
  43. package/dist/init/phases/cleanup.js +0 -27
  44. package/dist/init/phases/db.js +0 -257
  45. package/dist/init/phases/getting_started.js +0 -218
  46. package/dist/init/phases/mcp.js +0 -219
  47. package/dist/init/phases/migrations.js +0 -250
  48. package/dist/init/phases/neon_auth.js +0 -116
  49. package/dist/init/phases/setup.js +0 -582
  50. package/dist/init/phases/skills.js +0 -86
  51. package/dist/init/phases/status.js +0 -82
  52. package/dist/init/profile_cli.js +0 -56
  53. package/dist/init/resolve_context.js +0 -105
  54. package/dist/init/route_command.js +0 -93
  55. package/dist/init/skills.js +0 -206
  56. package/dist/init/types.js +0 -1
  57. package/dist/init/vsix.js +0 -89
@@ -1,82 +0,0 @@
1
- import { neonInitAgentCmd } from "../profile_cli.js";
2
- import { tryResolveAddMcpAgentId } from "../../mcp/agents.js";
3
- import { supportsSkills } from "../agents.js";
4
- import { isAuthenticated } from "../auth.js";
5
- import { inspectProject } from "../inspect.js";
6
- //#region src/init/phases/status.ts
7
- async function handleStatusPhase(options) {
8
- const authed = await isAuthenticated();
9
- const inspection = await inspectProject([
10
- {
11
- id: "mcp_server",
12
- description: "",
13
- lookFor: []
14
- },
15
- {
16
- id: "database_url",
17
- description: "",
18
- lookFor: []
19
- },
20
- {
21
- id: "skills",
22
- description: "",
23
- lookFor: []
24
- },
25
- {
26
- id: "migrations",
27
- description: "",
28
- lookFor: []
29
- }
30
- ]);
31
- const hasDatabaseUrl = inspection.databaseUrl === true;
32
- const mcpConfigured = inspection.mcpConfigured === true;
33
- const skillsInstalled = inspection.skillsInstalled === true;
34
- const migrationTool = inspection.migrationTool ?? null;
35
- const hasMigrations = migrationTool !== null && migrationTool !== "none" && inspection.migrationDir !== "none";
36
- const recommendations = [];
37
- if (!authed) recommendations.push({
38
- priority: "high",
39
- message: "Not authenticated with Neon",
40
- command: neonInitAgentCmd({ step: "auth" })
41
- });
42
- if (!hasDatabaseUrl) recommendations.push({
43
- priority: "high",
44
- message: "No DATABASE_URL found in .env",
45
- command: neonInitAgentCmd({ step: "db" })
46
- });
47
- const statusAgent = options.agent ? tryResolveAddMcpAgentId(options.agent) : void 0;
48
- if (!skillsInstalled && (!statusAgent || supportsSkills(statusAgent))) recommendations.push({
49
- priority: "medium",
50
- message: "Neon agent skills not detected in this project",
51
- command: neonInitAgentCmd({
52
- step: "skills",
53
- install: true
54
- })
55
- });
56
- if (migrationTool && !hasMigrations) recommendations.push({
57
- priority: "medium",
58
- message: `${migrationTool} detected but no migrations found`,
59
- command: neonInitAgentCmd({ step: "migrations" })
60
- });
61
- return {
62
- auth: { authenticated: authed },
63
- tooling: {
64
- mcpServer: {
65
- configured: mcpConfigured,
66
- scope: inspection.mcpScope || null
67
- },
68
- skills: {
69
- installed: skillsInstalled,
70
- scope: inspection.skillsScope || null
71
- }
72
- },
73
- project: { databaseUrl: hasDatabaseUrl },
74
- migrations: {
75
- tool: migrationTool,
76
- hasMigrations
77
- },
78
- recommendations
79
- };
80
- }
81
- //#endregion
82
- export { handleStatusPhase };
@@ -1,56 +0,0 @@
1
- import { n as configDir } from "../_chunks/paths-g3DRqJiD.js";
2
- import { t as DEFAULT_PROFILE } from "../_chunks/profiles-D95AeZ5v.js";
3
- import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-ktL6uoEe.js";
4
- //#region src/init/profile_cli.ts
5
- /**
6
- * API-key selections fall back to DEFAULT because init authenticates from stored credentials.
7
- */
8
- function selectedProfileName() {
9
- const selection = selectCredential(credentialInputs());
10
- return selection.source === "profile" ? selection.profile : DEFAULT_PROFILE;
11
- }
12
- /**
13
- * An explicit `--config-dir` must override `NEON_CONFIG_DIR` and the home path.
14
- */
15
- function selectedConfigDir() {
16
- const recorded = credentialInputs().configDir;
17
- return recorded !== "" ? recorded : configDir();
18
- }
19
- /**
20
- * Omitting implicit DEFAULT preserves command compatibility; retaining explicit DEFAULT
21
- * prevents ambient `NEON_PROFILE` from overriding it in subprocesses.
22
- */
23
- function explicitProfileArgs() {
24
- const selection = selectCredential(credentialInputs());
25
- if (selection.source === "profile" && selection.explicit) return ["--profile", selection.profile];
26
- return [];
27
- }
28
- function explicitProfileCli() {
29
- const args = explicitProfileArgs();
30
- return args.length === 0 ? "" : ` ${args.join(" ")}`;
31
- }
32
- function explicitConfigDirArgs() {
33
- const dir = credentialInputs().configDir;
34
- return dir !== "" ? ["--config-dir", dir] : [];
35
- }
36
- function posixSingleQuote(value) {
37
- return `'${value.replace(/'/g, `'\\''`)}'`;
38
- }
39
- function explicitConfigDirCli() {
40
- const dir = credentialInputs().configDir;
41
- return dir === "" ? "" : ` --config-dir ${posixSingleQuote(dir)}`;
42
- }
43
- function npxNeonArgs(command) {
44
- return [
45
- "-y",
46
- "neon",
47
- ...explicitProfileArgs(),
48
- ...explicitConfigDirArgs(),
49
- ...command
50
- ];
51
- }
52
- function neonInitAgentCmd(data) {
53
- return `neon init --agent${explicitProfileCli()}${explicitConfigDirCli()} --data '${JSON.stringify(data)}'`;
54
- }
55
- //#endregion
56
- export { explicitConfigDirCli, explicitProfileArgs, explicitProfileCli, neonInitAgentCmd, npxNeonArgs, selectedConfigDir, selectedProfileName };
@@ -1,105 +0,0 @@
1
- import { npxNeonArgs } from "./profile_cli.js";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { resolve } from "node:path";
4
- import { execa } from "execa";
5
- //#region src/init/resolve_context.ts
6
- /**
7
- * Resolves the Neon org/project from a DATABASE_URL in .env.
8
- * Parses the endpoint hostname, then queries the Neon API to find
9
- * which project it belongs to.
10
- */
11
- /**
12
- * Extracts the Neon endpoint host from .env's DATABASE_URL or PGHOST.
13
- * Returns the hostname (e.g. "ep-cool-name-12345.us-east-2.aws.neon.tech") or null.
14
- */
15
- function extractNeonHost(cwd) {
16
- for (const envFile of [".env", ".env.local"]) {
17
- const envPath = resolve(cwd, envFile);
18
- if (!existsSync(envPath)) continue;
19
- try {
20
- const content = readFileSync(envPath, "utf-8");
21
- const dbUrlMatch = content.match(/^DATABASE_URL=.*@([^/:\s]+\.neon\.tech)/m);
22
- if (dbUrlMatch) return dbUrlMatch[1];
23
- const pgHostMatch = content.match(/^PGHOST=([^\s]+\.neon\.tech)/m);
24
- if (pgHostMatch) return pgHostMatch[1];
25
- } catch {}
26
- }
27
- return null;
28
- }
29
- /**
30
- * Resolves the Neon org and project that own the endpoint in DATABASE_URL.
31
- *
32
- * Strategy:
33
- * 1. Extract endpoint hostname from .env
34
- * 2. List orgs via the Neon CLI
35
- * 3. For each org, list projects
36
- * 4. For each project, get connection string and compare hostnames
37
- * 5. Return the matching org/project
38
- */
39
- async function resolveNeonContext(cwd) {
40
- const neonHost = extractNeonHost(cwd);
41
- if (!neonHost) return null;
42
- let orgs;
43
- try {
44
- const result = await execa("npx", npxNeonArgs([
45
- "orgs",
46
- "list",
47
- "--output",
48
- "json"
49
- ]), {
50
- stdio: "pipe",
51
- timeout: 3e4,
52
- env: {
53
- ...process.env,
54
- CI: void 0
55
- }
56
- });
57
- orgs = JSON.parse(result.stdout);
58
- } catch {
59
- return null;
60
- }
61
- if (orgs.length === 0) return null;
62
- for (const org of orgs) {
63
- let projects;
64
- try {
65
- const result = await execa("npx", npxNeonArgs([
66
- "projects",
67
- "list",
68
- "--org-id",
69
- org.id,
70
- "--output",
71
- "json"
72
- ]), {
73
- stdio: "pipe",
74
- timeout: 3e4,
75
- env: {
76
- ...process.env,
77
- CI: void 0
78
- }
79
- });
80
- projects = JSON.parse(result.stdout);
81
- } catch {
82
- continue;
83
- }
84
- for (const project of projects) try {
85
- if ((await execa("npx", npxNeonArgs([
86
- "connection-string",
87
- "--project-id",
88
- project.id
89
- ]), {
90
- stdio: "pipe",
91
- timeout: 15e3,
92
- env: {
93
- ...process.env,
94
- CI: void 0
95
- }
96
- })).stdout.trim().includes(neonHost)) return {
97
- orgId: org.id,
98
- projectId: project.id
99
- };
100
- } catch {}
101
- }
102
- return null;
103
- }
104
- //#endregion
105
- export { resolveNeonContext };
@@ -1,93 +0,0 @@
1
- //#region src/init/route_command.ts
2
- /**
3
- * Routes a `--data` payload to a phase handler by its `step` field, without
4
- * shelling out. This is the whole agent-facing surface of `neon init`:
5
- *
6
- * neon init --agent --data '{"step":"auth"}'
7
- * neon init --agent --data '{"step":"db","projectId":"xyz"}'
8
- */
9
- async function routeDataStep(data, agent) {
10
- const { step, agentId, ...rest } = data;
11
- const resolvedAgent = typeof agentId === "string" ? agentId : agent;
12
- if (rest.data !== void 0 && Object.keys(rest).length === 1) {
13
- let nested = rest.data;
14
- if (typeof nested === "string") try {
15
- nested = JSON.parse(nested);
16
- } catch {}
17
- if (typeof nested === "object" && nested !== null) {
18
- Object.assign(rest, nested);
19
- delete rest.data;
20
- }
21
- }
22
- switch (step) {
23
- case "auth": {
24
- const { handleAuthPhase } = await import("./phases/auth.js");
25
- return handleAuthPhase({
26
- agent: resolvedAgent,
27
- ...rest
28
- });
29
- }
30
- case "db": {
31
- const { handleDbPhase } = await import("./phases/db.js");
32
- return handleDbPhase({
33
- agent: resolvedAgent,
34
- ...rest
35
- });
36
- }
37
- case "setup": {
38
- const { handleSetupPhase } = await import("./phases/setup.js");
39
- return handleSetupPhase({
40
- agent: resolvedAgent,
41
- ...rest
42
- });
43
- }
44
- case "getting-started": {
45
- const { handleGettingStartedPhase } = await import("./phases/getting_started.js");
46
- return handleGettingStartedPhase({
47
- agent: resolvedAgent,
48
- ...rest,
49
- cwd: process.cwd()
50
- });
51
- }
52
- case "mcp": {
53
- const { handleMcpPhase } = await import("./phases/mcp.js");
54
- return handleMcpPhase({
55
- agent: resolvedAgent,
56
- ...rest
57
- });
58
- }
59
- case "skills": {
60
- const { handleSkillsPhase } = await import("./phases/skills.js");
61
- return handleSkillsPhase({
62
- agent: resolvedAgent,
63
- ...rest
64
- });
65
- }
66
- case "migrations": {
67
- const { handleMigrationsPhase } = await import("./phases/migrations.js");
68
- return handleMigrationsPhase({
69
- agent: resolvedAgent,
70
- ...rest,
71
- cwd: process.cwd()
72
- });
73
- }
74
- case "neon-auth": {
75
- const { handleNeonAuthPhase } = await import("./phases/neon_auth.js");
76
- return handleNeonAuthPhase({
77
- agent: resolvedAgent,
78
- ...rest
79
- });
80
- }
81
- case "status": {
82
- const { handleStatusPhase } = await import("./phases/status.js");
83
- return handleStatusPhase({ agent: resolvedAgent });
84
- }
85
- case "finalize": {
86
- const { handleCleanup } = await import("./phases/cleanup.js");
87
- return handleCleanup();
88
- }
89
- default: throw new Error(`Unknown step: "${step}". Valid steps: auth, db, setup, getting-started, mcp, skills, migrations, neon-auth, status, finalize`);
90
- }
91
- }
92
- //#endregion
93
- export { routeDataStep };
@@ -1,206 +0,0 @@
1
- import { globalInstallCommand, resolveInvokingPackageManager } from "../utils/package_manager.js";
2
- import { getSkillsAgentName } from "./agents.js";
3
- import { existsSync, statSync } from "node:fs";
4
- import { resolve } from "node:path";
5
- import { log, spinner } from "@clack/prompts";
6
- import { dim } from "yoctocolors";
7
- import { execa } from "execa";
8
- //#region src/init/skills.ts
9
- /**
10
- * Ensures the `skills` CLI is globally installed so npx doesn't need
11
- * to download it (which can fail behind corporate proxies / sandboxes).
12
- */
13
- async function ensureSkillsCli() {
14
- try {
15
- await execa("skills", ["--version"], {
16
- stdio: "pipe",
17
- timeout: 5e3
18
- });
19
- } catch {
20
- const install = globalInstallCommand(resolveInvokingPackageManager(), "skills");
21
- if (!install) return;
22
- try {
23
- await execa(install.command, install.args, {
24
- stdio: "pipe",
25
- timeout: 6e4
26
- });
27
- } catch {}
28
- }
29
- }
30
- /** Base skills installed for all invocations */
31
- const BASE_SKILLS = ["neon", "neon-postgres"];
32
- /** Additional skills installed for preview (non-bootstrap) invocations */
33
- const PREVIEW_SKILLS = [
34
- "neon-object-storage",
35
- "neon-functions",
36
- "neon-ai-gateway"
37
- ];
38
- /** Returns the skill list based on whether preview mode is active */
39
- function getSkillList(preview) {
40
- return preview ? [...BASE_SKILLS, ...PREVIEW_SKILLS] : BASE_SKILLS;
41
- }
42
- const SKILL_BASE_URL = "https://neon.com/docs/ai/skills/neon-postgres/references";
43
- const SKILL_REFERENCE_URLS = {
44
- gettingStarted: `${SKILL_BASE_URL}/getting-started.md`,
45
- connectionMethods: `${SKILL_BASE_URL}/connection-methods.md`,
46
- neonAuth: `${SKILL_BASE_URL}/neon-auth.md`,
47
- serverlessDriver: `${SKILL_BASE_URL}/neon-serverless.md`,
48
- neonCli: `${SKILL_BASE_URL}/neon-cli.md`,
49
- devtools: `${SKILL_BASE_URL}/devtools.md`,
50
- branching: `${SKILL_BASE_URL}/branching.md`,
51
- neonJs: `${SKILL_BASE_URL}/neon-js.md`
52
- };
53
- /**
54
- * Installs Neon agent skills using Vercel's skills CLI.
55
- */
56
- async function installAgentSkills(selectedAgents, options) {
57
- const quiet = options?.json === true;
58
- const agentsWithSkills = selectedAgents.filter((id) => getSkillsAgentName(id) !== void 0);
59
- if (agentsWithSkills.length === 0) return true;
60
- const skillsSpinner = quiet ? null : spinner();
61
- skillsSpinner?.start("Installing agent skills for Neon in this project...");
62
- let anyFailed = false;
63
- await ensureSkillsCli();
64
- const skills = getSkillList(options?.preview);
65
- for (const agent of agentsWithSkills) {
66
- const agentName = getSkillsAgentName(agent);
67
- if (!agentName) continue;
68
- for (const skill of skills) try {
69
- await execa("skills", [
70
- "add",
71
- "neondatabase/agent-skills",
72
- "--skill",
73
- skill,
74
- "--agent",
75
- agentName,
76
- ...options?.scope === "global" ? ["-g"] : [],
77
- "-y"
78
- ], {
79
- stdio: "pipe",
80
- timeout: 12e4
81
- });
82
- } catch (error) {
83
- if (!quiet) log.error(`Failed to install skill ${skill} for ${agent}: ${error instanceof Error ? error.message : "Unknown error"}`);
84
- anyFailed = true;
85
- }
86
- }
87
- if (anyFailed) {
88
- skillsSpinner?.stop("Agent skills installation for this project completed with errors");
89
- if (!quiet) log.info("You can manually install skills by running: npx skills add neondatabase/agent-skills --skill neon --skill neon-postgres");
90
- return false;
91
- }
92
- skillsSpinner?.stop(dim("Agent skills installed ✓"));
93
- return true;
94
- }
95
- const SKILLS_FRESHNESS_MS = 432e5;
96
- /**
97
- * Agent skills directory paths by agent, keyed by scope.
98
- * These are the directories that `skills add` writes to.
99
- */
100
- const GLOBAL_SKILLS_DIRS = (() => {
101
- const home = process.env.HOME || process.env.USERPROFILE || "";
102
- const agentsDir = resolve(home, ".agents", "skills");
103
- return {
104
- cursor: [resolve(home, ".cursor", "skills"), agentsDir],
105
- "claude-code": [resolve(home, ".claude", "skills"), agentsDir],
106
- "github-copilot": [resolve(home, ".vscode", "skills"), agentsDir],
107
- codex: [resolve(home, ".codex", "skills"), agentsDir],
108
- cline: [resolve(home, ".cline", "skills"), agentsDir],
109
- grok: [resolve(home, ".grok", "skills"), agentsDir]
110
- };
111
- })();
112
- const PROJECT_SKILLS_DIRS = {
113
- cursor: [".cursor/skills", ".agents/skills"],
114
- "claude-code": [".claude/skills", ".agents/skills"],
115
- "github-copilot": [".vscode/skills", ".agents/skills"],
116
- codex: [".codex/skills", ".agents/skills"],
117
- cline: [".cline/skills", ".agents/skills"],
118
- grok: [".grok/skills", ".agents/skills"]
119
- };
120
- function skillDirsForAgent(skillsId, scope, cwd, home) {
121
- if (scope === "global") return [.../* @__PURE__ */ new Set([...GLOBAL_SKILLS_DIRS[skillsId] ?? [], resolve(home, ".agents", "skills")])];
122
- return [...new Set([...PROJECT_SKILLS_DIRS[skillsId] ?? [], ".agents/skills"].map((dir) => resolve(cwd, dir)))];
123
- }
124
- function missingSkillsForAgent(agent, options = {}) {
125
- const skillsId = getSkillsAgentName(agent);
126
- if (!skillsId) return [];
127
- const cwd = options.cwd ?? process.cwd();
128
- const home = process.env.HOME || process.env.USERPROFILE || "";
129
- const dirs = skillDirsForAgent(skillsId, options.scope ?? "project", cwd, home);
130
- return getSkillList(options.preview).filter((skill) => !dirs.some((dir) => existsSync(resolve(dir, skill, "SKILL.md"))));
131
- }
132
- function skillsInstalledForAgent(agent, cwd = process.cwd(), preview = false) {
133
- if (!getSkillsAgentName(agent)) return true;
134
- return missingSkillsForAgent(agent, {
135
- cwd,
136
- scope: "project",
137
- preview
138
- }).length === 0 || missingSkillsForAgent(agent, {
139
- cwd,
140
- scope: "global",
141
- preview
142
- }).length === 0;
143
- }
144
- /**
145
- * Checks whether skills were recently updated (within the freshness window).
146
- * Checks both project-level (skills-lock.json mtime) and global (skills dir mtime).
147
- */
148
- function skillsAreFresh(agent, requiredSkills) {
149
- const now = Date.now();
150
- const cwd = process.cwd();
151
- const skillsId = getSkillsAgentName(agent);
152
- const projectDirs = (skillsId ? PROJECT_SKILLS_DIRS[skillsId] : void 0)?.map((dir) => resolve(cwd, dir)) ?? [];
153
- const lockPath = resolve(cwd, "skills-lock.json");
154
- if (existsSync(lockPath) && projectDirs.length > 0) try {
155
- if (now - statSync(lockPath).mtimeMs < SKILLS_FRESHNESS_MS) {
156
- if (requiredSkills.every((skill) => projectDirs.some((dir) => existsSync(resolve(dir, skill, "SKILL.md"))))) return true;
157
- }
158
- } catch {}
159
- const agentName = getSkillsAgentName(agent);
160
- if (!agentName) return false;
161
- const globalDirs = GLOBAL_SKILLS_DIRS[agentName] ?? [];
162
- for (const dir of globalDirs) if (requiredSkills.every((skill) => existsSync(resolve(dir, skill, "SKILL.md")))) try {
163
- if (now - statSync(resolve(dir, requiredSkills[0], "SKILL.md")).mtimeMs < SKILLS_FRESHNESS_MS) return true;
164
- } catch {}
165
- return false;
166
- }
167
- /**
168
- * Ensures Neon agent skills are up to date. Runs `skills add` if the skills
169
- * haven't been updated within the freshness window (12 hours).
170
- *
171
- * Designed to be called from any phase handler — cheap to call repeatedly
172
- * since it's a no-op when skills are fresh.
173
- */
174
- async function ensureSkillsUpToDate(agent, scope, preview) {
175
- const resolvedAgent = agent || "cursor";
176
- const agentName = getSkillsAgentName(resolvedAgent);
177
- if (!agentName) return true;
178
- if (skillsAreFresh(resolvedAgent, getSkillList(preview))) return true;
179
- await ensureSkillsCli();
180
- let allOk = true;
181
- const missingSkills = missingSkillsForAgent(resolvedAgent, {
182
- scope: scope ?? "project",
183
- preview
184
- });
185
- if (missingSkills.length === 0) return true;
186
- for (const skill of missingSkills) try {
187
- await execa("skills", [
188
- "add",
189
- "neondatabase/agent-skills",
190
- "--skill",
191
- skill,
192
- "--agent",
193
- agentName,
194
- ...scope === "global" ? ["-g"] : [],
195
- "-y"
196
- ], {
197
- stdio: "pipe",
198
- timeout: 12e4
199
- });
200
- } catch {
201
- allOk = false;
202
- }
203
- return allOk;
204
- }
205
- //#endregion
206
- export { SKILL_REFERENCE_URLS, ensureSkillsUpToDate, getSkillList, installAgentSkills, missingSkillsForAgent, skillsInstalledForAgent };
@@ -1 +0,0 @@
1
- export {};
package/dist/init/vsix.js DELETED
@@ -1,89 +0,0 @@
1
- import "./build_config.js";
2
- import { createWriteStream } from "node:fs";
3
- import { join } from "node:path";
4
- import { tmpdir } from "node:os";
5
- import { pipeline } from "node:stream/promises";
6
- //#region src/init/vsix.ts
7
- /**
8
- * Shared VSIX download utilities for extension installation.
9
- * Supports corporate proxy via NEON_VSX_GALLERY_URL env var.
10
- */
11
- const NEON_EXTENSION_ID = "databricks.neon-local-connect";
12
- const OPEN_VSX_API = "https://open-vsx.org/api";
13
- /**
14
- * Downloads a .vsix file for the Neon extension.
15
- *
16
- * Strategy:
17
- * 1. If NEON_VSX_GALLERY_URL is set, download from the corporate proxy gallery
18
- * 2. Otherwise, download from the public Open VSX API
19
- *
20
- * Returns the path to the temp .vsix file, or null on failure.
21
- */
22
- async function downloadVsix() {
23
- const proxyGallery = process.env.NEON_VSX_GALLERY_URL || "";
24
- if (proxyGallery) return downloadFromGallery(proxyGallery);
25
- return downloadFromOpenVsx();
26
- }
27
- /**
28
- * Downloads from a VS Code marketplace-compatible gallery API (corporate proxy).
29
- * Uses the VS Code extensionquery POST API to find the VSIX download URL.
30
- */
31
- async function downloadFromGallery(galleryUrl) {
32
- const [publisher, name] = NEON_EXTENSION_ID.split(".");
33
- const queryUrl = `${galleryUrl.replace(/\/+$/, "")}/extensionquery`;
34
- try {
35
- const queryRes = await fetch(queryUrl, {
36
- method: "POST",
37
- headers: {
38
- "Content-Type": "application/json",
39
- Accept: "application/json;api-version=6.1-preview.1"
40
- },
41
- body: JSON.stringify({
42
- filters: [{ criteria: [{
43
- filterType: 7,
44
- value: `${publisher}.${name}`
45
- }] }],
46
- flags: 914
47
- }),
48
- signal: AbortSignal.timeout(15e3)
49
- });
50
- if (!queryRes.ok) return null;
51
- const vsixFile = (((await queryRes.json()).results?.[0]?.extensions?.[0])?.versions?.[0])?.files?.find((f) => f.assetType === "Microsoft.VisualStudio.Services.VSIXPackage");
52
- if (!vsixFile?.source) return null;
53
- const vsixRes = await fetch(vsixFile.source, {
54
- signal: AbortSignal.timeout(3e4),
55
- redirect: "follow"
56
- });
57
- if (!vsixRes.ok || !vsixRes.body) return null;
58
- const tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-proxy.vsix`);
59
- const fileStream = createWriteStream(tmpPath);
60
- await pipeline(vsixRes.body, fileStream);
61
- return tmpPath;
62
- } catch {
63
- return null;
64
- }
65
- }
66
- /**
67
- * Downloads from the public Open VSX API.
68
- */
69
- async function downloadFromOpenVsx() {
70
- const [publisher, name] = NEON_EXTENSION_ID.split(".");
71
- const metaUrl = `${OPEN_VSX_API}/${publisher}/${name}/latest`;
72
- try {
73
- const metaRes = await fetch(metaUrl, { signal: AbortSignal.timeout(1e4) });
74
- if (!metaRes.ok) return null;
75
- const meta = await metaRes.json();
76
- const downloadUrl = meta.files?.download;
77
- if (!downloadUrl) return null;
78
- const vsixRes = await fetch(downloadUrl, { signal: AbortSignal.timeout(3e4) });
79
- if (!vsixRes.ok || !vsixRes.body) return null;
80
- const tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-${meta.version ?? "latest"}.vsix`);
81
- const fileStream = createWriteStream(tmpPath);
82
- await pipeline(vsixRes.body, fileStream);
83
- return tmpPath;
84
- } catch {
85
- return null;
86
- }
87
- }
88
- //#endregion
89
- export { NEON_EXTENSION_ID, downloadVsix };