neon-init 0.13.1 → 0.15.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 (117) hide show
  1. package/dist/cli.js +368 -33
  2. package/dist/cli.js.map +1 -1
  3. package/dist/index.d.ts +15 -3
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +220 -41
  6. package/dist/index.js.map +1 -1
  7. package/dist/interactive.d.ts +12 -0
  8. package/dist/interactive.d.ts.map +1 -0
  9. package/dist/interactive.js +495 -0
  10. package/dist/interactive.js.map +1 -0
  11. package/dist/lib/agents.d.ts +23 -0
  12. package/dist/lib/agents.d.ts.map +1 -0
  13. package/dist/lib/agents.js +148 -0
  14. package/dist/lib/agents.js.map +1 -0
  15. package/dist/lib/auth.d.ts +10 -3
  16. package/dist/lib/auth.d.ts.map +1 -1
  17. package/dist/lib/auth.js +20 -13
  18. package/dist/lib/auth.js.map +1 -1
  19. package/dist/lib/bootstrap.d.ts +30 -0
  20. package/dist/lib/bootstrap.d.ts.map +1 -0
  21. package/dist/lib/bootstrap.js +61 -0
  22. package/dist/lib/bootstrap.js.map +1 -0
  23. package/dist/lib/build-config.d.ts +5 -0
  24. package/dist/lib/build-config.d.ts.map +1 -0
  25. package/dist/lib/build-config.js +6 -0
  26. package/dist/lib/build-config.js.map +1 -0
  27. package/dist/lib/detect-agent.d.ts +22 -0
  28. package/dist/lib/detect-agent.d.ts.map +1 -0
  29. package/dist/lib/detect-agent.js +65 -0
  30. package/dist/lib/detect-agent.js.map +1 -0
  31. package/dist/lib/editors.d.ts.map +1 -1
  32. package/dist/lib/editors.js +1 -2
  33. package/dist/lib/editors.js.map +1 -1
  34. package/dist/lib/extension.d.ts +11 -3
  35. package/dist/lib/extension.d.ts.map +1 -1
  36. package/dist/lib/extension.js +29 -9
  37. package/dist/lib/extension.js.map +1 -1
  38. package/dist/lib/inspect.d.ts +28 -0
  39. package/dist/lib/inspect.d.ts.map +1 -0
  40. package/dist/lib/inspect.js +190 -0
  41. package/dist/lib/inspect.js.map +1 -0
  42. package/dist/lib/install.d.ts +10 -4
  43. package/dist/lib/install.d.ts.map +1 -1
  44. package/dist/lib/install.js +74 -71
  45. package/dist/lib/install.js.map +1 -1
  46. package/dist/lib/neonctl.d.ts +32 -0
  47. package/dist/lib/neonctl.d.ts.map +1 -0
  48. package/dist/lib/neonctl.js +149 -0
  49. package/dist/lib/neonctl.js.map +1 -0
  50. package/dist/lib/phases/auth.d.ts +12 -0
  51. package/dist/lib/phases/auth.d.ts.map +1 -0
  52. package/dist/lib/phases/auth.js +188 -0
  53. package/dist/lib/phases/auth.js.map +1 -0
  54. package/dist/lib/phases/cleanup.d.ts +12 -0
  55. package/dist/lib/phases/cleanup.d.ts.map +1 -0
  56. package/dist/lib/phases/cleanup.js +29 -0
  57. package/dist/lib/phases/cleanup.js.map +1 -0
  58. package/dist/lib/phases/db.d.ts +17 -0
  59. package/dist/lib/phases/db.d.ts.map +1 -0
  60. package/dist/lib/phases/db.js +258 -0
  61. package/dist/lib/phases/db.js.map +1 -0
  62. package/dist/lib/phases/getting-started.d.ts +26 -0
  63. package/dist/lib/phases/getting-started.d.ts.map +1 -0
  64. package/dist/lib/phases/getting-started.js +195 -0
  65. package/dist/lib/phases/getting-started.js.map +1 -0
  66. package/dist/lib/phases/mcp.d.ts +15 -0
  67. package/dist/lib/phases/mcp.d.ts.map +1 -0
  68. package/dist/lib/phases/mcp.js +179 -0
  69. package/dist/lib/phases/mcp.js.map +1 -0
  70. package/dist/lib/phases/migrations.d.ts +14 -0
  71. package/dist/lib/phases/migrations.d.ts.map +1 -0
  72. package/dist/lib/phases/migrations.js +239 -0
  73. package/dist/lib/phases/migrations.js.map +1 -0
  74. package/dist/lib/phases/neon-auth.d.ts +13 -0
  75. package/dist/lib/phases/neon-auth.d.ts.map +1 -0
  76. package/dist/lib/phases/neon-auth.js +117 -0
  77. package/dist/lib/phases/neon-auth.js.map +1 -0
  78. package/dist/lib/phases/setup.d.ts +41 -0
  79. package/dist/lib/phases/setup.d.ts.map +1 -0
  80. package/dist/lib/phases/setup.js +689 -0
  81. package/dist/lib/phases/setup.js.map +1 -0
  82. package/dist/lib/phases/skills.d.ts +14 -0
  83. package/dist/lib/phases/skills.d.ts.map +1 -0
  84. package/dist/lib/phases/skills.js +80 -0
  85. package/dist/lib/phases/skills.js.map +1 -0
  86. package/dist/lib/phases/status.d.ts +10 -0
  87. package/dist/lib/phases/status.d.ts.map +1 -0
  88. package/dist/lib/phases/status.js +65 -0
  89. package/dist/lib/phases/status.js.map +1 -0
  90. package/dist/lib/resolve-context.d.ts +19 -0
  91. package/dist/lib/resolve-context.d.ts.map +1 -0
  92. package/dist/lib/resolve-context.js +112 -0
  93. package/dist/lib/resolve-context.js.map +1 -0
  94. package/dist/lib/route-command.d.ts +8 -0
  95. package/dist/lib/route-command.d.ts.map +1 -0
  96. package/dist/lib/route-command.js +195 -0
  97. package/dist/lib/route-command.js.map +1 -0
  98. package/dist/lib/skills.d.ts +21 -4
  99. package/dist/lib/skills.d.ts.map +1 -1
  100. package/dist/lib/skills.js +129 -22
  101. package/dist/lib/skills.js.map +1 -1
  102. package/dist/lib/types.d.ts +146 -13
  103. package/dist/lib/types.d.ts.map +1 -1
  104. package/dist/lib/types.js +1 -1
  105. package/dist/lib/vsix.d.ts +15 -0
  106. package/dist/lib/vsix.d.ts.map +1 -0
  107. package/dist/lib/vsix.js +91 -0
  108. package/dist/lib/vsix.js.map +1 -0
  109. package/dist/v2.d.ts +31 -0
  110. package/dist/v2.d.ts.map +1 -0
  111. package/dist/v2.js +147 -0
  112. package/dist/v2.js.map +1 -0
  113. package/package.json +9 -4
  114. package/dist/lib/mcp-config.d.ts +0 -24
  115. package/dist/lib/mcp-config.d.ts.map +0 -1
  116. package/dist/lib/mcp-config.js +0 -51
  117. package/dist/lib/mcp-config.js.map +0 -1
