embark-cli 1.1.7

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 (73) hide show
  1. package/.claude/CLAUDE.md +33 -0
  2. package/.claude/settings.local.json +32 -0
  3. package/.github/WORKFLOWS.md +147 -0
  4. package/.github/workflows/ci.yml +49 -0
  5. package/.github/workflows/publish.yml +109 -0
  6. package/.idea/embark-remote-mcp.iml +9 -0
  7. package/.idea/encodings.xml +4 -0
  8. package/.idea/indexLayout.xml +8 -0
  9. package/.idea/vcs.xml +6 -0
  10. package/.mcp.json +14 -0
  11. package/GIT_DISCOVERY.md +231 -0
  12. package/INTEGRATION_TESTING.md +243 -0
  13. package/MULTI_REPOSITORY_SEARCH.md +242 -0
  14. package/README.md +434 -0
  15. package/dist/auth/auth-helper.d.ts +3 -0
  16. package/dist/auth/auth-helper.d.ts.map +1 -0
  17. package/dist/auth/auth-helper.js +171 -0
  18. package/dist/auth/auth-helper.js.map +1 -0
  19. package/dist/auth/index.d.ts +4 -0
  20. package/dist/auth/index.d.ts.map +1 -0
  21. package/dist/auth/index.js +24 -0
  22. package/dist/auth/index.js.map +1 -0
  23. package/dist/auth/jba-login.d.ts +17 -0
  24. package/dist/auth/jba-login.d.ts.map +1 -0
  25. package/dist/auth/jba-login.js +345 -0
  26. package/dist/auth/jba-login.js.map +1 -0
  27. package/dist/auth/types.d.ts +16 -0
  28. package/dist/auth/types.d.ts.map +1 -0
  29. package/dist/auth/types.js +3 -0
  30. package/dist/auth/types.js.map +1 -0
  31. package/dist/config.d.ts +26 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +54 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/embark-client.d.ts +56 -0
  36. package/dist/embark-client.d.ts.map +1 -0
  37. package/dist/embark-client.js +543 -0
  38. package/dist/embark-client.js.map +1 -0
  39. package/dist/git-utils.d.ts +47 -0
  40. package/dist/git-utils.d.ts.map +1 -0
  41. package/dist/git-utils.js +232 -0
  42. package/dist/git-utils.js.map +1 -0
  43. package/dist/handlers.d.ts +80 -0
  44. package/dist/handlers.d.ts.map +1 -0
  45. package/dist/handlers.js +301 -0
  46. package/dist/handlers.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +165 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/logger.d.ts +4 -0
  52. package/dist/logger.d.ts.map +1 -0
  53. package/dist/logger.js +92 -0
  54. package/dist/logger.js.map +1 -0
  55. package/dist/stats-server.d.ts +3 -0
  56. package/dist/stats-server.d.ts.map +1 -0
  57. package/dist/stats-server.js +623 -0
  58. package/dist/stats-server.js.map +1 -0
  59. package/dist/stats.d.ts +118 -0
  60. package/dist/stats.d.ts.map +1 -0
  61. package/dist/stats.js +206 -0
  62. package/dist/stats.js.map +1 -0
  63. package/dist/tools.d.ts +9 -0
  64. package/dist/tools.d.ts.map +1 -0
  65. package/dist/tools.js +62 -0
  66. package/dist/tools.js.map +1 -0
  67. package/package.json +47 -0
  68. package/test-git-discovery.mjs +322 -0
  69. package/test-multi-repo-filters.mjs +151 -0
  70. package/test-multiple-roots.mjs +436 -0
  71. package/test-roots.mjs +306 -0
  72. package/test-snippet-extraction.mjs +136 -0
  73. package/watch-logs.sh +78 -0