@@ -0,0 +1,148 @@
1
+ //#region src/lib/agents.ts
2
+ /**
3
+ * All agents that can be configured via neon-init.
4
+ * Aligns with add-mcp's supported agents table.
5
+ * https://github.com/neondatabase/add-mcp#supported-agents
6
+ */
7
+ const ALL_CONFIGURABLE_AGENTS = [
8
+ {
9
+ editor: "Cursor",
10
+ addMcpId: "cursor",
11
+ hint: "Neon Local Connect extension"
12
+ },
13
+ {
14
+ editor: "VS Code",
15
+ addMcpId: "vscode",
16
+ hint: "Neon Local Connect extension"
17
+ },
18
+ {
19
+ editor: "Claude CLI",
20
+ addMcpId: "claude-code",
21
+ hint: "MCP Server"
22
+ },
23
+ {
24
+ editor: "Claude Desktop",
25
+ addMcpId: "claude-desktop",
26
+ hint: "MCP Server"
27
+ },
28
+ {
29
+ editor: "Codex",
30
+ addMcpId: "codex",
31
+ hint: "MCP Server"
32
+ },
33
+ {
34
+ editor: "OpenCode",
35
+ addMcpId: "opencode",
36
+ hint: "MCP Server"
37
+ },
38
+ {
39
+ editor: "Antigravity",
40
+ addMcpId: "antigravity",
41
+ hint: "MCP Server"
42
+ },
43
+ {
44
+ editor: "Cline",
45
+ addMcpId: "cline",
46
+ hint: "MCP Server"
47
+ },
48
+ {
49
+ editor: "Cline CLI",
50
+ addMcpId: "cline-cli",
51
+ hint: "MCP Server"
52
+ },
53
+ {
54
+ editor: "Gemini CLI",
55
+ addMcpId: "gemini-cli",
56
+ hint: "MCP Server"
57
+ },
58
+ {
59
+ editor: "GitHub Copilot CLI",
60
+ addMcpId: "github-copilot-cli",
61
+ hint: "MCP Server"
62
+ },
63
+ {
64
+ editor: "Goose",
65
+ addMcpId: "goose",
66
+ hint: "MCP Server"
67
+ },
68
+ {
69
+ editor: "MCPorter",
70
+ addMcpId: "mcporter",
71
+ hint: "MCP Server"
72
+ },
73
+ {
74
+ editor: "Zed",
75
+ addMcpId: "zed",
76
+ hint: "MCP Server"
77
+ }
78
+ ];
79
+ function getAddMcpAgentId(editor) {
80
+ const agent = ALL_CONFIGURABLE_AGENTS.find((a) => a.editor === editor);
81
+ if (!agent) throw new Error(`No add-mcp agent ID found for editor: ${editor}`);
82
+ return agent.addMcpId;
83
+ }
84
+ /**
85
+ * Maps a raw agent identifier (as reported by agents or passed via --agent)
86
+ * to the add-mcp compatible agent ID.
87
+ *
88
+ * This handles aliases like "copilot" → "vscode", "claude" → "claude-code", etc.
89
+ */
90
+ const AGENT_ALIAS_TO_MCP_ID = {
91
+ cursor: "cursor",
92
+ copilot: "vscode",
93
+ "github-copilot": "vscode",
94
+ "vs-code": "vscode",
95
+ vscode: "vscode",
96
+ claude: "claude-code",
97
+ "claude-code": "claude-code",
98
+ "claude-desktop": "claude-desktop",
99
+ codex: "codex",
100
+ opencode: "opencode",
101
+ antigravity: "antigravity",
102
+ cline: "cline",
103
+ "cline-cli": "cline-cli",
104
+ "gemini-cli": "gemini-cli",
105
+ gemini: "gemini-cli",
106
+ goose: "goose",
107
+ windsurf: "windsurf",
108
+ "github-copilot-cli": "github-copilot-cli",
109
+ mcporter: "mcporter",
110
+ zed: "zed"
111
+ };
112
+ function resolveAddMcpAgentId(rawAgent) {
113
+ const resolved = AGENT_ALIAS_TO_MCP_ID[rawAgent.toLowerCase()];
114
+ if (!resolved) throw new Error(`Unknown agent: "${rawAgent}". Supported agents: ${Object.keys(AGENT_ALIAS_TO_MCP_ID).join(", ")}`);
115
+ return resolved;
116
+ }
117
+ /**
118
+ * Maps a raw agent identifier to the skills CLI agent name.
119
+ */
120
+ function getSkillsAgentName(agent) {
121
+ switch (agent.toLowerCase()) {
122
+ case "cursor": return "cursor";
123
+ case "copilot":
124
+ case "vscode":
125
+ case "vs-code":
126
+ case "github-copilot": return "github-copilot";
127
+ case "claude":
128
+ case "claude-code": return "claude-code";
129
+ case "codex": return "codex";
130
+ case "opencode": return "opencode";
131
+ case "antigravity": return "antigravity";
132
+ case "cline": return "cline";
133
+ case "gemini-cli": return "gemini-cli";
134
+ case "goose": return "goose";
135
+ case "claude-desktop": return "claude-code";
136
+ case "cline-cli": return "cline";
137
+ case "gemini": return "gemini-cli";
138
+ case "windsurf": return "windsurf";
139
+ case "github-copilot-cli": return "github-copilot";
140
+ case "mcporter": return "mcporter";
141
+ case "zed": return "zed";
142
+ default: throw new Error(`Unknown agent for skills: "${agent}". Use a known agent identifier.`);
143
+ }
144
+ }
145
+ //#endregion
146
+ export { ALL_CONFIGURABLE_AGENTS, getAddMcpAgentId, getSkillsAgentName, resolveAddMcpAgentId };
147
+
148
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","names":[],"sources":["../../src/lib/agents.ts"],"sourcesContent":["import type { Editor } from \"./types.js\";\n\nexport interface AgentConfig {\n\teditor: Editor;\n\taddMcpId: string;\n\thint: string;\n}\n\n/**\n * All agents that can be configured via neon-init.\n * Aligns with add-mcp's supported agents table.\n * https://github.com/neondatabase/add-mcp#supported-agents\n */\nexport const ALL_CONFIGURABLE_AGENTS: AgentConfig[] = [\n\t{\n\t\teditor: \"Cursor\",\n\t\taddMcpId: \"cursor\",\n\t\thint: \"Neon Local Connect extension\",\n\t},\n\t{\n\t\teditor: \"VS Code\",\n\t\taddMcpId: \"vscode\",\n\t\thint: \"Neon Local Connect extension\",\n\t},\n\t{ editor: \"Claude CLI\", addMcpId: \"claude-code\", hint: \"MCP Server\" },\n\t{\n\t\teditor: \"Claude Desktop\",\n\t\taddMcpId: \"claude-desktop\",\n\t\thint: \"MCP Server\",\n\t},\n\t{ editor: \"Codex\", addMcpId: \"codex\", hint: \"MCP Server\" },\n\t{ editor: \"OpenCode\", addMcpId: \"opencode\", hint: \"MCP Server\" },\n\t{ editor: \"Antigravity\", addMcpId: \"antigravity\", hint: \"MCP Server\" },\n\t{ editor: \"Cline\", addMcpId: \"cline\", hint: \"MCP Server\" },\n\t{ editor: \"Cline CLI\", addMcpId: \"cline-cli\", hint: \"MCP Server\" },\n\t{ editor: \"Gemini CLI\", addMcpId: \"gemini-cli\", hint: \"MCP Server\" },\n\t{\n\t\teditor: \"GitHub Copilot CLI\",\n\t\taddMcpId: \"github-copilot-cli\",\n\t\thint: \"MCP Server\",\n\t},\n\t{ editor: \"Goose\", addMcpId: \"goose\", hint: \"MCP Server\" },\n\t{ editor: \"MCPorter\", addMcpId: \"mcporter\", hint: \"MCP Server\" },\n\t{ editor: \"Zed\", addMcpId: \"zed\", hint: \"MCP Server\" },\n];\n\nexport function getAddMcpAgentId(editor: Editor): string {\n\tconst agent = ALL_CONFIGURABLE_AGENTS.find((a) => a.editor === editor);\n\tif (!agent) {\n\t\tthrow new Error(`No add-mcp agent ID found for editor: ${editor}`);\n\t}\n\treturn agent.addMcpId;\n}\n\n/**\n * Maps a raw agent identifier (as reported by agents or passed via --agent)\n * to the add-mcp compatible agent ID.\n *\n * This handles aliases like \"copilot\" → \"vscode\", \"claude\" → \"claude-code\", etc.\n */\nconst AGENT_ALIAS_TO_MCP_ID: Record<string, string> = {\n\tcursor: \"cursor\",\n\tcopilot: \"vscode\",\n\t\"github-copilot\": \"vscode\",\n\t\"vs-code\": \"vscode\",\n\tvscode: \"vscode\",\n\tclaude: \"claude-code\",\n\t\"claude-code\": \"claude-code\",\n\t\"claude-desktop\": \"claude-desktop\",\n\tcodex: \"codex\",\n\topencode: \"opencode\",\n\tantigravity: \"antigravity\",\n\tcline: \"cline\",\n\t\"cline-cli\": \"cline-cli\",\n\t\"gemini-cli\": \"gemini-cli\",\n\tgemini: \"gemini-cli\",\n\tgoose: \"goose\",\n\twindsurf: \"windsurf\",\n\t\"github-copilot-cli\": \"github-copilot-cli\",\n\tmcporter: \"mcporter\",\n\tzed: \"zed\",\n};\n\nexport function resolveAddMcpAgentId(rawAgent: string): string {\n\tconst resolved = AGENT_ALIAS_TO_MCP_ID[rawAgent.toLowerCase()];\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t`Unknown agent: \"${rawAgent}\". Supported agents: ${Object.keys(AGENT_ALIAS_TO_MCP_ID).join(\", \")}`,\n\t\t);\n\t}\n\treturn resolved;\n}\n\n/**\n * Maps a raw agent identifier to the skills CLI agent name.\n */\nexport function getSkillsAgentName(agent: string): string {\n\tswitch (agent.toLowerCase()) {\n\t\tcase \"cursor\":\n\t\t\treturn \"cursor\";\n\t\tcase \"copilot\":\n\t\tcase \"vscode\":\n\t\tcase \"vs-code\":\n\t\tcase \"github-copilot\":\n\t\t\treturn \"github-copilot\";\n\t\tcase \"claude\":\n\t\tcase \"claude-code\":\n\t\t\treturn \"claude-code\";\n\t\tcase \"codex\":\n\t\t\treturn \"codex\";\n\t\tcase \"opencode\":\n\t\t\treturn \"opencode\";\n\t\tcase \"antigravity\":\n\t\t\treturn \"antigravity\";\n\t\tcase \"cline\":\n\t\t\treturn \"cline\";\n\t\tcase \"gemini-cli\":\n\t\t\treturn \"gemini-cli\";\n\t\tcase \"goose\":\n\t\t\treturn \"goose\";\n\t\tcase \"claude-desktop\":\n\t\t\treturn \"claude-code\";\n\t\tcase \"cline-cli\":\n\t\t\treturn \"cline\";\n\t\tcase \"gemini\":\n\t\t\treturn \"gemini-cli\";\n\t\tcase \"windsurf\":\n\t\t\treturn \"windsurf\";\n\t\tcase \"github-copilot-cli\":\n\t\t\treturn \"github-copilot\";\n\t\tcase \"mcporter\":\n\t\t\treturn \"mcporter\";\n\t\tcase \"zed\":\n\t\t\treturn \"zed\";\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown agent for skills: \"${agent}\". Use a known agent identifier.`,\n\t\t\t);\n\t}\n}\n"],"mappings":";;;;;;AAaA,MAAa,0BAAyC;CACrD;EACC,QAAQ;EACR,UAAU;EACV,MAAM;CACP;CACA;EACC,QAAQ;EACR,UAAU;EACV,MAAM;CACP;CACA;EAAE,QAAQ;EAAc,UAAU;EAAe,MAAM;CAAa;CACpE;EACC,QAAQ;EACR,UAAU;EACV,MAAM;CACP;CACA;EAAE,QAAQ;EAAS,UAAU;EAAS,MAAM;CAAa;CACzD;EAAE,QAAQ;EAAY,UAAU;EAAY,MAAM;CAAa;CAC/D;EAAE,QAAQ;EAAe,UAAU;EAAe,MAAM;CAAa;CACrE;EAAE,QAAQ;EAAS,UAAU;EAAS,MAAM;CAAa;CACzD;EAAE,QAAQ;EAAa,UAAU;EAAa,MAAM;CAAa;CACjE;EAAE,QAAQ;EAAc,UAAU;EAAc,MAAM;CAAa;CACnE;EACC,QAAQ;EACR,UAAU;EACV,MAAM;CACP;CACA;EAAE,QAAQ;EAAS,UAAU;EAAS,MAAM;CAAa;CACzD;EAAE,QAAQ;EAAY,UAAU;EAAY,MAAM;CAAa;CAC/D;EAAE,QAAQ;EAAO,UAAU;EAAO,MAAM;CAAa;AACtD;AAEA,SAAgB,iBAAiB,QAAwB;CACxD,MAAM,QAAQ,wBAAwB,MAAM,MAAM,EAAE,WAAW,MAAM;CACrE,IAAI,CAAC,OACJ,MAAM,IAAI,MAAM,yCAAyC,QAAQ;CAElE,OAAO,MAAM;AACd;;;;;;;AAQA,MAAM,wBAAgD;CACrD,QAAQ;CACR,SAAS;CACT,kBAAkB;CAClB,WAAW;CACX,QAAQ;CACR,QAAQ;CACR,eAAe;CACf,kBAAkB;CAClB,OAAO;CACP,UAAU;CACV,aAAa;CACb,OAAO;CACP,aAAa;CACb,cAAc;CACd,QAAQ;CACR,OAAO;CACP,UAAU;CACV,sBAAsB;CACtB,UAAU;CACV,KAAK;AACN;AAEA,SAAgB,qBAAqB,UAA0B;CAC9D,MAAM,WAAW,sBAAsB,SAAS,YAAY;CAC5D,IAAI,CAAC,UACJ,MAAM,IAAI,MACT,mBAAmB,SAAS,uBAAuB,OAAO,KAAK,qBAAqB,CAAC,CAAC,KAAK,IAAI,GAChG;CAED,OAAO;AACR;;;;AAKA,SAAgB,mBAAmB,OAAuB;CACzD,QAAQ,MAAM,YAAY,GAA1B;EACC,KAAK,UACJ,OAAO;EACR,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,kBACJ,OAAO;EACR,KAAK;EACL,KAAK,eACJ,OAAO;EACR,KAAK,SACJ,OAAO;EACR,KAAK,YACJ,OAAO;EACR,KAAK,eACJ,OAAO;EACR,KAAK,SACJ,OAAO;EACR,KAAK,cACJ,OAAO;EACR,KAAK,SACJ,OAAO;EACR,KAAK,kBACJ,OAAO;EACR,KAAK,aACJ,OAAO;EACR,KAAK,UACJ,OAAO;EACR,KAAK,YACJ,OAAO;EACR,KAAK,sBACJ,OAAO;EACR,KAAK,YACJ,OAAO;EACR,KAAK,OACJ,OAAO;EACR,SACC,MAAM,IAAI,MACT,8BAA8B,MAAM,iCACrC;CACF;AACD"}
@@ -1,13 +1,20 @@
1
1
  //#region src/lib/auth.d.ts
2
+ interface AuthOptions {
3
+ json?: boolean;
4
+ }
2
5
  /**
3
6
  * Ensures neonctl is authenticated by running a command that triggers auth if needed
4
7
  * This will automatically start the OAuth flow if the user isn't already authenticated
5
8
  */
6
- declare function ensureNeonctlAuth(): Promise<boolean>;
9
+ declare function ensureNeonctlAuth(options?: AuthOptions): Promise<boolean>;
10
+ /**
11
+ * Checks whether neonctl has stored OAuth credentials.
12
+ */
13
+ declare function isAuthenticated(): Promise<boolean>;
7
14
  /**
8
15
  * Creates an API key using the Neon API with the OAuth token from neonctl
9
16
  */
10
- declare function createApiKeyFromNeonctl(): Promise<string | null>;
17
+ declare function createApiKeyFromNeonctl(options?: AuthOptions): Promise<string | null>;
11
18
  //#endregion
12
- export { createApiKeyFromNeonctl, ensureNeonctlAuth };
19
+ export { AuthOptions, createApiKeyFromNeonctl, ensureNeonctlAuth, isAuthenticated };
13
20
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":[],"mappings":";;AASA;AAqDA;;iBArDsB,iBAAA,CAAA,GAAqB;;;;iBAqDrB,uBAAA,CAAA,GAA2B"}
1
+ {"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/lib/auth.ts"],"mappings":";UAKiB,WAAA;EAAA,IAAA,CAAA,EAAA,OAAW;AAQ5B;;;;AAEU;AAkCY,iBApCA,iBAAA,CAoC0B,OAAA,CAAA,EAnCrC,WAmCqC,CAAA,EAlC7C,OAkC6C,CAAA,OAAA,CAAA;AA+BhD;;;AAEG,iBAjCmB,eAAA,CAAA,CAiCnB,EAjCsC,OAiCtC,CAAA,OAAA,CAAA;AAAO;;;iBAFY,uBAAA,WACX,cACR"}
package/dist/lib/auth.js CHANGED
@@ -1,21 +1,20 @@
1
- import { log } from "@clack/prompts";
2
1
  import { existsSync, readFileSync } from "node:fs";
3
2
  import { resolve } from "node:path";
3
+ import { log } from "@clack/prompts";
4
4
  import { execa } from "execa";
5
-
6
5
  //#region src/lib/auth.ts
7
6
  /**
8
7
  * Ensures neonctl is authenticated by running a command that triggers auth if needed
9
8
  * This will automatically start the OAuth flow if the user isn't already authenticated
10
9
  */