package/README.md ADDED
@@ -0,0 +1,434 @@
1
+ # Embark MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that provides a proxy interface to Embark's semantic code search capabilities. This server enables LLM applications to search through code repositories using Embark's powerful indexing and similarity search.
4
+
5
+ ## Features
6
+
7
+ - **Semantic Code Search**: Search for code using natural language queries through Embark's semantic search engine
8
+ - **Multi-Repository Search**: Search across multiple Git repositories simultaneously with filtering options
9
+ - **Dependency-Based Search**: Search for code that uses specific dependencies and libraries
10
+ - **MCP Protocol Compliance**: Fully compatible with the Model Context Protocol standard
11
+ - **JetBrains Account Authentication**: Secure authentication with Embark's API using JetBrains Account OAuth
12
+ - **Configurable Repositories**: Search across different code repositories with include/exclude filters
13
+ - **Fallback JWT Support**: Supports `GRAZIE_JWT_TOKEN` for non-interactive environments
14
+
15
+ ## Installation
16
+
17
+ 1. Clone the repository:
18
+ ```bash
19
+ git clone <repository-url>
20
+ cd embark-mcp
21
+ ```
22
+
23
+ 2. Install dependencies:
24
+ ```bash
25
+ npm install
26
+ ```
27
+
28
+ 3. Build the project:
29
+ ```bash
30
+ npm run build
31
+ ```
32
+
33
+ ## Configuration
34
+
35
+ ### Authentication
36
+
37
+ This server uses JetBrains Account OAuth for authentication by default.
38
+
39
+ #### First-Time Authorization
40
+
41
+ The OAuth flow is triggered when you first use a search tool. To manually trigger the authorization before using it with Claude Desktop:
42
+
43
+ 1. Start the server: `npx embark-mcp`
44
+ 2. In another terminal, trigger a search to start the OAuth flow:
45
+ ```bash
46
+ echo '{"method": "tools/call", "params": {"name": "semantic_code_search", "arguments": {"text": "test"}}}' | npx embark-mcp
47
+ ```
48
+ 3. The server will automatically open a browser window to `https://account.jetbrains.com`
49
+ 4. Log in with your JetBrains Account credentials
50
+ 5. Review and accept the authorization request for Embark access
51
+ 6. The browser will redirect to a success page showing "Authorization successful, return to the terminal"
52
+ 7. Return to your terminal - the server should now be authenticated
53
+
54
+ The authorization tokens are securely saved in `~/.jbaccount` for future sessions, so you only need to do this once.
55
+
56
+ ### Environment Variables
57
+
58
+ #### Authentication
59
+ - `GRAZIE_JWT_TOKEN` (optional): JWT authentication token for Embark API. If provided, it will be used instead of the OAuth flow. This is useful for non-interactive environments.
60
+ - `JETBRAINS_AI_URL` (optional): Base URL for JetBrains AI API (defaults to `https://api.jetbrains.ai`, can be set to `https://api.stgn.jetbrains.ai/` for staging).
61
+
62
+ #### Repository Configuration
63
+ - `REPOSITORY_GIT_REMOTE_URL` (optional): Default repository Git remote URL to search in (can be overridden per search request)
64
+ - `REPOSITORY_ID` (optional): Alternative way to specify default repository (fallback for `REPOSITORY_GIT_REMOTE_URL`)
65
+ - `REPOSITORY_REVISION` (optional): Default repository revision (commit hash, branch name, or tag) to search in. If not set, searches the latest indexed version.
66
+
67
+ #### Multi-Repository Filtering
68
+ - `INCLUDE_REPOSITORY_URLS` (optional): Comma-separated list of Git repository URLs to add to searches. These repositories are searched in addition to any discovered from workspace roots. Useful when your working directory does not contain all repos you want to search. Example: `"https://github.com/owner/repo1.git,https://github.com/owner/repo2.git"`
69
+ - `EXCLUDE_REPOSITORY_URLS` (optional): Comma-separated list of Git repository URLs to exclude from searches. When set, these repositories will be skipped. Example: `"https://github.com/owner/large-repo.git"`
70
+
71
+ #### Other Options
72
+ - `TYPE_TOKEN` (optional): Endpoint type token, either `USER` (default) or `APPLICATION`. When set to `APPLICATION`, the server uses `/application/*` endpoints instead of `/user/*` endpoints.
73
+ - `ENABLE_REMOTE_LOGS` (optional): When set to `true`, includes `logAllowed=true` parameter in Embark API requests to enable remote logging for debugging purposes. Defaults to `false`.
74
+
75
+ **Note**: See [MULTI_REPOSITORY_SEARCH.md](MULTI_REPOSITORY_SEARCH.md) for detailed documentation on multi-repository search and filtering.
76
+
77
+ ### Setting up Fallback Authentication (Optional)
78
+
79
+ If you need to use the server in a non-interactive environment, you can use a JWT token.
80
+
81
+ 1. Obtain a JWT token from your Embark service administrator
82
+ 2. Set the token as an environment variable:
83
+ ```bash
84
+ export GRAZIE_JWT_TOKEN="your-jwt-token-here"
85
+ export REPOSITORY_GIT_REMOTE_URL="https://github.com/owner/repo.git" # optional default repository
86
+ ```
87
+
88
+ ## Usage
89
+
90
+ ### Running the Server
91
+
92
+ #### Using npx (Recommended)
93
+
94
+ You can run the server directly using `npx` without cloning the repository:
95
+
96
+ ```bash
97
+ # Optional: set a default repository
98
+ export REPOSITORY_GIT_REMOTE_URL="https://github.com/owner/repo.git"
99
+
100
+ # Run the server and follow the on-screen instructions for OAuth login
101
+ npx embark-mcp
102
+ ```
103
+
104
+ If you are using fallback JWT authentication:
105
+ ```bash
106
+ # Set your authentication token
107
+ export GRAZIE_JWT_TOKEN="your-jwt-token-here"
108
+ export REPOSITORY_GIT_REMOTE_URL="https://github.com/owner/repo.git" # optional
109
+
110
+ # Run the server
111
+ npx embark-mcp
112
+ ```
113
+
114
+ #### Running from Source
115
+
116
+ Start the MCP server:
117
+ ```bash
118
+ npm start
119
+ ```
120
+
121
+ Or for development with auto-reload:
122
+ ```bash
123
+ npm run dev
124
+ ```
125
+
126
+ ### Integrating with Claude Desktop
127
+
128
+ Add the server to your Claude Desktop configuration file:
129
+
130
+ **macOS/Linux**: `~/.config/claude/claude_desktop_config.json`
131
+ **Windows**: `%APPDATA%/Claude/claude_desktop_config.json`
132
+
133
+ #### Using npx (Recommended)
134
+
135
+ ```json
136
+ {
137
+ "mcpServers": {
138
+ "embark-mcp": {
139
+ "command": "npx",
140
+ "args": ["embark-mcp"],
141
+ "env": {
142
+ "JETBRAINS_AI_URL": "https://api.jetbrains.ai",
143
+ "REPOSITORY_GIT_REMOTE_URL": "https://github.com/owner/repo.git",
144
+ "REPOSITORY_REVISION": "main",
145
+ "TYPE_TOKEN": "USER",
146
+ "ENABLE_LOCAL_LOGS": true,
147
+ "ENABLE_REMOTE_LOGS": "false"
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+ **Note**: For the OAuth flow to work with Claude Desktop, you must complete the initial authorization process first. Follow the steps in the "First-Time Authorization" section above to trigger the OAuth flow and save the tokens before Claude Desktop attempts to use the server.
154
+
155
+ #### Using Local Installation
156
+
157
+ ```json
158
+ {
159
+ "mcpServers": {
160
+ "embark-mcp": {
161
+ "command": "node",
162
+ "args": ["/path/to/embark-mcp/dist/index.js"],
163
+ "env": {
164
+ "REPOSITORY_GIT_REMOTE_URL": "https://github.com/owner/repo.git"
165
+ }
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Available Tools
172
+
173
+ #### `semantic_code_search`
174
+
175
+ Search for code using Embark's semantic search engine.
176
+
177
+ **Parameters:**
178
+ - `text` (required): The text/code to search for. Use detailed, descriptive natural language queries for best results. Examples: "function that validates user email addresses and returns boolean", "error handling middleware for HTTP requests with logging", "React component that renders a modal dialog with close button".
179
+ - `pathFilter` (optional): A specific directory or file to narrow the search. If not provided, the whole codebase is searched. Examples: "path/to/module", "path/to/module/submodule", "path/to/file/example.kt".
180
+ - `repositoryGitRemoteUrl` (optional): The repository Git remote URL to search in (defaults to `REPOSITORY_GIT_REMOTE_URL` environment variable)
181
+
182
+ **Example:**
183
+ ```
184
+ {'text': 'Search for "authentication middleware" in the codebase', 'pathFilter': 'src'}
185
+ ```
186
+
187
+ **Response Format:**
188
+ ```
189
+ Found 10 results for "authentication middleware" in repository "https://github.com/owner/repo.git" (revision: "5e7f1ab4bf58e473e5d7f878eb2b499d7deabd29", pathFilter: "src")
190
+
191
+ 1. File=src/middleware/auth.js, offset=120:340, similarity=0.892, type=FUNCTION
192
+ Snippet:
193
+ """
194
+ export function authMiddleware(req, res, next) {
195
+ const token = req.headers['authorization'];
196
+ if (!token) return res.status(401).send('Missing token');
197
+ try {
198
+ req.user = verifyToken(token);
199
+ next();
200
+ } catch (err) {
201
+ res.status(403).send('Invalid token');
202
+ }
203
+ }
204
+ """
205
+ 2. File=src/security/middleware.ts, offset=45:180, similarity=0.834, type=CLASS
206
+ Snippet:
207
+ """
208
+ export class SecurityMiddleware {
209
+ constructor(private readonly secret: string) {}
210
+ handle(req: Request, res: Response, next: NextFunction) {
211
+ if (!req.headers['x-api-key']) {
212
+ res.status(401).json({ error: 'Unauthorized' });
213
+ } else {
214
+ next();
215
+ }
216
+ }
217
+ }
218
+ """
219
+ 3. File=src/routes/auth.js, offset=890:1120, similarity=0.776, type=FUNCTION
220
+ Snippet:
221
+ """
222
+ router.post('/login', async (req, res) => {
223
+ const { username, password } = req.body;
224
+ const token = await authenticate(username, password);
225
+ res.json({ token });
226
+ });
227
+ """
228
+ ...
229
+ ```
230
+
231
+ #### `search_in_dependencies`
232
+
233
+ Search for code that uses specific dependencies using Embark's semantic search.
234
+
235
+ **Parameters:**
236
+ - `text` (required): The search query describing what to look for in relation to dependencies
237
+ - `dependencies` (required): Array of dependency objects with the following structure:
238
+ - `dependency` (required): The dependency name
239
+ - `version` (required): The dependency version
240
+
241
+ **Example:**
242
+ ```
243
+ Search for code that uses React hooks with specific dependencies
244
+ ```
245
+
246
+ **Dependencies Parameter Example:**
247
+ ```json
248
+ [
249
+ {"dependency": "react", "version": "18.0.0"},
250
+ {"dependency": "react-hooks", "version": "1.0.0"}
251
+ ]
252
+ ```
253
+
254
+ **Response Format:**
255
+ ```
256
+ Found 3 results for "React hooks usage" with dependencies [react:18.0.0, react-hooks:1.0.0] in repository "https://github.com/owner/repo.git":
257
+
258
+ 1. File=src/components/UserProfile.tsx, offset=45:180, similarity=0.891, type=FUNCTION
259
+ 2. File=src/hooks/useAuth.ts, offset=12:95, similarity=0.834, type=FUNCTION
260
+ 3. File=src/pages/Dashboard.tsx, offset=200:350, similarity=0.776, type=FUNCTION
261
+
262
+ Timings: search: 120ms, total: 150ms
263
+ ```
264
+
265
+ ## API Reference
266
+
267
+ ### Embark API Integration
268
+
269
+ This server integrates with Embark's REST API endpoints:
270
+
271
+ #### Semantic Code Search
272
+ - **Endpoint**: `/user/v5/indexing/search` (or `/application/v5/indexing/search` when `TYPE_TOKEN=APPLICATION`)
273
+ - **Method**: POST
274
+ - **Authentication**: Bearer token from JetBrains Account OAuth or `GRAZIE_JWT_TOKEN` via `grazie-authenticate-jwt` header
275
+ - **Request Body**:
276
+ ```json
277
+ {
278
+ "text": "search query",
279
+ "repository": "https://github.com/owner/repo.git",
280
+ "revision": "main", // optional: commit hash, branch name, or tag
281
+ "pathFilter": "dir" // optional: a file or a dir
282
+ }
283
+ ```
284
+
285
+ #### Dependencies Search
286
+ - **Endpoint**: `/search-dependencies`
287
+ - **Method**: POST
288
+ - **Authentication**: Bearer token from JetBrains Account OAuth or `GRAZIE_JWT_TOKEN` via `grazie-authenticate-jwt` header
289
+ - **Request Body**:
290
+ ```json
291
+ {
292
+ "index": "ProductionIndices.CodeBlocks",
293
+ "text": "search query",
294
+ "dependencies": [
295
+ {"dependency": "react", "version": "18.0.0"}
296
+ ],
297
+ "maxResults": 10,
298
+ "minScore": 0.0,
299
+ "logAllowed": false,
300
+ "searchPipelineConfig": "SearchPipelineConfig.SearchOnly"
301
+ }
302
+ ```
303
+
304
+ ### Response Structure
305
+
306
+ #### Semantic Code Search Response
307
+
308
+ Embark returns search results with the following structure:
309
+
310
+ ```typescript
311
+ interface SearchResponse {
312
+ searchResponse: {
313
+ res: Array<{
314
+ scoredText: {
315
+ text: string;
316
+ similarity: number;
317
+ };
318
+ sourcePosition: {
319
+ relativePath: string;
320
+ startOffset: number;
321
+ endOffset: number;
322
+ };
323
+ indexItemType: string;
324
+ }>;
325
+ };
326
+ }
327
+ ```
328
+
329
+ #### Dependencies Search Response
330
+
331
+ The dependencies search endpoint returns results with timing information:
332
+
333
+ ```typescript
334
+ interface DependenciesSearchResponse {
335
+ results: Array<{
336
+ searchResult: {
337
+ sourcePosition: {
338
+ relativePath: string;
339
+ startOffset: number;
340
+ endOffset: number;
341
+ };
342
+ indexItemType: string;
343
+ similarity: number;
344
+ };
345
+ content: string;
346
+ }>;
347
+ timings: {
348
+ [key: string]: number; // timing in milliseconds
349
+ };
350
+ }
351
+ ```
352
+
353
+ ## Development
354
+
355
+ ### Building
356
+
357
+ ```bash
358
+ npm run build
359
+ ```
360
+
361
+ ### Development Mode
362
+
363
+ ```bash
364
+ npm run dev
365
+ ```
366
+
367
+ ### Watch Mode
368
+
369
+ ```bash
370
+ npm run watch
371
+ ```
372
+
373
+ ## Troubleshooting
374
+
375
+ ### Common Issues
376
+
377
+ 1. **OAuth Error**:
378
+ - Ensure your browser is available to complete the login flow.
379
+ - If behind a firewall, ensure that `https://www.jetbrains.com` and `http://localhost:62345` (or a nearby port) are accessible.
380
+ - If the browser doesn't open automatically, copy the URL from the terminal and open it manually.
381
+ 2. **Authentication Error (JWT)**: Ensure your `GRAZIE_JWT_TOKEN` is valid and not expired. This is only relevant if you are using fallback authentication.
382
+ 3. **Connection Error**: Check that the `JETBRAINS_AI_URL` is correct and accessible.
383
+ 4. **No Results**: Verify the repository name exists and is accessible with your token.
384
+
385
+ ### Error Messages
386
+
387
+ - `Failed to get authorization code`: The OAuth flow was not completed successfully.
388
+ - `Embark API error (401)`: Invalid or expired token (either from OAuth or JWT).
389
+ - `Embark API error (404)`: Repository not found or not accessible.
390
+
391
+ ## License
392
+
393
+ MIT
394
+
395
+ ## Contributing
396
+
397
+ 1. Fork the repository
398
+ 2. Create a feature branch
399
+ 3. Make your changes
400
+ 4. Submit a pull request
401
+
402
+ ## Support
403
+
404
+ For issues related to:
405
+ - **This MCP server**: Open an issue in this repository
406
+ - **Embark API**: Contact your Embark service administrator
407
+ - **Model Context Protocol**: See the [official MCP documentation](https://modelcontextprotocol.io/)
408
+ ## Usage Analytics & Dashboard
409
+
410
+ The MCP server records structured JSON stats for each server initialization, Embark API call, and search invocation. Files are grouped per calendar day and stored under `~/.embark/stats/YYYY-MM-DD.json`, making it easy to audit historical usage or inspect failure cases.
411
+
412
+ ### Launching the Local Dashboard
413
+
414
+ For a one-liner that starts the dashboard server and opens it in your default browser:
415
+
416
+ ```bash
417
+ npx embark-mcp stats --port 43210
418
+ ```
419
+
420
+ If you prefer to start the server without opening a browser automatically, use:
421
+
422
+ ```bash
423
+ npx embark-mcp stats-server --port 43210
424
+ ```
425
+
426
+ The dashboard renders:
427
+
428
+ - Searches per day (semantic, dependency, multi-repo summaries)
429
+ - Estimated token usage per day
430
+ - Average response times
431
+ - Counts of error HTTP status codes
432
+ - A rolling feed of recent events for quick debugging
433
+
434
+ Data refreshes automatically every 15 seconds and never leaves your machine because everything is served directly from the local `~/.embark/stats` directory.
@@ -0,0 +1,3 @@
1
+ export declare function getAuthToken(forceRefresh?: boolean): Promise<string>;
2
+ export declare function areJbaCredentialsAvailable(): boolean;
3
+ //# sourceMappingURL=auth-helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-helper.d.ts","sourceRoot":"","sources":["../../src/auth/auth-helper.ts"],"names":[],"mappings":"AA0IA,wBAAsB,YAAY,CAAC,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA2CjF;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAWpD"}
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getAuthToken = getAuthToken;
7
+ exports.areJbaCredentialsAvailable = areJbaCredentialsAvailable;
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const jba_login_js_1 = require("./jba-login.js");
10
+ const logger_js_1 = require("../logger.js");
11
+ const os_1 = require("os");
12
+ const path_1 = require("path");
13
+ const fs_1 = require("fs");
14
+ const GRAZIE_JWT_TOKEN = process.env.GRAZIE_JWT_TOKEN;
15
+ const JETBRAINS_AI_URL = process.env.JETBRAINS_AI_URL || 'https://api.jetbrains.ai';
16
+ const EMBARK_DIR = (0, path_1.join)((0, os_1.homedir)(), '.embark');
17
+ const LICENSES_CACHE_PATH = (0, path_1.join)(EMBARK_DIR, 'licenses.json');
18
+ function readLicensesFromCache() {
19
+ if ((0, fs_1.existsSync)(LICENSES_CACHE_PATH)) {
20
+ try {
21
+ const data = (0, fs_1.readFileSync)(LICENSES_CACHE_PATH, 'utf-8');
22
+ const licenses = JSON.parse(data);
23
+ (0, logger_js_1.logToFile)('info', '[AUTH] Licenses loaded from cache.');
24
+ return licenses;
25
+ }
26
+ catch (error) {
27
+ (0, logger_js_1.logToFile)('error', '[AUTH] Failed to read or parse licenses cache.', { error });
28
+ return null;
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ function writeLicensesToCache(licenses) {
34
+ try {
35
+ if (!(0, fs_1.existsSync)(EMBARK_DIR)) {
36
+ (0, fs_1.mkdirSync)(EMBARK_DIR, { recursive: true });
37
+ }
38
+ (0, fs_1.writeFileSync)(LICENSES_CACHE_PATH, JSON.stringify(licenses, null, 2));
39
+ (0, logger_js_1.logToFile)('info', '[AUTH] Licenses saved to cache.');
40
+ }
41
+ catch (error) {
42
+ (0, logger_js_1.logToFile)('error', '[AUTH] Failed to write licenses to cache.', { error });
43
+ }
44
+ }
45
+ async function fetchLicenses(jbaToken) {
46
+ (0, logger_js_1.logToFile)('info', '[AUTH] Fetching licenses from JetBrains Account');
47
+ try {
48
+ const response = await axios_1.default.get('https://account.jetbrains.com/services/account/assetsByIdToken', {
49
+ headers: {
50
+ 'Authorization': `Bearer ${jbaToken}`
51
+ }
52
+ });
53
+ const assets = response.data.assets;
54
+ if (!assets || assets.length === 0) {
55
+ (0, logger_js_1.logToFile)('warn', '[AUTH] No assets found for the user.');
56
+ throw new Error('No assets found for your JetBrains Account.');
57
+ }
58
+ (0, logger_js_1.logToFile)('info', `[AUTH] Found ${assets.length} assets.`);
59
+ writeLicensesToCache(assets);
60
+ return assets;
61
+ }
62
+ catch (error) {
63
+ (0, logger_js_1.logToFile)('error', '[AUTH] Failed to fetch licenses', { error });
64
+ if (axios_1.default.isAxiosError(error)) {
65
+ const status = error.response?.status;
66
+ const statusText = error.response?.statusText;
67
+ const responseData = error.response?.data;
68
+ (0, logger_js_1.logToFile)('error', 'JetBrains Account API error details', { status, statusText, responseData });
69
+ throw new Error(`JetBrains Account API error for licenses (${status}): ${statusText} - ${JSON.stringify(responseData)}`);
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+ async function getLicenseId(jbaLogin) {
75
+ const idToken = await jbaLogin.getValidIdToken();
76
+ let licenses = readLicensesFromCache();
77
+ if (!licenses) {
78
+ licenses = await fetchLicenses(idToken);
79
+ }
80
+ const validLicense = licenses.find(l => !l.suspended && !l.cancelled);
81
+ if (!validLicense || !validLicense.licenseId) {
82
+ throw new Error('No valid license found.');
83
+ }
84
+ (0, logger_js_1.logToFile)('info', '[AUTH] Selected license', { licenseId: validLicense.licenseId });
85
+ return validLicense.licenseId;
86
+ }
87
+ async function exchangeJbaForGrazieToken(jbaToken, licenseId) {
88
+ (0, logger_js_1.logToFile)('info', '[AUTH] Exchanging JBA token for Grazie token', { licenseId });
89
+ try {
90
+ const response = await axios_1.default.post(`${JETBRAINS_AI_URL}/auth/jetbrains-jwt/provide-access/license/v2`, {
91
+ licenseId: licenseId,
92
+ }, {
93
+ headers: {
94
+ 'Authorization': `Bearer ${jbaToken}`,
95
+ 'Content-Type': 'application/json',
96
+ },
97
+ });
98
+ const { token } = response.data;
99
+ if (!token) {
100
+ (0, logger_js_1.logToFile)('error', '[AUTH] Grazie token not found in response', { responseData: response.data });
101
+ throw new Error('Failed to get Grazie token');
102
+ }
103
+ (0, logger_js_1.logToFile)('info', '[AUTH] Successfully exchanged JBA token for Grazie token');
104
+ return token;
105
+ }
106
+ catch (error) {
107
+ (0, logger_js_1.logToFile)('error', '[AUTH] Failed to exchange JBA token for Grazie token', { error });
108
+ if (axios_1.default.isAxiosError(error)) {
109
+ const status = error.response?.status;
110
+ const statusText = error.response?.statusText;
111
+ const responseData = error.response?.data;
112
+ (0, logger_js_1.logToFile)('error', 'Embark API error details', { status, statusText, responseData });
113
+ throw new Error(`Embark API error for token exchange (${status}): ${statusText} - ${JSON.stringify(responseData)}`);
114
+ }
115
+ throw error;
116
+ }
117
+ }
118
+ async function getAuthToken(forceRefresh = false) {
119
+ (0, logger_js_1.logToFile)('info', '[AUTH] Getting auth token', { forceRefresh });
120
+ if (GRAZIE_JWT_TOKEN && !forceRefresh) {
121
+ (0, logger_js_1.logToFile)('info', '[AUTH] Using GRAZIE_JWT_TOKEN from environment');
122
+ return GRAZIE_JWT_TOKEN;
123
+ }
124
+ (0, logger_js_1.logToFile)('info', '[AUTH] Using JetBrains Account OAuth flow', { forceRefresh });
125
+ try {
126
+ const jbaLogin = new jba_login_js_1.JBALogin();
127
+ const licenseId = await getLicenseId(jbaLogin);
128
+ let idToken;
129
+ if (forceRefresh) {
130
+ (0, logger_js_1.logToFile)('info', '[AUTH] Force refreshing JBA token');
131
+ // Force refresh by using the refresh token
132
+ let account = jbaLogin.readAccount();
133
+ if (!account) {
134
+ (0, logger_js_1.logToFile)('info', '[AUTH] No account found for force refresh, getting new token');
135
+ account = await jbaLogin.login();
136
+ }
137
+ else {
138
+ (0, logger_js_1.logToFile)('info', '[AUTH] Forcing token refresh using refresh token');
139
+ account = await jbaLogin.refreshToken(account);
140
+ }
141
+ if (!account.idToken) {
142
+ throw new Error('No ID token found after forced refresh');
143
+ }
144
+ idToken = account.idToken;
145
+ }
146
+ else {
147
+ idToken = await jbaLogin.getValidIdToken();
148
+ }
149
+ (0, logger_js_1.logToFile)('info', '[AUTH] Using ID token to exchange for Grazie token');
150
+ const grazieToken = await exchangeJbaForGrazieToken(idToken, licenseId);
151
+ return grazieToken;
152
+ }
153
+ catch (error) {
154
+ (0, logger_js_1.logToFile)('error', '[AUTH] Failed to get auth token', { error, forceRefresh });
155
+ throw error;
156
+ }
157
+ }
158
+ function areJbaCredentialsAvailable() {
159
+ try {
160
+ const jbaLogin = new jba_login_js_1.JBALogin();
161
+ const account = jbaLogin.readAccount();
162
+ const available = account !== null;
163
+ (0, logger_js_1.logToFile)('info', `[AUTH] JBA credentials available: ${available}`);
164
+ return available;
165
+ }
166
+ catch (error) {
167
+ (0, logger_js_1.logToFile)('error', '[AUTH] Failed to check for JBA credentials', { error });
168
+ return false;
169
+ }
170
+ }
171
+ //# sourceMappingURL=auth-helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-helper.js","sourceRoot":"","sources":["../../src/auth/auth-helper.ts"],"names":[],"mappings":";;;;;AA0IA,oCA2CC;AAED,gEAWC;AAlMD,kDAA0B;AAC1B,iDAA0C;AAC1C,4CAAyC;AACzC,2BAA6B;AAC7B,+BAA4B;AAC5B,2BAAwE;AAExE,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AACtD,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,0BAA0B,CAAC;AAEpF,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,IAAA,YAAO,GAAE,EAAE,SAAS,CAAC,CAAC;AAC9C,MAAM,mBAAmB,GAAG,IAAA,WAAI,EAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAa9D,SAAS,qBAAqB;IAC1B,IAAI,IAAA,eAAU,EAAC,mBAAmB,CAAC,EAAE,CAAC;QAClC,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAA,iBAAY,EAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClC,IAAA,qBAAS,EAAC,MAAM,EAAE,oCAAoC,CAAC,CAAC;YACxD,OAAO,QAAQ,CAAC;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAA,qBAAS,EAAC,OAAO,EAAE,gDAAgD,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAChF,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAiB;IAC3C,IAAI,CAAC;QACD,IAAI,CAAC,IAAA,eAAU,EAAC,UAAU,CAAC,EAAE,CAAC;YAC1B,IAAA,cAAS,EAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,IAAA,kBAAa,EAAC,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACtE,IAAA,qBAAS,EAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAA,qBAAS,EAAC,OAAO,EAAE,2CAA2C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC;AACL,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,QAAgB;IACzC,IAAA,qBAAS,EAAC,MAAM,EAAE,iDAAiD,CAAC,CAAC;IACrE,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,eAAK,CAAC,GAAG,CAAiB,gEAAgE,EAAE;YAC/G,OAAO,EAAE;gBACL,eAAe,EAAE,UAAU,QAAQ,EAAE;aACxC;SACJ,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QACpC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,IAAA,qBAAS,EAAC,MAAM,EAAE,sCAAsC,CAAC,CAAC;YAC1D,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACnE,CAAC;QAED,IAAA,qBAAS,EAAC,MAAM,EAAE,gBAAgB,MAAM,CAAC,MAAM,UAAU,CAAC,CAAC;QAC3D,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC;IAElB,CAAC;IAAC,OAAM,KAAK,EAAE,CAAC;QACZ,IAAA,qBAAS,EAAC,OAAO,EAAE,iCAAiC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACjE,IAAI,eAAK,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC;YACtC,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC;YAC9C,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC;YAC1C,IAAA,qBAAS,EAAC,OAAO,EAAE,qCAAqC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;YAChG,MAAM,IAAI,KAAK,CAAC,6CAA6C,MAAM,MAAM,UAAU,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC7H,CAAC;QACD,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,QAAkB;IAC1C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,eAAe,EAAE,CAAC;IACjD,IAAI,QAAQ,GAAG,qBAAqB,EAAE,CAAC;IACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,QAAQ,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEtE,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC/C,CAAC;IAED,IAAA,qBAAS,EAAC,MAAM,EAAE,yBAAyB,EAAE,EAAE,SAAS,EAAE,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC;IACpF,OAAO,YAAY,CAAC,SAAS,CAAC;AAClC,CAAC;AAED,KAAK,UAAU,yBAAyB,CAAC,QAAgB,EAAE,SAAiB;IAC1E,IAAA,qBAAS,EAAC,MAAM,EAAE,8CAA8C,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IAEjF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,eAAK,CAAC,IAAI,CAC/B,GAAG,gBAAgB,+CAA+C,EAClE;YACE,SAAS,EAAE,SAAS;SACrB,EACD;YACE,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,QAAQ,EAAE;gBACrC,cAAc,EAAE,kBAAkB;aACnC;SACF,CACF,CAAC;QAEF,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAA,qBAAS,EAAC,OAAO,EAAE,2CAA2C,EAAE,EAAE,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YACjG,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QAED,IAAA,qBAAS,EAAC,MAAM,EAAE,0DAA0D,CAAC,CAAC;QAC9E,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,qBAAS,EAAC,OAAO,EAAE,sDAAsD,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACtF,IAAI,eAAK,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC;YACtC,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC;YAC9C,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC;YAC1C,IAAA,qBAAS,EAAC,OAAO,EAAE,0BAA0B,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;YACrF,MAAM,IAAI,KAAK,CAAC,wCAAwC,MAAM,MAAM,UAAU,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACxH,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,YAAY,CAAC,eAAwB,KAAK;IAC9D,IAAA,qBAAS,EAAC,MAAM,EAAE,2BAA2B,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IAEjE,IAAI,gBAAgB,IAAI,CAAC,YAAY,EAAE,CAAC;QACtC,IAAA,qBAAS,EAAC,MAAM,EAAE,gDAAgD,CAAC,CAAC;QACpE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,IAAA,qBAAS,EAAC,MAAM,EAAE,2CAA2C,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IACjF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,uBAAQ,EAAE,CAAC;QAEhC,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAE/C,IAAI,OAAe,CAAC;QACpB,IAAI,YAAY,EAAE,CAAC;YACjB,IAAA,qBAAS,EAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;YACvD,2CAA2C;YAC3C,IAAI,OAAO,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,IAAA,qBAAS,EAAC,MAAM,EAAE,8DAA8D,CAAC,CAAC;gBAClF,OAAO,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,IAAA,qBAAS,EAAC,MAAM,EAAE,kDAAkD,CAAC,CAAC;gBACtE,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YACjD,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC5D,CAAC;YACD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,MAAM,QAAQ,CAAC,eAAe,EAAE,CAAC;QAC7C,CAAC;QAED,IAAA,qBAAS,EAAC,MAAM,EAAE,oDAAoD,CAAC,CAAC;QACxE,MAAM,WAAW,GAAG,MAAM,yBAAyB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAExE,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,qBAAS,EAAC,OAAO,EAAE,iCAAiC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAC/E,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAgB,0BAA0B;IACxC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,uBAAQ,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,OAAO,KAAK,IAAI,CAAC;QACnC,IAAA,qBAAS,EAAC,MAAM,EAAE,qCAAqC,SAAS,EAAE,CAAC,CAAC;QACpE,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,qBAAS,EAAC,OAAO,EAAE,4CAA4C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export { getAuthToken, areJbaCredentialsAvailable } from './auth-helper.js';
3
+ export { JBALogin } from './jba-login.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.JBALogin = exports.areJbaCredentialsAvailable = exports.getAuthToken = void 0;
18
+ __exportStar(require("./types.js"), exports);
19
+ var auth_helper_js_1 = require("./auth-helper.js");
20
+ Object.defineProperty(exports, "getAuthToken", { enumerable: true, get: function () { return auth_helper_js_1.getAuthToken; } });
21
+ Object.defineProperty(exports, "areJbaCredentialsAvailable", { enumerable: true, get: function () { return auth_helper_js_1.areJbaCredentialsAvailable; } });
22
+ var jba_login_js_1 = require("./jba-login.js");
23
+ Object.defineProperty(exports, "JBALogin", { enumerable: true, get: function () { return jba_login_js_1.JBALogin; } });
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,mDAA4E;AAAnE,8GAAA,YAAY,OAAA;AAAE,4HAAA,0BAA0B,OAAA;AACjD,+CAA0C;AAAjC,wGAAA,QAAQ,OAAA"}
@@ -0,0 +1,17 @@
1
+ import { JbAccount, OAuthConfig } from './types.js';
2
+ export declare class JBALogin {
3
+ private readonly accountFilePath;
4
+ constructor();
5
+ loadConfig(): Promise<OAuthConfig>;
6
+ private sendPostRequest;
7
+ private openBrowser;
8
+ login(): Promise<JbAccount>;
9
+ private exchangeCodeForToken;
10
+ refreshToken(account: JbAccount): Promise<JbAccount>;
11
+ saveAccount(account: JbAccount): void;
12
+ readAccount(): JbAccount | null;
13
+ isTokenExpired(account: JbAccount): boolean;
14
+ getValidToken(): Promise<string>;
15
+ getValidIdToken(): Promise<string>;
16
+ }
17
+ //# sourceMappingURL=jba-login.d.ts.map