11
- async function ensureNeonctlAuth() {
10
+ async function ensureNeonctlAuth(options) {
11
+ const quiet = options?.json === true;
12
12
  if (await getNeonctlAccessToken()) return true;
13
13
  try {
14
14
  await execa("npx", [
15
15
  "-y",
16
16
  "neonctl",
17
- "me",
18
- "--no-analytics"
17
+ "me"
19
18
  ], {
20
19
  stdio: "inherit",
21
20
  env: {
@@ -26,12 +25,18 @@ async function ensureNeonctlAuth() {
26
25
  return true;
27
26
  } catch (error) {
28
27
  const msg = error instanceof Error ? error.message : "Unknown error";
29
- 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.");
28
+ if (!quiet) 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.");
30
29
  else log.error(`Authentication failed: ${msg}`);
31
30
  return false;
32
31
  }
33
32
  }
34
33
  /**
34
+ * Checks whether neonctl has stored OAuth credentials.
35
+ */
36
+ async function isAuthenticated() {
37
+ return await getNeonctlAccessToken() !== null;
38
+ }
39
+ /**
35
40
  * Gets the OAuth access token from neonctl's stored credentials
36
41
  */
37
42
  async function getNeonctlAccessToken() {
@@ -48,11 +53,12 @@ async function getNeonctlAccessToken() {
48
53
  /**
49
54
  * Creates an API key using the Neon API with the OAuth token from neonctl
50
55
  */
51
- async function createApiKeyFromNeonctl() {
56
+ async function createApiKeyFromNeonctl(options) {
57
+ const quiet = options?.json === true;
52
58
  try {
53
59
  const accessToken = await getNeonctlAccessToken();
54
60
  if (!accessToken) {
55
- log.error("Could not find OAuth token from neonctl");
61
+ if (!quiet) log.error("Could not find OAuth token from neonctl");
56
62
  return null;
57
63
  }
58
64
  const keyName = `neonctl-init-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5)}`;
@@ -62,20 +68,21 @@ async function createApiKeyFromNeonctl() {
62
68
  Authorization: `Bearer ${accessToken}`,
63
69
  "Content-Type": "application/json"
64
70
  },
65
- body: JSON.stringify({ key_name: keyName })
71
+ body: JSON.stringify({ key_name: keyName }),
72
+ signal: AbortSignal.timeout(3e4)
66
73
  });
67
74
  if (!response.ok) {
68
75
  const errorText = await response.text();
69
- log.error(`Failed to create API key: ${response.status} ${errorText}`);
76
+ if (!quiet) log.error(`Failed to create API key: ${response.status} ${errorText}`);
70
77
  return null;
71
78
  }
72
79
  return (await response.json()).key || null;
73
80
  } catch (error) {
74
- log.error(`Failed to create API key: ${error instanceof Error ? error.message : "Unknown error"}`);
81
+ if (!quiet) log.error(`Failed to create API key: ${error instanceof Error ? error.message : "Unknown error"}`);
75
82
  return null;
76
83
  }
77
84
  }
78
-
79
85
  //#endregion
80
- export { createApiKeyFromNeonctl, ensureNeonctlAuth };
86
+ export { createApiKeyFromNeonctl, ensureNeonctlAuth, isAuthenticated };
87
+
81
88
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { log } from \"@clack/prompts\";\nimport { execa } from \"execa\";\n\n/**\n * Ensures neonctl is authenticated by running a command that triggers auth if needed\n * This will automatically start the OAuth flow if the user isn't already authenticated\n */\nexport async function ensureNeonctlAuth(): Promise<boolean> {\n\t// If already authenticated (e.g. ran in a terminal before), we can proceed\n\tconst existingToken = await getNeonctlAccessToken();\n\tif (existingToken) return true;\n\n\ttry {\n\t\t// Use execa to authenticate with neonctl\n\t\tawait execa(\"npx\", [\"-y\", \"neonctl\", \"me\", \"--no-analytics\"], {\n\t\t\t// Shows OAuth URL and prompts to the user\n\t\t\tstdio: \"inherit\",\n\t\t\t// Unset CI so neonctl doesn't refuse to open the browser (e.g. when run from agent chat)\n\t\t\tenv: { ...process.env, CI: undefined },\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\tconst msg = error instanceof Error ? error.message : \"Unknown error\";\n\t\tif (msg.includes(\"interactive auth\") || msg.includes(\"CI\")) {\n\t\t\tlog.error(\n\t\t\t\t\"Auth requires an interactive terminal. Run neon-init in your system terminal (outside the chat) to sign in.\",\n\t\t\t);\n\t\t} else {\n\t\t\tlog.error(`Authentication failed: ${msg}`);\n\t\t}\n\t\treturn false;\n\t}\n}\n\n/**\n * Gets the OAuth access token from neonctl's stored credentials\n */\nasync function getNeonctlAccessToken(): Promise<string | null> {\n\ttry {\n\t\tconst homeDir = process.env.HOME || process.env.USERPROFILE;\n\t\tif (!homeDir) return null;\n\n\t\tconst credentialsPath = resolve(\n\t\t\thomeDir,\n\t\t\t\".config\",\n\t\t\t\"neonctl\",\n\t\t\t\"credentials.json\",\n\t\t);\n\t\tif (!existsSync(credentialsPath)) return null;\n\n\t\tconst credentials = JSON.parse(readFileSync(credentialsPath, \"utf-8\"));\n\t\treturn credentials.access_token || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Creates an API key using the Neon API with the OAuth token from neonctl\n */\nexport async function createApiKeyFromNeonctl(): Promise<string | null> {\n\ttry {\n\t\tconst accessToken = await getNeonctlAccessToken();\n\t\tif (!accessToken) {\n\t\t\tlog.error(\"Could not find OAuth token from neonctl\");\n\t\t\treturn null;\n\t\t}\n\n\t\t// Generate a unique key name with timestamp\n\t\tconst timestamp = new Date()\n\t\t\t.toISOString()\n\t\t\t.replace(/[:.]/g, \"-\")\n\t\t\t.slice(0, -5); // e.g., 2024-10-08T15-30-45\n\t\tconst keyName = `neonctl-init-${timestamp}`;\n\n\t\t// Call Neon API to create an API key\n\t\tconst response = await fetch(\n\t\t\t\"https://console.neon.tech/api/v2/api_keys\",\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tkey_name: keyName,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\t\t\tlog.error(\n\t\t\t\t`Failed to create API key: ${response.status} ${errorText}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data = await response.json();\n\t\treturn data.key || null;\n\t} catch (error) {\n\t\tlog.error(\n\t\t\t`Failed to create API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t);\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;;;;;;;AASA,eAAsB,oBAAsC;AAG3D,KADsB,MAAM,uBAAuB,CAChC,QAAO;AAE1B,KAAI;AAEH,QAAM,MAAM,OAAO;GAAC;GAAM;GAAW;GAAM;GAAiB,EAAE;GAE7D,OAAO;GAEP,KAAK;IAAE,GAAG,QAAQ;IAAK,IAAI;IAAW;GACtC,CAAC;AACF,SAAO;UACC,OAAO;EACf,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU;AACrD,MAAI,IAAI,SAAS,mBAAmB,IAAI,IAAI,SAAS,KAAK,CACzD,KAAI,MACH,8GACA;MAED,KAAI,MAAM,0BAA0B,MAAM;AAE3C,SAAO;;;;;;AAOT,eAAe,wBAAgD;AAC9D,KAAI;EACH,MAAM,UAAU,QAAQ,IAAI,QAAQ,QAAQ,IAAI;AAChD,MAAI,CAAC,QAAS,QAAO;EAErB,MAAM,kBAAkB,QACvB,SACA,WACA,WACA,mBACA;AACD,MAAI,CAAC,WAAW,gBAAgB,CAAE,QAAO;AAGzC,SADoB,KAAK,MAAM,aAAa,iBAAiB,QAAQ,CAAC,CACnD,gBAAgB;SAC5B;AACP,SAAO;;;;;;AAOT,eAAsB,0BAAkD;AACvE,KAAI;EACH,MAAM,cAAc,MAAM,uBAAuB;AACjD,MAAI,CAAC,aAAa;AACjB,OAAI,MAAM,0CAA0C;AACpD,UAAO;;EAQR,MAAM,UAAU,iCAJE,IAAI,MAAM,EAC1B,aAAa,CACb,QAAQ,SAAS,IAAI,CACrB,MAAM,GAAG,GAAG;EAId,MAAM,WAAW,MAAM,MACtB,6CACA;GACC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU;IACzB,gBAAgB;IAChB;GACD,MAAM,KAAK,UAAU,EACpB,UAAU,SACV,CAAC;GACF,CACD;AAED,MAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,MAAM;AACvC,OAAI,MACH,6BAA6B,SAAS,OAAO,GAAG,YAChD;AACD,UAAO;;AAIR,UADa,MAAM,SAAS,MAAM,EACtB,OAAO;UACX,OAAO;AACf,MAAI,MACH,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,kBACtE;AACD,SAAO"}
1
+ {"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { log } from \"@clack/prompts\";\nimport { execa } from \"execa\";\n\nexport interface AuthOptions {\n\tjson?: boolean;\n}\n\n/**\n * Ensures neonctl is authenticated by running a command that triggers auth if needed\n * This will automatically start the OAuth flow if the user isn't already authenticated\n */\nexport async function ensureNeonctlAuth(\n\toptions?: AuthOptions,\n): Promise<boolean> {\n\tconst quiet = options?.json === true;\n\n\t// If already authenticated (e.g. ran in a terminal before), we can proceed\n\tconst existingToken = await getNeonctlAccessToken();\n\tif (existingToken) return true;\n\n\ttry {\n\t\t// Use execa to authenticate with neonctl\n\t\tawait execa(\"npx\", [\"-y\", \"neonctl\", \"me\"], {\n\t\t\t// Shows OAuth URL and prompts to the user\n\t\t\tstdio: \"inherit\",\n\t\t\t// Unset CI so neonctl doesn't refuse to open the browser (e.g. when run from agent chat)\n\t\t\tenv: { ...process.env, CI: undefined },\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\tconst msg = error instanceof Error ? error.message : \"Unknown error\";\n\t\tif (!quiet) {\n\t\t\tif (msg.includes(\"interactive auth\") || msg.includes(\"CI\")) {\n\t\t\t\tlog.error(\n\t\t\t\t\t\"Auth requires an interactive terminal. Run neon-init in your system terminal (outside the chat) to sign in.\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tlog.error(`Authentication failed: ${msg}`);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n\n/**\n * Checks whether neonctl has stored OAuth credentials.\n */\nexport async function isAuthenticated(): Promise<boolean> {\n\tconst token = await getNeonctlAccessToken();\n\treturn token !== null;\n}\n\n/**\n * Gets the OAuth access token from neonctl's stored credentials\n */\nasync function getNeonctlAccessToken(): Promise<string | null> {\n\ttry {\n\t\tconst homeDir = process.env.HOME || process.env.USERPROFILE;\n\t\tif (!homeDir) return null;\n\n\t\tconst credentialsPath = resolve(\n\t\t\thomeDir,\n\t\t\t\".config\",\n\t\t\t\"neonctl\",\n\t\t\t\"credentials.json\",\n\t\t);\n\t\tif (!existsSync(credentialsPath)) return null;\n\n\t\tconst credentials = JSON.parse(readFileSync(credentialsPath, \"utf-8\"));\n\t\treturn credentials.access_token || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Creates an API key using the Neon API with the OAuth token from neonctl\n */\nexport async function createApiKeyFromNeonctl(\n\toptions?: AuthOptions,\n): Promise<string | null> {\n\tconst quiet = options?.json === true;\n\n\ttry {\n\t\tconst accessToken = await getNeonctlAccessToken();\n\t\tif (!accessToken) {\n\t\t\tif (!quiet) log.error(\"Could not find OAuth token from neonctl\");\n\t\t\treturn null;\n\t\t}\n\n\t\t// Generate a unique key name with timestamp\n\t\tconst timestamp = new Date()\n\t\t\t.toISOString()\n\t\t\t.replace(/[:.]/g, \"-\")\n\t\t\t.slice(0, -5); // e.g., 2024-10-08T15-30-45\n\t\tconst keyName = `neonctl-init-${timestamp}`;\n\n\t\t// Call Neon API to create an API key\n\t\tconst response = await fetch(\n\t\t\t\"https://console.neon.tech/api/v2/api_keys\",\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tkey_name: keyName,\n\t\t\t\t}),\n\t\t\t\tsignal: AbortSignal.timeout(30000),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\t\t\tif (!quiet)\n\t\t\t\tlog.error(\n\t\t\t\t\t`Failed to create API key: ${response.status} ${errorText}`,\n\t\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data = await response.json();\n\t\treturn data.key || null;\n\t} catch (error) {\n\t\tif (!quiet)\n\t\t\tlog.error(\n\t\t\t\t`Failed to create API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t\t);\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;;;;;;AAaA,eAAsB,kBACrB,SACmB;CACnB,MAAM,QAAQ,SAAS,SAAS;CAIhC,IAAI,MADwB,sBAAsB,GAC/B,OAAO;CAE1B,IAAI;EAEH,MAAM,MAAM,OAAO;GAAC;GAAM;GAAW;EAAI,GAAG;GAE3C,OAAO;GAEP,KAAK;IAAE,GAAG,QAAQ;IAAK,IAAI,KAAA;GAAU;EACtC,CAAC;EACD,OAAO;CACR,SAAS,OAAO;EACf,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU;EACrD,IAAI,CAAC,OACJ,IAAI,IAAI,SAAS,kBAAkB,KAAK,IAAI,SAAS,IAAI,GACxD,IAAI,MACH,6GACD;OAEA,IAAI,MAAM,0BAA0B,KAAK;EAG3C,OAAO;CACR;AACD;;;;AAKA,eAAsB,kBAAoC;CAEzD,OAAO,MADa,sBAAsB,MACzB;AAClB;;;;AAKA,eAAe,wBAAgD;CAC9D,IAAI;EACH,MAAM,UAAU,QAAQ,IAAI,QAAQ,QAAQ,IAAI;EAChD,IAAI,CAAC,SAAS,OAAO;EAErB,MAAM,kBAAkB,QACvB,SACA,WACA,WACA,kBACD;EACA,IAAI,CAAC,WAAW,eAAe,GAAG,OAAO;EAGzC,OADoB,KAAK,MAAM,aAAa,iBAAiB,OAAO,CACnD,CAAC,CAAC,gBAAgB;CACpC,QAAQ;EACP,OAAO;CACR;AACD;;;;AAKA,eAAsB,wBACrB,SACyB;CACzB,MAAM,QAAQ,SAAS,SAAS;CAEhC,IAAI;EACH,MAAM,cAAc,MAAM,sBAAsB;EAChD,IAAI,CAAC,aAAa;GACjB,IAAI,CAAC,OAAO,IAAI,MAAM,yCAAyC;GAC/D,OAAO;EACR;EAOA,MAAM,UAAU,iCAJE,IAAI,KAAK,EAAA,CACzB,YAAY,CAAC,CACb,QAAQ,SAAS,GAAG,CAAC,CACrB,MAAM,GAAG,EAC6B;EAGxC,MAAM,WAAW,MAAM,MACtB,6CACA;GACC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU;IACzB,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU,EACpB,UAAU,QACX,CAAC;GACD,QAAQ,YAAY,QAAQ,GAAK;EAClC,CACD;EAEA,IAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,KAAK;GACtC,IAAI,CAAC,OACJ,IAAI,MACH,6BAA6B,SAAS,OAAO,GAAG,WACjD;GACD,OAAO;EACR;EAGA,QAAO,MADY,SAAS,KAAK,EAAA,CACrB,OAAO;CACpB,SAAS,OAAO;EACf,IAAI,CAAC,OACJ,IAAI,MACH,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,iBACvE;EACD,OAAO;CACR;AACD"}
@@ -0,0 +1,30 @@
1
+ //#region src/lib/bootstrap.d.ts
2
+ /**
3
+ * Neon features that a template or project may require.
4
+ * Each feature maps to a setup phase that the orchestrator can run.
5
+ */
6
+ type NeonFeature = "database" | "auth" | "functions" | "ai-gateway" | "object-storage";
7
+ interface BootstrapTemplate {
8
+ id: string;
9
+ title: string;
10
+ description: string;
11
+ /** Neon features this template needs (defaults to ["database"]) */
12
+ requires: NeonFeature[];
13
+ source: {
14
+ owner: string;
15
+ repo: string;
16
+ ref: string;
17
+ subdir: string;
18
+ };
19
+ }
20
+ /** Hardcoded fallback used when the remote manifest cannot be fetched. */
21
+ declare const FALLBACK_TEMPLATES: BootstrapTemplate[];
22
+ declare function parseManifest(text: string): BootstrapTemplate[];
23
+ /**
24
+ * Fetch the template manifest from the remote bootstrap.yaml in the
25
+ * neondatabase/examples repo. Falls back to the hardcoded list on any error.
26
+ */
27
+ declare function fetchTemplates(): Promise<BootstrapTemplate[]>;
28
+ //#endregion
29
+ export { BootstrapTemplate, FALLBACK_TEMPLATES, NeonFeature, fetchTemplates, parseManifest };
30
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","names":[],"sources":["../../src/lib/bootstrap.ts"],"mappings":";;AAMA;AAUA;AAeA;AAsBgB,KA/CJ,WAAA,GA+CiB,UAAgB,GAAA,MAAA,GAAiB,WAAA,GAAA,YAAA,GAAA,gBAAA;AAiDxC,UAtFL,iBAAA,CAsFmB;EAAA,EAAA,EAAA,MAAA;OAAY,EAAA,MAAA;aAAR,EAAA,MAAA;EAAO;YAjFpC;;;;;;;;;cAUE,oBAAoB;iBAsBjB,aAAA,gBAA6B;;;;;iBAiDvB,cAAA,CAAA,GAAkB,QAAQ"}
@@ -0,0 +1,61 @@
1
+ import YAML from "yaml";
2
+ //#region src/lib/bootstrap.ts
3
+ /** Default features when a template doesn't specify `requires`. */
4
+ const DEFAULT_REQUIRES = ["database"];
5
+ /** Hardcoded fallback used when the remote manifest cannot be fetched. */
6
+ const FALLBACK_TEMPLATES = [{
7
+ id: "hono",
8
+ title: "Hono API (Drizzle, Neon Postgres) on Neon Functions",
9
+ description: "A Hono API using Drizzle ORM and Neon Postgres, ready to deploy as a Neon Function.",
10
+ requires: ["database", "functions"],
11
+ source: {
12
+ owner: "neondatabase",
13
+ repo: "examples",
14
+ ref: "main",
15
+ subdir: "with-hono"
16
+ }
17
+ }];
18
+ const MANIFEST_URL = "https://raw.githubusercontent.com/neondatabase/examples/main/bootstrap.yaml";
19
+ const isRecord = (value) => typeof value === "object" && value !== null;
20
+ function parseManifest(text) {
21
+ const data = YAML.parse(text);
22
+ if (!isRecord(data) || !Array.isArray(data.templates)) throw new Error("Invalid bootstrap manifest: missing \"templates\" array.");
23
+ const templates = [];
24
+ for (const item of data.templates) {
25
+ if (!isRecord(item) || typeof item.id !== "string" || typeof item.title !== "string" || typeof item.description !== "string" || !isRecord(item.source) || typeof item.source.owner !== "string" || typeof item.source.repo !== "string" || typeof item.source.ref !== "string" || typeof item.source.subdir !== "string") continue;
26
+ const requires = Array.isArray(item.requires) && item.requires.every((r) => typeof r === "string") ? item.requires : DEFAULT_REQUIRES;
27
+ templates.push({
28
+ id: item.id,
29
+ title: item.title,
30
+ description: item.description,
31
+ requires,
32
+ source: {
33
+ owner: item.source.owner,
34
+ repo: item.source.repo,
35
+ ref: item.source.ref,
36
+ subdir: item.source.subdir
37
+ }
38
+ });
39
+ }
40
+ return templates;
41
+ }
42
+ /**
43
+ * Fetch the template manifest from the remote bootstrap.yaml in the
44
+ * neondatabase/examples repo. Falls back to the hardcoded list on any error.
45
+ */
46
+ async function fetchTemplates() {
47
+ const url = process.env.NEON_BOOTSTRAP_MANIFEST_URL ?? MANIFEST_URL;
48
+ try {
49
+ const res = await fetch(url, { signal: AbortSignal.timeout(1e4) });
50
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
51
+ const templates = parseManifest(await res.text());
52
+ if (templates.length === 0) return FALLBACK_TEMPLATES;
53
+ return templates;
54
+ } catch {
55
+ return FALLBACK_TEMPLATES;
56
+ }
57
+ }
58
+ //#endregion
59
+ export { FALLBACK_TEMPLATES, fetchTemplates, parseManifest };
60
+
61
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","names":[],"sources":["../../src/lib/bootstrap.ts"],"sourcesContent":["import YAML from \"yaml\";\n\n/**\n * Neon features that a template or project may require.\n * Each feature maps to a setup phase that the orchestrator can run.\n */\nexport type NeonFeature =\n\t| \"database\"\n\t| \"auth\"\n\t| \"functions\"\n\t| \"ai-gateway\"\n\t| \"object-storage\";\n\n/** Default features when a template doesn't specify `requires`. */\nconst DEFAULT_REQUIRES: NeonFeature[] = [\"database\"];\n\nexport interface BootstrapTemplate {\n\tid: string;\n\ttitle: string;\n\tdescription: string;\n\t/** Neon features this template needs (defaults to [\"database\"]) */\n\trequires: NeonFeature[];\n\tsource: {\n\t\towner: string;\n\t\trepo: string;\n\t\tref: string;\n\t\tsubdir: string;\n\t};\n}\n\n/** Hardcoded fallback used when the remote manifest cannot be fetched. */\nexport const FALLBACK_TEMPLATES: BootstrapTemplate[] = [\n\t{\n\t\tid: \"hono\",\n\t\ttitle: \"Hono API (Drizzle, Neon Postgres) on Neon Functions\",\n\t\tdescription:\n\t\t\t\"A Hono API using Drizzle ORM and Neon Postgres, ready to deploy as a Neon Function.\",\n\t\trequires: [\"database\", \"functions\"],\n\t\tsource: {\n\t\t\towner: \"neondatabase\",\n\t\t\trepo: \"examples\",\n\t\t\tref: \"main\",\n\t\t\tsubdir: \"with-hono\",\n\t\t},\n\t},\n];\n\nconst MANIFEST_URL =\n\t\"https://raw.githubusercontent.com/neondatabase/examples/main/bootstrap.yaml\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nexport function parseManifest(text: string): BootstrapTemplate[] {\n\tconst data: unknown = YAML.parse(text);\n\tif (!isRecord(data) || !Array.isArray(data.templates)) {\n\t\tthrow new Error(\n\t\t\t'Invalid bootstrap manifest: missing \"templates\" array.',\n\t\t);\n\t}\n\tconst templates: BootstrapTemplate[] = [];\n\tfor (const item of data.templates) {\n\t\tif (\n\t\t\t!isRecord(item) ||\n\t\t\ttypeof item.id !== \"string\" ||\n\t\t\ttypeof item.title !== \"string\" ||\n\t\t\ttypeof item.description !== \"string\" ||\n\t\t\t!isRecord(item.source) ||\n\t\t\ttypeof item.source.owner !== \"string\" ||\n\t\t\ttypeof item.source.repo !== \"string\" ||\n\t\t\ttypeof item.source.ref !== \"string\" ||\n\t\t\ttypeof item.source.subdir !== \"string\"\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\t// Parse requires — accept string array, default to [\"database\"]\n\t\tconst requires: NeonFeature[] =\n\t\t\tArray.isArray(item.requires) &&\n\t\t\titem.requires.every((r: unknown) => typeof r === \"string\")\n\t\t\t\t? (item.requires as NeonFeature[])\n\t\t\t\t: DEFAULT_REQUIRES;\n\n\t\ttemplates.push({\n\t\t\tid: item.id,\n\t\t\ttitle: item.title,\n\t\t\tdescription: item.description,\n\t\t\trequires,\n\t\t\tsource: {\n\t\t\t\towner: item.source.owner,\n\t\t\t\trepo: item.source.repo,\n\t\t\t\tref: item.source.ref,\n\t\t\t\tsubdir: item.source.subdir,\n\t\t\t},\n\t\t});\n\t}\n\treturn templates;\n}\n\n/**\n * Fetch the template manifest from the remote bootstrap.yaml in the\n * neondatabase/examples repo. Falls back to the hardcoded list on any error.\n */\nexport async function fetchTemplates(): Promise<BootstrapTemplate[]> {\n\tconst url = process.env.NEON_BOOTSTRAP_MANIFEST_URL ?? MANIFEST_URL;\n\ttry {\n\t\tconst res = await fetch(url, {\n\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t});\n\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\t\tconst text = await res.text();\n\t\tconst templates = parseManifest(text);\n\t\tif (templates.length === 0) return FALLBACK_TEMPLATES;\n\t\treturn templates;\n\t} catch {\n\t\treturn FALLBACK_TEMPLATES;\n\t}\n}\n"],"mappings":";;;AAcA,MAAM,mBAAkC,CAAC,UAAU;;AAiBnD,MAAa,qBAA0C,CACtD;CACC,IAAI;CACJ,OAAO;CACP,aACC;CACD,UAAU,CAAC,YAAY,WAAW;CAClC,QAAQ;EACP,OAAO;EACP,MAAM;EACN,KAAK;EACL,QAAQ;CACT;AACD,CACD;AAEA,MAAM,eACL;AAED,MAAM,YAAY,UACjB,OAAO,UAAU,YAAY,UAAU;AAExC,SAAgB,cAAc,MAAmC;CAChE,MAAM,OAAgB,KAAK,MAAM,IAAI;CACrC,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,QAAQ,KAAK,SAAS,GACnD,MAAM,IAAI,MACT,0DACD;CAED,MAAM,YAAiC,CAAC;CACxC,KAAK,MAAM,QAAQ,KAAK,WAAW;EAClC,IACC,CAAC,SAAS,IAAI,KACd,OAAO,KAAK,OAAO,YACnB,OAAO,KAAK,UAAU,YACtB,OAAO,KAAK,gBAAgB,YAC5B,CAAC,SAAS,KAAK,MAAM,KACrB,OAAO,KAAK,OAAO,UAAU,YAC7B,OAAO,KAAK,OAAO,SAAS,YAC5B,OAAO,KAAK,OAAO,QAAQ,YAC3B,OAAO,KAAK,OAAO,WAAW,UAE9B;EAGD,MAAM,WACL,MAAM,QAAQ,KAAK,QAAQ,KAC3B,KAAK,SAAS,OAAO,MAAe,OAAO,MAAM,QAAQ,IACrD,KAAK,WACN;EAEJ,UAAU,KAAK;GACd,IAAI,KAAK;GACT,OAAO,KAAK;GACZ,aAAa,KAAK;GAClB;GACA,QAAQ;IACP,OAAO,KAAK,OAAO;IACnB,MAAM,KAAK,OAAO;IAClB,KAAK,KAAK,OAAO;IACjB,QAAQ,KAAK,OAAO;GACrB;EACD,CAAC;CACF;CACA,OAAO;AACR;;;;;AAMA,eAAsB,iBAA+C;CACpE,MAAM,MAAM,QAAQ,IAAI,+BAA+B;CACvD,IAAI;EACH,MAAM,MAAM,MAAM,MAAM,KAAK,EAC5B,QAAQ,YAAY,QAAQ,GAAM,EACnC,CAAC;EACD,IAAI,CAAC,IAAI,IAAI,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAQ;EAEjD,MAAM,YAAY,cAAc,MADb,IAAI,KAAK,CACQ;EACpC,IAAI,UAAU,WAAW,GAAG,OAAO;EACnC,OAAO;CACR,QAAQ;EACP,OAAO;CACR;AACD"}
@@ -0,0 +1,5 @@
1
+ //#region src/lib/build-config.d.ts
2
+ declare const INTERNAL_VSX_GALLERY = "";
3
+ //#endregion
4
+ export { INTERNAL_VSX_GALLERY };
5
+ //# sourceMappingURL=build-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-config.d.ts","names":[],"sources":["../../src/lib/build-config.ts"],"mappings":";cACa,oBAAA"}
@@ -0,0 +1,6 @@
1
+ //#region src/lib/build-config.ts
2
+ const INTERNAL_VSX_GALLERY = "";
3
+ //#endregion
4
+ export { INTERNAL_VSX_GALLERY };
5
+
6
+ //# sourceMappingURL=build-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-config.js","names":[],"sources":["../../src/lib/build-config.ts"],"sourcesContent":["// Auto-generated by scripts/set-vsx-gallery.mjs — do not edit\nexport const INTERNAL_VSX_GALLERY = \"\";\n"],"mappings":";AACA,MAAa,uBAAuB"}
@@ -0,0 +1,22 @@
1
+ //#region src/lib/detect-agent.d.ts
2
+ /**
3
+ * Detects the IDE/editor the terminal is running in, regardless of which
4
+ * agent is active. Used for extension installation — the extension goes
5
+ * into the IDE, not the agent.
6
+ *
7
+ * Returns an Editor-compatible string: "Cursor", "VS Code", "Windsurf", or null.
8
+ */
9
+ declare function detectIde(): "Cursor" | "VS Code" | "Windsurf" | null;
10
+ declare function isCursorInstalled(): boolean;
11
+ declare function isVSCodeInstalled(): boolean;
12
+ /**
13
+ * Detects which agent/IDE is running the CLI from environment variables.
14
+ * Returns the add-mcp compatible agent ID (e.g. "cursor", "claude-code").
15
+ *
16
+ * Delegates IDE detection to detectIde() to avoid duplicating Cursor/VS Code
17
+ * heuristics, then checks for agent-specific env vars.
18
+ */
19
+ declare function detectAgent(): string | null;
20
+ //#endregion
21
+ export { detectAgent, detectIde, isCursorInstalled, isVSCodeInstalled };
22
+ //# sourceMappingURL=detect-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-agent.d.ts","names":[],"sources":["../../src/lib/detect-agent.ts"],"mappings":";;AASA;AAyDA;AAiBA;AA0BA;;;iBApGgB,SAAA,CAAA;iBAyDA,iBAAA,CAAA;iBAiBA,iBAAA,CAAA;;;;;;;;iBA0BA,WAAA,CAAA"}
@@ -0,0 +1,65 @@
1
+ import { existsSync } from "node:fs";
2
+ //#region src/lib/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 };
64
+
65
+ //# sourceMappingURL=detect-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-agent.js","names":[],"sources":["../../src/lib/detect-agent.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\n\n/**\n * Detects the IDE/editor the terminal is running in, regardless of which\n * agent is active. Used for extension installation — the extension goes\n * into the IDE, not the agent.\n *\n * Returns an Editor-compatible string: \"Cursor\", \"VS Code\", \"Windsurf\", or null.\n */\nexport function detectIde(): \"Cursor\" | \"VS Code\" | \"Windsurf\" | null {\n\tconst env = process.env;\n\n\t// Cursor: check explicit Cursor env vars (set in extension host context)\n\tif (\n\t\tenv.TERM_PROGRAM === \"cursor\" ||\n\t\tenv.CURSOR_TRACE_ID !== undefined ||\n\t\tenv.CURSOR_EXTENSION_HOST_ROLE !== undefined ||\n\t\tenv.CURSOR_LAYOUT !== undefined ||\n\t\tenv.CURSOR_SPAWNED_BY_EXTENSION_ID !== undefined\n\t) {\n\t\treturn \"Cursor\";\n\t}\n\n\t// Cursor: check app paths in env vars (reliable from terminal context)\n\tif (\n\t\tenv.GIT_ASKPASS?.includes(\"Cursor.app\") ||\n\t\tenv.VSCODE_GIT_ASKPASS_NODE?.includes(\"Cursor.app\") ||\n\t\tenv.GIT_ASKPASS?.includes(\"cursor\") ||\n\t\tenv.VSCODE_GIT_ASKPASS_NODE?.includes(\"cursor\")\n\t) {\n\t\treturn \"Cursor\";\n\t}\n\n\t// Windsurf\n\tif (env.TERM_PROGRAM === \"windsurf\") {\n\t\treturn \"Windsurf\";\n\t}\n\n\t// At this point we know we're in a VS Code-like environment if any\n\t// VSCODE_ env vars are set. But is it actually Cursor with generic env vars?\n\tif (\n\t\tenv.TERM_PROGRAM === \"vscode\" ||\n\t\tenv.VSCODE_PID !== undefined ||\n\t\tenv.VSCODE_CWD !== undefined\n\t) {\n\t\t// Check any remaining env vars that might contain \"cursor\" (case-insensitive)\n\t\t// This catches VSCODE_IPC_HOOK_CLI, PATH additions, etc.\n\t\tconst envValues = Object.values(env).join(\"\\n\").toLowerCase();\n\t\tif (\n\t\t\tenvValues.includes(\"cursor.app\") ||\n\t\t\tenvValues.includes(\"/cursor/\")\n\t\t) {\n\t\t\treturn \"Cursor\";\n\t\t}\n\n\t\t// If only Cursor is installed (not VS Code), it must be Cursor\n\t\tif (isCursorInstalled() && !isVSCodeInstalled()) {\n\t\t\treturn \"Cursor\";\n\t\t}\n\n\t\treturn \"VS Code\";\n\t}\n\n\treturn null;\n}\n\nexport function isCursorInstalled(): boolean {\n\tif (process.platform === \"darwin\") {\n\t\treturn existsSync(\"/Applications/Cursor.app\");\n\t}\n\tif (process.platform === \"linux\") {\n\t\treturn existsSync(\"/usr/share/cursor\") || existsSync(\"/usr/bin/cursor\");\n\t}\n\tif (process.platform === \"win32\") {\n\t\tconst localAppData = process.env.LOCALAPPDATA || \"\";\n\t\treturn (\n\t\t\texistsSync(`${localAppData}\\\\Programs\\\\Cursor\\\\Cursor.exe`) ||\n\t\t\texistsSync(`${localAppData}\\\\cursor\\\\Cursor.exe`)\n\t\t);\n\t}\n\treturn false;\n}\n\nexport function isVSCodeInstalled(): boolean {\n\tif (process.platform === \"darwin\") {\n\t\treturn existsSync(\"/Applications/Visual Studio Code.app\");\n\t}\n\tif (process.platform === \"linux\") {\n\t\treturn existsSync(\"/usr/share/code\") || existsSync(\"/usr/bin/code\");\n\t}\n\tif (process.platform === \"win32\") {\n\t\tconst localAppData = process.env.LOCALAPPDATA || \"\";\n\t\tconst programFiles = process.env.PROGRAMFILES || \"C:\\\\Program Files\";\n\t\treturn (\n\t\t\texistsSync(\n\t\t\t\t`${localAppData}\\\\Programs\\\\Microsoft VS Code\\\\Code.exe`,\n\t\t\t) || existsSync(`${programFiles}\\\\Microsoft VS Code\\\\Code.exe`)\n\t\t);\n\t}\n\treturn false;\n}\n\n/**\n * Detects which agent/IDE is running the CLI from environment variables.\n * Returns the add-mcp compatible agent ID (e.g. \"cursor\", \"claude-code\").\n *\n * Delegates IDE detection to detectIde() to avoid duplicating Cursor/VS Code\n * heuristics, then checks for agent-specific env vars.\n */\nexport function detectAgent(): string | null {\n\tconst env = process.env;\n\n\t// Agent-specific env vars (checked first — these are unambiguous)\n\tif (\n\t\tenv.CLAUDECODE === \"1\" ||\n\t\tenv.CLAUDE_CODE === \"1\" ||\n\t\tenv.CLAUDE_CLI === \"1\"\n\t) {\n\t\treturn \"claude-code\";\n\t}\n\tif (env.CODEX === \"1\") return \"codex\";\n\tif (env.CLINE === \"1\") return \"cline\";\n\n\t// Fall back to IDE detection (Cursor, VS Code, Windsurf)\n\tconst ide = detectIde();\n\tif (ide) {\n\t\tconst IDE_TO_AGENT: Record<string, string> = {\n\t\t\tCursor: \"cursor\",\n\t\t\t\"VS Code\": \"vscode\",\n\t\t\tWindsurf: \"windsurf\",\n\t\t};\n\t\treturn IDE_TO_AGENT[ide] ?? null;\n\t}\n\n\treturn null;\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,YAAsD;CACrE,MAAM,MAAM,QAAQ;CAGpB,IACC,IAAI,iBAAiB,YACrB,IAAI,oBAAoB,KAAA,KACxB,IAAI,+BAA+B,KAAA,KACnC,IAAI,kBAAkB,KAAA,KACtB,IAAI,mCAAmC,KAAA,GAEvC,OAAO;CAIR,IACC,IAAI,aAAa,SAAS,YAAY,KACtC,IAAI,yBAAyB,SAAS,YAAY,KAClD,IAAI,aAAa,SAAS,QAAQ,KAClC,IAAI,yBAAyB,SAAS,QAAQ,GAE9C,OAAO;CAIR,IAAI,IAAI,iBAAiB,YACxB,OAAO;CAKR,IACC,IAAI,iBAAiB,YACrB,IAAI,eAAe,KAAA,KACnB,IAAI,eAAe,KAAA,GAClB;EAGD,MAAM,YAAY,OAAO,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,YAAY;EAC5D,IACC,UAAU,SAAS,YAAY,KAC/B,UAAU,SAAS,UAAU,GAE7B,OAAO;EAIR,IAAI,kBAAkB,KAAK,CAAC,kBAAkB,GAC7C,OAAO;EAGR,OAAO;CACR;CAEA,OAAO;AACR;AAEA,SAAgB,oBAA6B;CAC5C,IAAI,QAAQ,aAAa,UACxB,OAAO,WAAW,0BAA0B;CAE7C,IAAI,QAAQ,aAAa,SACxB,OAAO,WAAW,mBAAmB,KAAK,WAAW,iBAAiB;CAEvE,IAAI,QAAQ,aAAa,SAAS;EACjC,MAAM,eAAe,QAAQ,IAAI,gBAAgB;EACjD,OACC,WAAW,GAAG,aAAa,+BAA+B,KAC1D,WAAW,GAAG,aAAa,qBAAqB;CAElD;CACA,OAAO;AACR;AAEA,SAAgB,oBAA6B;CAC5C,IAAI,QAAQ,aAAa,UACxB,OAAO,WAAW,sCAAsC;CAEzD,IAAI,QAAQ,aAAa,SACxB,OAAO,WAAW,iBAAiB,KAAK,WAAW,eAAe;CAEnE,IAAI,QAAQ,aAAa,SAAS;EACjC,MAAM,eAAe,QAAQ,IAAI,gBAAgB;EACjD,MAAM,eAAe,QAAQ,IAAI,gBAAgB;EACjD,OACC,WACC,GAAG,aAAa,wCACjB,KAAK,WAAW,GAAG,aAAa,8BAA8B;CAEhE;CACA,OAAO;AACR;;;;;;;;AASA,SAAgB,cAA6B;CAC5C,MAAM,MAAM,QAAQ;CAGpB,IACC,IAAI,eAAe,OACnB,IAAI,gBAAgB,OACpB,IAAI,eAAe,KAEnB,OAAO;CAER,IAAI,IAAI,UAAU,KAAK,OAAO;CAC9B,IAAI,IAAI,UAAU,KAAK,OAAO;CAG9B,MAAM,MAAM,UAAU;CACtB,IAAI,KAMH,OAAO;EAJN,QAAQ;EACR,WAAW;EACX,UAAU;CAEO,EAAE,QAAQ;CAG7B,OAAO;AACR"}
@@ -1 +1 @@
1
- {"version":3,"file":"editors.d.ts","names":[],"sources":["../../src/lib/editors.ts"],"sourcesContent":[],"mappings":";;;;;;AAQA;AA8CsB,iBA9CN,wBAAA,CA8C4B,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;;;;AAElC,iBAFY,sBAAA,CAEZ,OAAA,EAAA,MAAA,CAAA,EAAP,OAAO,CAAC,MAAD,EAAA,CAAA"}
1
+ {"version":3,"file":"editors.d.ts","names":[],"sources":["../../src/lib/editors.ts"],"mappings":";;;;;;AAQA;AA8CsB,iBA9CN,wBAAA,CA8C4B,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;;;;AAElC,iBAFY,sBAAA,CAEZ,OAAA,EAAA,MAAA,CAAA,EAAP,OAAO,CAAC,MAAD,EAAA,CAAA"}
@@ -1,7 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { execa } from "execa";
4
-
5
4
  //#region src/lib/editors.ts
6
5
  /**
7
6
  * Gets VS Code's global config directory based on the platform
@@ -41,7 +40,7 @@ async function detectAvailableEditors(homeDir) {
41
40
  if (await isClaudeCLIInstalled()) editors.push("Claude CLI");
42
41
  return editors;
43
42
  }
44
-
45
43
  //#endregion
46
44
  export { detectAvailableEditors, getVSCodeGlobalConfigDir };
45
+
47
46
  //# sourceMappingURL=editors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"editors.js","names":[],"sources":["../../src/lib/editors.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execa } from \"execa\";\nimport type { Editor } from \"./types.js\";\n\n/**\n * Gets VS Code's global config directory based on the platform\n */\nexport function getVSCodeGlobalConfigDir(homeDir: string): string | null {\n\tconst platform = process.platform;\n\n\tif (platform === \"darwin\") {\n\t\t// macOS: ~/Library/Application Support/Code/User\n\t\treturn resolve(\n\t\t\thomeDir,\n\t\t\t\"Library\",\n\t\t\t\"Application Support\",\n\t\t\t\"Code\",\n\t\t\t\"User\",\n\t\t);\n\t}\n\tif (platform === \"linux\") {\n\t\t// Linux: ~/.config/Code/User\n\t\treturn resolve(homeDir, \".config\", \"Code\", \"User\");\n\t}\n\tif (platform === \"win32\") {\n\t\t// Windows: %APPDATA%\\Code\\User\n\t\tconst appData = process.env.APPDATA;\n\t\tif (appData) {\n\t\t\treturn resolve(appData, \"Code\", \"User\");\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Checks if Claude CLI is installed\n */\nasync function isClaudeCLIInstalled(): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"claude\", [\"--version\"], {\n\t\t\tstdio: \"ignore\",\n\t\t\ttimeout: 5000,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Detects which editors are installed on the system\n */\nexport async function detectAvailableEditors(\n\thomeDir: string,\n): Promise<Editor[]> {\n\tconst editors: Editor[] = [];\n\n\t// Check for Cursor (global config directory)\n\tconst cursorDir = resolve(homeDir, \".cursor\");\n\tif (existsSync(cursorDir)) {\n\t\teditors.push(\"Cursor\");\n\t}\n\n\t// Check if VS Code's global config directory exists\n\tconst vscodeGlobalDir = getVSCodeGlobalConfigDir(homeDir);\n\tif (vscodeGlobalDir && existsSync(vscodeGlobalDir)) {\n\t\teditors.push(\"VS Code\");\n\t}\n\n\t// Check for Claude CLI by running the command\n\tconst claudeInstalled = await isClaudeCLIInstalled();\n\tif (claudeInstalled) {\n\t\teditors.push(\"Claude CLI\");\n\t}\n\n\treturn editors;\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,yBAAyB,SAAgC;CACxE,MAAM,WAAW,QAAQ;AAEzB,KAAI,aAAa,SAEhB,QAAO,QACN,SACA,WACA,uBACA,QACA,OACA;AAEF,KAAI,aAAa,QAEhB,QAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;AAEnD,KAAI,aAAa,SAAS;EAEzB,MAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,QACH,QAAO,QAAQ,SAAS,QAAQ,OAAO;;AAIzC,QAAO;;;;;AAMR,eAAe,uBAAyC;AACvD,KAAI;AACH,QAAM,MAAM,UAAU,CAAC,YAAY,EAAE;GACpC,OAAO;GACP,SAAS;GACT,CAAC;AACF,SAAO;SACA;AACP,SAAO;;;;;;AAOT,eAAsB,uBACrB,SACoB;CACpB,MAAM,UAAoB,EAAE;AAI5B,KAAI,WADc,QAAQ,SAAS,UAAU,CACpB,CACxB,SAAQ,KAAK,SAAS;CAIvB,MAAM,kBAAkB,yBAAyB,QAAQ;AACzD,KAAI,mBAAmB,WAAW,gBAAgB,CACjD,SAAQ,KAAK,UAAU;AAKxB,KADwB,MAAM,sBAAsB,CAEnD,SAAQ,KAAK,aAAa;AAG3B,QAAO"}
1
+ {"version":3,"file":"editors.js","names":[],"sources":["../../src/lib/editors.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execa } from \"execa\";\nimport type { Editor } from \"./types.js\";\n\n/**\n * Gets VS Code's global config directory based on the platform\n */\nexport function getVSCodeGlobalConfigDir(homeDir: string): string | null {\n\tconst platform = process.platform;\n\n\tif (platform === \"darwin\") {\n\t\t// macOS: ~/Library/Application Support/Code/User\n\t\treturn resolve(\n\t\t\thomeDir,\n\t\t\t\"Library\",\n\t\t\t\"Application Support\",\n\t\t\t\"Code\",\n\t\t\t\"User\",\n\t\t);\n\t}\n\tif (platform === \"linux\") {\n\t\t// Linux: ~/.config/Code/User\n\t\treturn resolve(homeDir, \".config\", \"Code\", \"User\");\n\t}\n\tif (platform === \"win32\") {\n\t\t// Windows: %APPDATA%\\Code\\User\n\t\tconst appData = process.env.APPDATA;\n\t\tif (appData) {\n\t\t\treturn resolve(appData, \"Code\", \"User\");\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Checks if Claude CLI is installed\n */\nasync function isClaudeCLIInstalled(): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"claude\", [\"--version\"], {\n\t\t\tstdio: \"ignore\",\n\t\t\ttimeout: 5000,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Detects which editors are installed on the system\n */\nexport async function detectAvailableEditors(\n\thomeDir: string,\n): Promise<Editor[]> {\n\tconst editors: Editor[] = [];\n\n\t// Check for Cursor (global config directory)\n\tconst cursorDir = resolve(homeDir, \".cursor\");\n\tif (existsSync(cursorDir)) {\n\t\teditors.push(\"Cursor\");\n\t}\n\n\t// Check if VS Code's global config directory exists\n\tconst vscodeGlobalDir = getVSCodeGlobalConfigDir(homeDir);\n\tif (vscodeGlobalDir && existsSync(vscodeGlobalDir)) {\n\t\teditors.push(\"VS Code\");\n\t}\n\n\t// Check for Claude CLI by running the command\n\tconst claudeInstalled = await isClaudeCLIInstalled();\n\tif (claudeInstalled) {\n\t\teditors.push(\"Claude CLI\");\n\t}\n\n\treturn editors;\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,yBAAyB,SAAgC;CACxE,MAAM,WAAW,QAAQ;CAEzB,IAAI,aAAa,UAEhB,OAAO,QACN,SACA,WACA,uBACA,QACA,MACD;CAED,IAAI,aAAa,SAEhB,OAAO,QAAQ,SAAS,WAAW,QAAQ,MAAM;CAElD,IAAI,aAAa,SAAS;EAEzB,MAAM,UAAU,QAAQ,IAAI;EAC5B,IAAI,SACH,OAAO,QAAQ,SAAS,QAAQ,MAAM;CAExC;CAEA,OAAO;AACR;;;;AAKA,eAAe,uBAAyC;CACvD,IAAI;EACH,MAAM,MAAM,UAAU,CAAC,WAAW,GAAG;GACpC,OAAO;GACP,SAAS;EACV,CAAC;EACD,OAAO;CACR,QAAQ;EACP,OAAO;CACR;AACD;;;;AAKA,eAAsB,uBACrB,SACoB;CACpB,MAAM,UAAoB,CAAC;CAI3B,IAAI,WADc,QAAQ,SAAS,SACZ,CAAC,GACvB,QAAQ,KAAK,QAAQ;CAItB,MAAM,kBAAkB,yBAAyB,OAAO;CACxD,IAAI,mBAAmB,WAAW,eAAe,GAChD,QAAQ,KAAK,SAAS;CAKvB,IAAI,MAD0B,qBAAqB,GAElD,QAAQ,KAAK,YAAY;CAG1B,OAAO;AACR"}
@@ -8,14 +8,22 @@ import { Editor } from "./types.js";
8
8
  * Falls back to the simple command name if no full path is found (in case it's in PATH)
9
9
  */
10
10
  declare function findEditorCommand(editor: Editor): Promise<string | null>;
11
+ /**
12
+ * Checks if the extension is installed by querying the editor's extension list
13
+ */
14
+ declare function isExtensionInstalled(editor: Editor): Promise<boolean>;
11
15
  /**
12
16
  * Waits for the extension to appear in the installed extensions list
13
17
  * This ensures the extension is fully installed and activated before we try to configure it
14
18
  */
15
19
  declare function waitForExtensionInstalled(editor: Editor, maxAttempts?: number, delayMs?: number): Promise<boolean>;
16
20
  /**
17
- * Installs the Neon Local Connect extension for VS Code or Cursor
18
- * Returns success only if installation succeeds, fails silently otherwise
21
+ * Installs the Neon Local Connect extension for VS Code or Cursor.
22
+ *
23
+ * Strategy:
24
+ * 1. Try `--install-extension <id>` directly (uses the editor's configured marketplace)
25
+ * 2. If that fails, download .vsix (from proxy or Open VSX) and install locally
26
+ * 3. Set NEON_VSX_GALLERY_URL to use a corporate proxy for the download
19
27
  */
20
28
  declare function installExtension(editor: Editor): Promise<boolean>;
21
29
  /**
@@ -28,5 +36,5 @@ declare function configureExtension(editor: Editor, apiKey: string): Promise<boo
28
36
  */
29
37
  declare function usesExtension(editor: Editor): boolean;
30
38
  //#endregion
31
- export { configureExtension, findEditorCommand, installExtension, usesExtension, waitForExtensionInstalled };
39
+ export { configureExtension, findEditorCommand, installExtension, isExtensionInstalled, usesExtension, waitForExtensionInstalled };
32
40
  //# sourceMappingURL=extension.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.ts","names":[],"sources":["../../src/lib/extension.ts"],"sourcesContent":[],"mappings":";;;;;;AAuGA;;;AAEG,iBAFmB,iBAAA,CAEnB,MAAA,EADM,MACN,CAAA,EAAA,OAAA,CAAA,MAAA,GAAA,IAAA,CAAA;;AAyEH;;;AAIG,iBAJmB,yBAAA,CAInB,MAAA,EAHM,MAGN,EAAA,WAAA,CAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,OAAA,CAAA,OAAA,CAAA;;AAuBH;;;AAAwD,iBAAlC,gBAAA,CAAkC,MAAA,EAAT,MAAS,CAAA,EAAA,OAAA,CAAA,OAAA,CAAA;;AAkBxD;;;AAGG,iBAHmB,kBAAA,CAGnB,MAAA,EAFM,MAEN,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,OAAA,CAAA,OAAA,CAAA;;AAoCH;;iBAAgB,aAAA,SAAsB"}
1
+ {"version":3,"file":"extension.d.ts","names":[],"sources":["../../src/lib/extension.ts"],"mappings":";;;;;;AAuGA;;;AAEG,iBAFmB,iBAAA,CAEnB,MAAA,EADM,MACN,CAAA,EAAA,OAAA,CAAA,MAAA,GAAA,IAAA,CAAA;AAAO;AAqDV;;AAAmD,iBAA7B,oBAAA,CAA6B,MAAA,EAAA,MAAA,CAAA,EAAS,OAAT,CAAA,OAAA,CAAA;;AAAgB;AAoBnE;;AACS,iBADa,yBAAA,CACb,MAAA,EAAA,MAAA,EAAA,WAAA,CAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAGN,OAHM,CAAA,OAAA,CAAA;;AAGC;AA2BV;;;;AAA+D;AA0C/D;AAAwC,iBA1ClB,gBAAA,CA0CkB,MAAA,EA1CO,MA0CP,CAAA,EA1CgB,OA0ChB,CAAA,OAAA,CAAA;;;AAG9B;AAoCV;iBAvCsB,kBAAA,SACb,yBAEN;;;;iBAoCa,aAAA,SAAsB"}