githits 0.1.11 → 0.2.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.
@@ -0,0 +1,4303 @@
1
+ import {
2
+ version
3
+ } from "./chunk-fa4571ch.js";
4
+
5
+ // src/services/auth-service.ts
6
+ import { createServer } from "node:http";
7
+
8
+ // src/auth/pkce.ts
9
+ import { createHash, randomBytes } from "node:crypto";
10
+ function generateCodeVerifier() {
11
+ return randomBytes(32).toString("base64url");
12
+ }
13
+ function generateCodeChallenge(verifier) {
14
+ return createHash("sha256").update(verifier).digest("base64url");
15
+ }
16
+ function generateState() {
17
+ return randomBytes(32).toString("hex");
18
+ }
19
+
20
+ // src/services/auth-service.ts
21
+ class AuthServiceImpl {
22
+ async discoverEndpoints(mcpBaseUrl) {
23
+ const url = `${mcpBaseUrl}/.well-known/oauth-authorization-server`;
24
+ const response = await fetch(url);
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to discover OAuth endpoints: ${response.status} ${response.statusText}`);
27
+ }
28
+ const data = await response.json();
29
+ const authorizationEndpoint = data.authorization_endpoint;
30
+ const tokenEndpoint = data.token_endpoint;
31
+ const registrationEndpoint = data.registration_endpoint;
32
+ if (!authorizationEndpoint || !tokenEndpoint || !registrationEndpoint) {
33
+ throw new Error("OAuth metadata missing required endpoints");
34
+ }
35
+ return {
36
+ authorizationEndpoint,
37
+ tokenEndpoint,
38
+ registrationEndpoint
39
+ };
40
+ }
41
+ async registerClient(params) {
42
+ const response = await fetch(params.registrationEndpoint, {
43
+ method: "POST",
44
+ headers: { "Content-Type": "application/json" },
45
+ body: JSON.stringify({
46
+ client_name: "GitHits CLI",
47
+ redirect_uris: [params.redirectUri],
48
+ grant_types: ["authorization_code", "refresh_token"],
49
+ response_types: ["code"],
50
+ token_endpoint_auth_method: "client_secret_post"
51
+ })
52
+ });
53
+ if (!response.ok) {
54
+ const error = await response.text();
55
+ throw new Error(`Client registration failed: ${error}`);
56
+ }
57
+ const data = await response.json();
58
+ if (!data.client_id || !data.client_secret) {
59
+ throw new Error("Client registration response missing required fields");
60
+ }
61
+ return {
62
+ clientId: data.client_id,
63
+ clientSecret: data.client_secret
64
+ };
65
+ }
66
+ generatePkceParams() {
67
+ const verifier = generateCodeVerifier();
68
+ return {
69
+ verifier,
70
+ challenge: generateCodeChallenge(verifier),
71
+ state: generateState()
72
+ };
73
+ }
74
+ buildAuthUrl(params) {
75
+ const url = new URL(params.authorizationEndpoint);
76
+ url.searchParams.set("response_type", "code");
77
+ url.searchParams.set("client_id", params.clientId);
78
+ url.searchParams.set("redirect_uri", params.redirectUri);
79
+ url.searchParams.set("state", params.state);
80
+ url.searchParams.set("code_challenge", params.codeChallenge);
81
+ url.searchParams.set("code_challenge_method", "S256");
82
+ return url.toString();
83
+ }
84
+ startCallbackServer(port, expectedState) {
85
+ return new Promise((resolve, reject) => {
86
+ let callbackHandled = false;
87
+ let resolved = false;
88
+ let closeTimer;
89
+ const server = createServer((req, res) => {
90
+ const url = new URL(req.url ?? "", `http://127.0.0.1:${port}`);
91
+ if (url.pathname === "/favicon.ico") {
92
+ res.writeHead(204);
93
+ res.end();
94
+ return;
95
+ }
96
+ if (url.pathname !== "/callback") {
97
+ if (callbackHandled) {
98
+ sendHtmlResponse(res, 200, successHtml("Authentication already completed."));
99
+ return;
100
+ }
101
+ sendHtmlResponse(res, 404, errorHtml("Invalid callback path.", "Run `githits login` to start authentication."));
102
+ return;
103
+ }
104
+ const code = url.searchParams.get("code");
105
+ const state = url.searchParams.get("state");
106
+ const error = url.searchParams.get("error");
107
+ const errorDescription = url.searchParams.get("error_description");
108
+ const evaluation = evaluateCallback({
109
+ code,
110
+ state,
111
+ error,
112
+ errorDescription,
113
+ expectedState
114
+ });
115
+ callbackHandled = true;
116
+ sendHtmlResponse(res, evaluation.statusCode, evaluation.html);
117
+ if (!resolved) {
118
+ resolved = true;
119
+ resolve(evaluation.result);
120
+ }
121
+ if (closeTimer)
122
+ clearTimeout(closeTimer);
123
+ closeTimer = setTimeout(() => closeServer(server), 1500);
124
+ });
125
+ server.listen(port, "127.0.0.1");
126
+ server.on("error", (err) => {
127
+ reject(new Error(`Failed to start callback server: ${err.message}`));
128
+ });
129
+ });
130
+ }
131
+ async exchangeCodeForTokens(params) {
132
+ const body = new URLSearchParams({
133
+ grant_type: "authorization_code",
134
+ client_id: params.clientId,
135
+ client_secret: params.clientSecret,
136
+ code: params.code,
137
+ code_verifier: params.codeVerifier,
138
+ redirect_uri: params.redirectUri
139
+ });
140
+ const response = await fetch(params.tokenEndpoint, {
141
+ method: "POST",
142
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
143
+ body: body.toString()
144
+ });
145
+ if (!response.ok) {
146
+ const error = await response.text();
147
+ throw new Error(`Token exchange failed: ${error}`);
148
+ }
149
+ return parseTokenResponse(await response.json());
150
+ }
151
+ async refreshAccessToken(params) {
152
+ const body = new URLSearchParams({
153
+ grant_type: "refresh_token",
154
+ client_id: params.clientId,
155
+ client_secret: params.clientSecret,
156
+ refresh_token: params.refreshToken
157
+ });
158
+ const response = await fetch(params.tokenEndpoint, {
159
+ method: "POST",
160
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
161
+ body: body.toString()
162
+ });
163
+ if (!response.ok) {
164
+ const error = await response.text();
165
+ throw new Error(`Token refresh failed: ${error}`);
166
+ }
167
+ return parseTokenResponse(await response.json());
168
+ }
169
+ }
170
+ function parseTokenResponse(data) {
171
+ const d = data;
172
+ if (!d.access_token || !d.refresh_token) {
173
+ throw new Error("Token response missing required fields");
174
+ }
175
+ return {
176
+ accessToken: d.access_token,
177
+ refreshToken: d.refresh_token,
178
+ expiresIn: d.expires_in || 3600
179
+ };
180
+ }
181
+ function successHtml(title = "Authentication successful") {
182
+ return `<!DOCTYPE html>
183
+ <html><head><title>GitHits CLI</title>
184
+ <style>
185
+ body {
186
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
187
+ display: flex;
188
+ justify-content: center;
189
+ align-items: center;
190
+ height: 100vh;
191
+ margin: 0;
192
+ background: radial-gradient(circle at center center, #4d3648, #3a2835, #261a22, #0d1117);
193
+ }
194
+ .card {
195
+ text-align: center;
196
+ background: rgba(13, 17, 23, 0.75);
197
+ padding: 3rem;
198
+ border-radius: 16px;
199
+ border: 1px solid rgba(244, 11, 166, 0.35);
200
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
201
+ max-width: 720px;
202
+ }
203
+ h1 {
204
+ color: #f40ba6;
205
+ margin-bottom: 0.75rem;
206
+ font-size: 3rem;
207
+ font-weight: 700;
208
+ }
209
+ p {
210
+ color: #f385a5;
211
+ font-size: 1.1rem;
212
+ margin: 0;
213
+ }
214
+ </style>
215
+ </head>
216
+ <body>
217
+ <div class="card">
218
+ <h1>${escapeHtml(title)}</h1>
219
+ <p>You can close this window and return to the terminal.</p>
220
+ </div>
221
+ </body></html>`;
222
+ }
223
+ function evaluateCallback(input) {
224
+ if (input.error) {
225
+ const message = input.errorDescription ? `${input.error}: ${input.errorDescription}` : input.error;
226
+ return {
227
+ statusCode: 200,
228
+ html: errorHtml(message, "Run `githits login` to try again."),
229
+ result: { type: "oauth_error", message }
230
+ };
231
+ }
232
+ if (input.code && input.state) {
233
+ if (input.state !== input.expectedState) {
234
+ return {
235
+ statusCode: 400,
236
+ html: errorHtml("Authentication failed security validation (state mismatch)", "Run `githits login` to try again."),
237
+ result: {
238
+ type: "state_mismatch",
239
+ message: "Security validation failed (state mismatch)"
240
+ }
241
+ };
242
+ }
243
+ return {
244
+ statusCode: 200,
245
+ html: successHtml(),
246
+ result: { type: "success", code: input.code, state: input.state }
247
+ };
248
+ }
249
+ return {
250
+ statusCode: 400,
251
+ html: errorHtml("Authentication callback was missing required parameters", "Run `githits login` to try again."),
252
+ result: {
253
+ type: "invalid_callback",
254
+ message: "Authentication callback missing required parameters"
255
+ }
256
+ };
257
+ }
258
+ function errorHtml(error, nextStep) {
259
+ const nextStepHtml = nextStep ? `<p>${escapeHtml(nextStep)}</p>` : "";
260
+ return `<!DOCTYPE html>
261
+ <html><head><title>GitHits CLI</title>
262
+ <style>
263
+ body {
264
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
265
+ display: flex;
266
+ justify-content: center;
267
+ align-items: center;
268
+ height: 100vh;
269
+ margin: 0;
270
+ background: radial-gradient(circle at center center, #4d3648, #3a2835, #261a22, #0d1117);
271
+ }
272
+ .card {
273
+ text-align: center;
274
+ background: rgba(13, 17, 23, 0.75);
275
+ padding: 3rem;
276
+ border-radius: 16px;
277
+ border: 1px solid rgba(239, 68, 68, 0.35);
278
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
279
+ max-width: 720px;
280
+ }
281
+ h1 {
282
+ color: #ef4444;
283
+ margin-bottom: 0.75rem;
284
+ font-size: 3rem;
285
+ font-weight: 700;
286
+ }
287
+ p {
288
+ color: #f385a5;
289
+ font-size: 1.1rem;
290
+ margin: 0;
291
+ }
292
+ </style>
293
+ </head>
294
+ <body>
295
+ <div class="card">
296
+ <h1>Authentication failed</h1>
297
+ <p>${escapeHtml(error)}</p>
298
+ ${nextStepHtml}
299
+ </div>
300
+ </body></html>`;
301
+ }
302
+ function sendHtmlResponse(res, statusCode, html) {
303
+ res.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" });
304
+ res.end(html);
305
+ }
306
+ function closeServer(server) {
307
+ server.close();
308
+ }
309
+ function escapeHtml(text) {
310
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
311
+ }
312
+ // src/services/auth-storage.ts
313
+ var CONFIG_DIR = ".githits";
314
+ var AUTH_FILE = "auth.json";
315
+ var CLIENT_FILE = "client.json";
316
+ var DIR_MODE = 448;
317
+ var FILE_MODE = 384;
318
+
319
+ class AuthStorageImpl {
320
+ fs;
321
+ configDir;
322
+ authPath;
323
+ clientPath;
324
+ constructor(fs, configDir) {
325
+ this.fs = fs;
326
+ this.configDir = configDir ?? fs.joinPath(fs.getHomeDir(), CONFIG_DIR);
327
+ this.authPath = fs.joinPath(this.configDir, AUTH_FILE);
328
+ this.clientPath = fs.joinPath(this.configDir, CLIENT_FILE);
329
+ }
330
+ getStorageLocation() {
331
+ return this.configDir;
332
+ }
333
+ async loadTokens(baseUrl) {
334
+ const stored = await this.loadAuthFile();
335
+ if (!stored)
336
+ return null;
337
+ return stored.tokens[normalizeBaseUrl(baseUrl)] ?? null;
338
+ }
339
+ async saveTokens(baseUrl, data) {
340
+ const stored = await this.loadAuthFile() ?? {
341
+ version: 1,
342
+ tokens: {}
343
+ };
344
+ stored.tokens[normalizeBaseUrl(baseUrl)] = data;
345
+ await this.fs.ensureDir(this.configDir, DIR_MODE);
346
+ await this.fs.writeFile(this.authPath, JSON.stringify(stored, null, 2), FILE_MODE);
347
+ }
348
+ async clearTokens(baseUrl) {
349
+ const stored = await this.loadAuthFile();
350
+ if (!stored)
351
+ return;
352
+ delete stored.tokens[normalizeBaseUrl(baseUrl)];
353
+ if (Object.keys(stored.tokens).length === 0) {
354
+ await this.fs.deleteFile(this.authPath);
355
+ } else {
356
+ await this.fs.writeFile(this.authPath, JSON.stringify(stored, null, 2), FILE_MODE);
357
+ }
358
+ }
359
+ async loadClient(baseUrl) {
360
+ const stored = await this.loadClientFile();
361
+ if (!stored)
362
+ return null;
363
+ return stored.clients[normalizeBaseUrl(baseUrl)] ?? null;
364
+ }
365
+ async clearClient(baseUrl) {
366
+ const stored = await this.loadClientFile();
367
+ if (!stored)
368
+ return;
369
+ delete stored.clients[normalizeBaseUrl(baseUrl)];
370
+ if (Object.keys(stored.clients).length === 0) {
371
+ await this.fs.deleteFile(this.clientPath);
372
+ } else {
373
+ await this.fs.writeFile(this.clientPath, JSON.stringify(stored, null, 2), FILE_MODE);
374
+ }
375
+ }
376
+ async saveClient(baseUrl, data) {
377
+ const stored = await this.loadClientFile() ?? {
378
+ version: 1,
379
+ clients: {}
380
+ };
381
+ stored.clients[normalizeBaseUrl(baseUrl)] = data;
382
+ await this.fs.ensureDir(this.configDir, DIR_MODE);
383
+ await this.fs.writeFile(this.clientPath, JSON.stringify(stored, null, 2), FILE_MODE);
384
+ }
385
+ async loadAuthFile() {
386
+ if (!await this.fs.exists(this.authPath))
387
+ return null;
388
+ try {
389
+ const content = await this.fs.readFile(this.authPath);
390
+ const data = JSON.parse(content);
391
+ if (data.version !== 1 || !data.tokens)
392
+ return null;
393
+ return data;
394
+ } catch {
395
+ return null;
396
+ }
397
+ }
398
+ async loadClientFile() {
399
+ if (!await this.fs.exists(this.clientPath))
400
+ return null;
401
+ try {
402
+ const content = await this.fs.readFile(this.clientPath);
403
+ const data = JSON.parse(content);
404
+ if (data.version !== 1 || !data.clients)
405
+ return null;
406
+ return data;
407
+ } catch {
408
+ return null;
409
+ }
410
+ }
411
+ }
412
+ function normalizeBaseUrl(url) {
413
+ return url.replace(/\/+$/, "");
414
+ }
415
+ // src/services/browser-service.ts
416
+ import open from "open";
417
+
418
+ class BrowserServiceImpl {
419
+ async open(url) {
420
+ await open(url);
421
+ }
422
+ }
423
+ // src/services/chunking-keyring-service.ts
424
+ var WINDOWS_MAX_ENTRY_SIZE = 1200;
425
+ var CHUNKED_PREFIX = "CHUNKED:";
426
+ var MAX_CHUNK_COUNT = 100;
427
+ function chunkKey(account, writeId, index) {
428
+ return `${account}:chunk:${writeId}:${index}`;
429
+ }
430
+ function parseChunkedSentinel(value) {
431
+ if (!value.startsWith(CHUNKED_PREFIX))
432
+ return null;
433
+ const rest = value.slice(CHUNKED_PREFIX.length);
434
+ const colonIndex = rest.indexOf(":");
435
+ if (colonIndex === -1)
436
+ return null;
437
+ const writeId = rest.slice(0, colonIndex);
438
+ if (writeId.length === 0)
439
+ return null;
440
+ const countStr = rest.slice(colonIndex + 1);
441
+ const count = Number(countStr);
442
+ if (!Number.isInteger(count) || count <= 0)
443
+ return null;
444
+ return { writeId, count };
445
+ }
446
+ function splitIntoChunks(value, maxSize) {
447
+ if (value.length === 0)
448
+ return [""];
449
+ const chunks = [];
450
+ for (let offset = 0;offset < value.length; offset += maxSize) {
451
+ chunks.push(value.slice(offset, offset + maxSize));
452
+ }
453
+ return chunks;
454
+ }
455
+ function generateWriteId() {
456
+ let id;
457
+ do {
458
+ id = Math.random().toString(36).slice(2, 8);
459
+ } while (id.length < 6);
460
+ return id;
461
+ }
462
+
463
+ class ChunkingKeyringService {
464
+ inner;
465
+ maxEntrySize;
466
+ constructor(inner, maxEntrySize = WINDOWS_MAX_ENTRY_SIZE) {
467
+ this.inner = inner;
468
+ this.maxEntrySize = maxEntrySize;
469
+ }
470
+ getPassword(service, account) {
471
+ const value = this.inner.getPassword(service, account);
472
+ if (value === null)
473
+ return null;
474
+ if (!value.startsWith(CHUNKED_PREFIX))
475
+ return value;
476
+ const sentinel = parseChunkedSentinel(value);
477
+ if (sentinel === null)
478
+ return null;
479
+ const chunks = [];
480
+ for (let i = 0;i < sentinel.count; i++) {
481
+ const chunk = this.inner.getPassword(service, chunkKey(account, sentinel.writeId, i));
482
+ if (chunk === null) {
483
+ console.error(`Warning: Incomplete chunked keychain entry for "${account}" (missing chunk ${i} of ${sentinel.count}). Treating as missing.`);
484
+ return null;
485
+ }
486
+ chunks.push(chunk);
487
+ }
488
+ return chunks.join("");
489
+ }
490
+ setPassword(service, account, password) {
491
+ const oldValue = this.readOldSentinel(service, account);
492
+ if (password.length <= this.maxEntrySize) {
493
+ this.inner.setPassword(service, account, password);
494
+ } else {
495
+ const chunks = splitIntoChunks(password, this.maxEntrySize);
496
+ if (chunks.length > MAX_CHUNK_COUNT) {
497
+ throw new Error(`Value requires ${chunks.length} chunks, exceeding maximum of ${MAX_CHUNK_COUNT}. ` + `This likely indicates a bug — credential data should not be this large.`);
498
+ }
499
+ const writeId = generateWriteId();
500
+ for (const [i, chunk] of chunks.entries()) {
501
+ this.inner.setPassword(service, chunkKey(account, writeId, i), chunk);
502
+ }
503
+ this.inner.setPassword(service, account, `${CHUNKED_PREFIX}${writeId}:${chunks.length}`);
504
+ }
505
+ if (oldValue !== null) {
506
+ this.deleteChunkEntries(service, account, oldValue);
507
+ }
508
+ }
509
+ deletePassword(service, account) {
510
+ const oldValue = this.readOldSentinel(service, account);
511
+ if (oldValue !== null) {
512
+ this.deleteChunkEntries(service, account, oldValue);
513
+ }
514
+ return this.inner.deletePassword(service, account);
515
+ }
516
+ readOldSentinel(service, account) {
517
+ try {
518
+ const value = this.inner.getPassword(service, account);
519
+ if (value === null)
520
+ return null;
521
+ return parseChunkedSentinel(value);
522
+ } catch {
523
+ return null;
524
+ }
525
+ }
526
+ deleteChunkEntries(service, account, sentinel) {
527
+ for (let i = 0;i < sentinel.count; i++) {
528
+ try {
529
+ this.inner.deletePassword(service, chunkKey(account, sentinel.writeId, i));
530
+ } catch {}
531
+ }
532
+ }
533
+ }
534
+ // src/services/code-navigation-capability.ts
535
+ function getCodeNavigationCapability(token) {
536
+ if (!token)
537
+ return "unknown";
538
+ const payload = parseJwtPayload(token);
539
+ if (!payload)
540
+ return "unknown";
541
+ const featureFlags = extractFeatureFlags(payload);
542
+ if (!featureFlags)
543
+ return "unknown";
544
+ return featureFlags.includes("code_navigation") ? "enabled" : "disabled";
545
+ }
546
+ function extractFeatureFlags(payload) {
547
+ if (isStringArray(payload.feature_flags)) {
548
+ return payload.feature_flags;
549
+ }
550
+ if (payload.claims && isStringArray(payload.claims.feature_flags)) {
551
+ return payload.claims.feature_flags;
552
+ }
553
+ return null;
554
+ }
555
+ function isStringArray(value) {
556
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
557
+ }
558
+ function parseJwtPayload(token) {
559
+ const parts = token.split(".");
560
+ if (parts.length < 2)
561
+ return null;
562
+ try {
563
+ const decoded = Buffer.from(toBase64(parts[1] ?? ""), "base64").toString("utf8");
564
+ const parsed = JSON.parse(decoded);
565
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
566
+ return null;
567
+ }
568
+ return parsed;
569
+ } catch {
570
+ return null;
571
+ }
572
+ }
573
+ function toBase64(base64Url) {
574
+ const normalized = base64Url.replace(/-/g, "+").replace(/_/g, "/");
575
+ const padding = normalized.length % 4;
576
+ if (padding === 0)
577
+ return normalized;
578
+ return normalized.padEnd(normalized.length + (4 - padding), "=");
579
+ }
580
+ // src/services/code-navigation-service.ts
581
+ import { z } from "zod";
582
+
583
+ // src/shared/debug-log.ts
584
+ function debugLog(area, payload) {
585
+ if (!isAreaEnabled(area))
586
+ return;
587
+ const line = {
588
+ ts: new Date().toISOString(),
589
+ area,
590
+ ...payload
591
+ };
592
+ let text;
593
+ try {
594
+ text = JSON.stringify(line);
595
+ } catch {
596
+ text = JSON.stringify({
597
+ ts: line.ts,
598
+ area,
599
+ error: "debug-log payload not serialisable"
600
+ });
601
+ }
602
+ process.stderr.write(`${text}
603
+ `);
604
+ }
605
+ function isAreaEnabled(area) {
606
+ const raw = process.env.GITHITS_DEBUG;
607
+ if (!raw || raw === "")
608
+ return false;
609
+ if (raw === "*")
610
+ return true;
611
+ const scopes = raw.split(",").map((s) => s.trim()).filter(Boolean);
612
+ return scopes.includes(area) || scopes.includes("*");
613
+ }
614
+
615
+ // src/shared/request-headers.ts
616
+ import { createHash as createHash2, randomUUID } from "node:crypto";
617
+ var MAX_HEADER_BYTES = 256;
618
+ var SESSION_ENV_VARS = [
619
+ "TERM_SESSION_ID",
620
+ "ITERM_SESSION_ID",
621
+ "WEZTERM_PANE",
622
+ "KITTY_PID",
623
+ "ALACRITTY_SOCKET",
624
+ "WT_SESSION",
625
+ "VSCODE_PID",
626
+ "SUPERSET_PANE_ID",
627
+ "SUPERSET_WORKSPACE_ID",
628
+ "STARSHIP_SESSION_KEY",
629
+ "SSH_CONNECTION"
630
+ ];
631
+ var cachedSessionId;
632
+ function resolveRawSessionId(env = process.env, ppid = process.ppid) {
633
+ for (const key of SESSION_ENV_VARS) {
634
+ const value = env[key];
635
+ if (value && value.trim().length > 0) {
636
+ return value.trim();
637
+ }
638
+ }
639
+ if (typeof ppid === "number" && !Number.isNaN(ppid) && ppid > 0) {
640
+ return String(ppid);
641
+ }
642
+ return randomUUID();
643
+ }
644
+ function getSessionId(env, ppid) {
645
+ if (cachedSessionId !== undefined && env === undefined && ppid === undefined) {
646
+ return cachedSessionId;
647
+ }
648
+ const raw = resolveRawSessionId(env, ppid);
649
+ const hashed = hashValue(raw);
650
+ if (env === undefined && ppid === undefined) {
651
+ cachedSessionId = hashed;
652
+ }
653
+ return hashed;
654
+ }
655
+ function hashValue(input) {
656
+ return createHash2("sha256").update(input).digest("hex").slice(0, 16);
657
+ }
658
+ var AGENT_PROBES = [
659
+ { envVar: "OPENCODE", name: "opencode" },
660
+ { envVar: "CLAUDECODE", name: "claude-code" },
661
+ { envVar: "CURSOR_TRACE_ID", name: "cursor" },
662
+ { envVar: "WINDSURF_CONFIG_DIR", name: "windsurf" },
663
+ { envVar: "ZED_TERM", name: "zed" },
664
+ { envVar: "VSCODE_PID", name: "vscode" }
665
+ ];
666
+ function parseAgentString(raw) {
667
+ const trimmed = raw.trim();
668
+ if (trimmed.length === 0)
669
+ return;
670
+ const slashIndex = trimmed.indexOf("/");
671
+ if (slashIndex === -1)
672
+ return { name: trimmed };
673
+ const name = trimmed.slice(0, slashIndex);
674
+ const ver = trimmed.slice(slashIndex + 1);
675
+ if (name.length === 0)
676
+ return;
677
+ return { name, version: ver || undefined };
678
+ }
679
+ function formatAgentInfo(info) {
680
+ return info.version ? `${info.name}/${info.version}` : info.name;
681
+ }
682
+ function resolveAgentInfo(env = process.env) {
683
+ const explicit = env.GITHITS_AGENT;
684
+ if (explicit && explicit.trim().length > 0) {
685
+ return parseAgentString(explicit);
686
+ }
687
+ for (const probe of AGENT_PROBES) {
688
+ const value = env[probe.envVar];
689
+ if (value && value.trim().length > 0) {
690
+ return { name: probe.name };
691
+ }
692
+ }
693
+ return;
694
+ }
695
+ var currentAgentInfo;
696
+ var agentInfoInitialized = false;
697
+ var agentInfoExplicitlySet = false;
698
+ var mcpClientVersionProvider;
699
+ function setMcpClientVersionProvider(provider) {
700
+ mcpClientVersionProvider = provider;
701
+ }
702
+ function getAgentInfo(env) {
703
+ if (agentInfoExplicitlySet) {
704
+ return currentAgentInfo;
705
+ }
706
+ if (mcpClientVersionProvider) {
707
+ try {
708
+ const fromProvider = mcpClientVersionProvider();
709
+ if (fromProvider && fromProvider.name.trim().length > 0) {
710
+ return fromProvider;
711
+ }
712
+ } catch {}
713
+ }
714
+ if (!agentInfoInitialized || env !== undefined) {
715
+ currentAgentInfo = resolveAgentInfo(env);
716
+ if (env === undefined) {
717
+ agentInfoInitialized = true;
718
+ }
719
+ }
720
+ return currentAgentInfo;
721
+ }
722
+ var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
723
+ function sanitizeHeaderValue(value) {
724
+ if (value === undefined || value === null || typeof value !== "string") {
725
+ return;
726
+ }
727
+ const cleaned = value.replace(CONTROL_CHARS, "").trim();
728
+ if (cleaned.length === 0)
729
+ return;
730
+ if (Buffer.byteLength(cleaned, "utf8") > MAX_HEADER_BYTES)
731
+ return;
732
+ return cleaned;
733
+ }
734
+ var BASE_CLIENT_NAME = "githits-cli";
735
+ var clientName = BASE_CLIENT_NAME;
736
+ function setClientMode(mode) {
737
+ clientName = `${BASE_CLIENT_NAME}/${mode}`;
738
+ }
739
+ function buildClientHeaders(env, ppid) {
740
+ try {
741
+ const headers = {};
742
+ const name = sanitizeHeaderValue(clientName);
743
+ if (name) {
744
+ headers["x-githits-client-name"] = name;
745
+ }
746
+ const clientVersion = sanitizeHeaderValue(version);
747
+ if (clientVersion) {
748
+ headers["x-githits-client-version"] = clientVersion;
749
+ }
750
+ const agentInfo = getAgentInfo(env);
751
+ if (agentInfo) {
752
+ const agentValue = sanitizeHeaderValue(formatAgentInfo(agentInfo));
753
+ if (agentValue) {
754
+ headers["x-githits-agent"] = agentValue;
755
+ }
756
+ }
757
+ const sessionId = sanitizeHeaderValue(getSessionId(env, ppid));
758
+ if (sessionId) {
759
+ headers["x-githits-session-id"] = sessionId;
760
+ }
761
+ return headers;
762
+ } catch {
763
+ return {};
764
+ }
765
+ }
766
+
767
+ // src/shared/pkgseer-graphql.ts
768
+ class PkgseerTransportError extends Error {
769
+ constructor(message, options) {
770
+ super(message, options);
771
+ this.name = "PkgseerTransportError";
772
+ }
773
+ }
774
+ function baseUrl(endpointUrl) {
775
+ return endpointUrl.replace(/\/+$/, "");
776
+ }
777
+ async function postPkgseerGraphql(request) {
778
+ const fetchFn = request.fetchFn ?? globalThis.fetch;
779
+ const userAgent = request.userAgent ?? `githits-cli/${version}`;
780
+ let response;
781
+ try {
782
+ response = await fetchFn(`${baseUrl(request.endpointUrl)}/api/graphql`, {
783
+ method: "POST",
784
+ headers: {
785
+ ...buildClientHeaders(),
786
+ Authorization: `Bearer ${request.token}`,
787
+ "Content-Type": "application/json",
788
+ "User-Agent": userAgent
789
+ },
790
+ body: JSON.stringify({
791
+ query: request.query,
792
+ variables: request.variables
793
+ })
794
+ });
795
+ } catch (cause) {
796
+ debugLog("pkg-graphql", {
797
+ event: "transport-error",
798
+ errorName: cause instanceof Error ? cause.name : typeof cause,
799
+ hasCause: true
800
+ });
801
+ throw new PkgseerTransportError("Network request failed before a response was received. Caller should re-wrap with a domain-specific message.", { cause });
802
+ }
803
+ const responseBody = await response.text().catch(() => "");
804
+ const parsedBody = parseJsonOrNull(responseBody);
805
+ return {
806
+ status: response.status,
807
+ responseBody,
808
+ parsedBody
809
+ };
810
+ }
811
+ function parseJsonOrNull(body) {
812
+ if (!body)
813
+ return null;
814
+ try {
815
+ return JSON.parse(body);
816
+ } catch {
817
+ return null;
818
+ }
819
+ }
820
+
821
+ // src/shared/telemetry.ts
822
+ import { writeSync } from "node:fs";
823
+ var ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
824
+ function isTelemetryEnabled(env = process.env) {
825
+ const raw = env.GITHITS_TELEMETRY?.trim().toLowerCase();
826
+ if (!raw)
827
+ return false;
828
+ return ENABLED_VALUES.has(raw);
829
+ }
830
+
831
+ class TelemetryCollector {
832
+ enabled;
833
+ now;
834
+ write;
835
+ sessionStartMs;
836
+ spans = [];
837
+ activeSpans = new Map;
838
+ nextId = 1;
839
+ flushed = false;
840
+ constructor(options = {}) {
841
+ this.enabled = isTelemetryEnabled(options.env);
842
+ this.now = options.now ?? (() => globalThis.performance.now());
843
+ this.write = options.write ?? ((text) => writeSync(process.stderr.fd, text));
844
+ this.sessionStartMs = this.now();
845
+ }
846
+ isEnabled() {
847
+ return this.enabled;
848
+ }
849
+ startSpan(name, attributes) {
850
+ if (!this.enabled)
851
+ return;
852
+ const span = {
853
+ id: this.nextId++,
854
+ name,
855
+ startMs: this.now(),
856
+ attributes: sanitiseAttributes(attributes)
857
+ };
858
+ this.spans.push(span);
859
+ this.activeSpans.set(span.id, span);
860
+ return { id: span.id };
861
+ }
862
+ endSpan(handle, attributes) {
863
+ if (!this.enabled || !handle)
864
+ return;
865
+ const span = this.activeSpans.get(handle.id);
866
+ if (!span || span.endMs !== undefined)
867
+ return;
868
+ span.endMs = this.now();
869
+ span.attributes = mergeAttributes(span.attributes, attributes);
870
+ this.activeSpans.delete(handle.id);
871
+ }
872
+ flush(exitCode = 0) {
873
+ if (!this.enabled || this.flushed)
874
+ return;
875
+ const nowMs = this.now();
876
+ for (const span of this.activeSpans.values()) {
877
+ if (span.endMs !== undefined)
878
+ continue;
879
+ span.endMs = nowMs;
880
+ span.endedAtExit = true;
881
+ }
882
+ this.activeSpans.clear();
883
+ this.write(formatTelemetryReport(this.spans, this.sessionStartMs, nowMs, exitCode));
884
+ this.flushed = true;
885
+ }
886
+ }
887
+ async function withTelemetrySpan(name, operation, attributes) {
888
+ const handle = telemetryCollector.startSpan(name, attributes);
889
+ try {
890
+ const result = await operation();
891
+ telemetryCollector.endSpan(handle);
892
+ return result;
893
+ } catch (error) {
894
+ telemetryCollector.endSpan(handle, { error: true });
895
+ throw error;
896
+ }
897
+ }
898
+ function withTelemetrySpanSync(name, operation, attributes) {
899
+ const handle = telemetryCollector.startSpan(name, attributes);
900
+ try {
901
+ const result = operation();
902
+ telemetryCollector.endSpan(handle);
903
+ return result;
904
+ } catch (error) {
905
+ telemetryCollector.endSpan(handle, { error: true });
906
+ throw error;
907
+ }
908
+ }
909
+ function startTelemetrySpan(name, attributes) {
910
+ return telemetryCollector.startSpan(name, attributes);
911
+ }
912
+ function endTelemetrySpan(handle, attributes) {
913
+ telemetryCollector.endSpan(handle, attributes);
914
+ }
915
+ function flushTelemetry(exitCode = 0) {
916
+ telemetryCollector.flush(exitCode);
917
+ }
918
+ var telemetryCollector = new TelemetryCollector;
919
+ function sanitiseAttributes(attributes) {
920
+ if (!attributes)
921
+ return;
922
+ const entries = Object.entries(attributes).filter(([, value]) => value !== undefined);
923
+ if (entries.length === 0)
924
+ return;
925
+ return Object.fromEntries(entries);
926
+ }
927
+ function mergeAttributes(initial, extra) {
928
+ if (!initial && !extra)
929
+ return;
930
+ return sanitiseAttributes({
931
+ ...initial ?? {},
932
+ ...extra ?? {}
933
+ });
934
+ }
935
+ function formatTelemetryReport(spans, sessionStartMs, sessionEndMs, exitCode) {
936
+ const lines = [
937
+ "[githits telemetry]",
938
+ `exit: ${exitCode}`,
939
+ `total: ${formatMs(sessionEndMs - sessionStartMs)}`
940
+ ];
941
+ const orderedSpans = [...spans].sort((left, right) => {
942
+ if (left.startMs !== right.startMs) {
943
+ return left.startMs - right.startMs;
944
+ }
945
+ return left.id - right.id;
946
+ });
947
+ for (const span of orderedSpans) {
948
+ const endMs = span.endMs ?? sessionEndMs;
949
+ const details = [`start +${formatMs(span.startMs - sessionStartMs)}`];
950
+ if (span.endedAtExit) {
951
+ details.push("ended-at-exit");
952
+ }
953
+ const attrs = formatAttributes(span.attributes);
954
+ if (attrs) {
955
+ details.push(attrs);
956
+ }
957
+ lines.push(`- ${span.name}: ${formatMs(endMs - span.startMs)} (${details.join(", ")})`);
958
+ }
959
+ return `${lines.join(`
960
+ `)}
961
+ `;
962
+ }
963
+ function formatAttributes(attributes) {
964
+ if (!attributes)
965
+ return "";
966
+ return Object.entries(attributes).map(([key, value]) => `${key}=${String(value)}`).join(" ");
967
+ }
968
+ function formatMs(value) {
969
+ return `${value.toFixed(1)}ms`;
970
+ }
971
+
972
+ // src/services/githits-service.ts
973
+ class AuthenticationError extends Error {
974
+ constructor(message) {
975
+ super(message);
976
+ this.name = "AuthenticationError";
977
+ }
978
+ }
979
+ function parseDetail(body) {
980
+ if (!body)
981
+ return;
982
+ try {
983
+ const parsed = JSON.parse(body);
984
+ if (typeof parsed.detail === "string")
985
+ return parsed.detail;
986
+ } catch {
987
+ return body;
988
+ }
989
+ return;
990
+ }
991
+
992
+ class GitHitsServiceImpl {
993
+ apiUrl;
994
+ token;
995
+ constructor(apiUrl, token) {
996
+ this.apiUrl = apiUrl;
997
+ this.token = token;
998
+ }
999
+ async search(params) {
1000
+ return withTelemetrySpan("githits.search.request", async () => {
1001
+ const response = await fetch(`${this.apiUrl}/search`, {
1002
+ method: "POST",
1003
+ headers: this.headers(),
1004
+ body: JSON.stringify({
1005
+ query: params.query,
1006
+ language: params.language,
1007
+ license_mode: params.licenseMode ?? "strict",
1008
+ include_explanation: params.includeExplanation ?? false
1009
+ })
1010
+ });
1011
+ if (!response.ok) {
1012
+ throw await this.createError(response);
1013
+ }
1014
+ return response.text();
1015
+ });
1016
+ }
1017
+ async getLanguages() {
1018
+ return withTelemetrySpan("githits.languages.request", async () => {
1019
+ const response = await fetch(`${this.apiUrl}/languages`, {
1020
+ headers: this.headers()
1021
+ });
1022
+ if (!response.ok) {
1023
+ throw await this.createError(response);
1024
+ }
1025
+ return response.json();
1026
+ });
1027
+ }
1028
+ async submitFeedback(params) {
1029
+ return withTelemetrySpan("githits.feedback.request", async () => {
1030
+ const response = await fetch(`${this.apiUrl}/feedbacks`, {
1031
+ method: "POST",
1032
+ headers: this.headers(),
1033
+ body: JSON.stringify({
1034
+ solution_id: params.solutionId,
1035
+ accepted: params.accepted,
1036
+ feedback_text: params.feedbackText ?? null
1037
+ })
1038
+ });
1039
+ if (!response.ok) {
1040
+ throw await this.createError(response);
1041
+ }
1042
+ return { success: true, message: "Feedback submitted successfully" };
1043
+ });
1044
+ }
1045
+ headers() {
1046
+ return {
1047
+ ...buildClientHeaders(),
1048
+ Authorization: `Bearer ${this.token}`,
1049
+ "Content-Type": "application/json",
1050
+ "User-Agent": `githits-cli/${version}`
1051
+ };
1052
+ }
1053
+ async createError(response) {
1054
+ const status = response.status;
1055
+ const body = await response.text().catch(() => "");
1056
+ switch (status) {
1057
+ case 401:
1058
+ return new AuthenticationError("Authentication required. Run `githits login` to authenticate.");
1059
+ case 403:
1060
+ return new Error("Access denied.");
1061
+ case 404:
1062
+ return new Error(parseDetail(body) || "Resource not found.");
1063
+ default: {
1064
+ if (status >= 500) {
1065
+ const detail = body ? `: ${body}` : "";
1066
+ return new Error(`Server error (${status})${detail}`);
1067
+ }
1068
+ return new Error(body || `Request failed with status ${status}`);
1069
+ }
1070
+ }
1071
+ }
1072
+ }
1073
+
1074
+ // src/services/execute-with-token-refresh.ts
1075
+ async function executeWithTokenRefresh(options) {
1076
+ const token = await options.getToken();
1077
+ if (!token) {
1078
+ throw new AuthenticationError("Authentication required. Run `githits login` to authenticate.");
1079
+ }
1080
+ try {
1081
+ return await options.executeWithToken(token);
1082
+ } catch (error) {
1083
+ if (!options.shouldRefresh(error)) {
1084
+ throw error;
1085
+ }
1086
+ const refreshedToken = await options.forceRefresh();
1087
+ if (!refreshedToken) {
1088
+ throw error;
1089
+ }
1090
+ return options.executeWithToken(refreshedToken);
1091
+ }
1092
+ }
1093
+
1094
+ // src/services/code-navigation-service.ts
1095
+ class CodeNavigationAccessError extends Error {
1096
+ constructor(message) {
1097
+ super(message);
1098
+ this.name = "CodeNavigationAccessError";
1099
+ }
1100
+ }
1101
+
1102
+ class CodeNavigationGraphQLError extends Error {
1103
+ code;
1104
+ constructor(message, code) {
1105
+ super(message);
1106
+ this.code = code;
1107
+ this.name = "CodeNavigationGraphQLError";
1108
+ }
1109
+ }
1110
+
1111
+ class CodeNavigationIndexingError extends Error {
1112
+ indexingRef;
1113
+ availableVersions;
1114
+ constructor(message, indexingRef, availableVersions) {
1115
+ super(message);
1116
+ this.indexingRef = indexingRef;
1117
+ this.availableVersions = availableVersions;
1118
+ this.name = "CodeNavigationIndexingError";
1119
+ }
1120
+ }
1121
+
1122
+ class CodeNavigationUnresolvableError extends Error {
1123
+ constructor(message) {
1124
+ super(message);
1125
+ this.name = "CodeNavigationUnresolvableError";
1126
+ }
1127
+ }
1128
+
1129
+ class MalformedCodeNavigationResponseError extends Error {
1130
+ constructor(message) {
1131
+ super(message);
1132
+ this.name = "MalformedCodeNavigationResponseError";
1133
+ }
1134
+ }
1135
+
1136
+ class CodeNavigationTargetNotFoundError extends Error {
1137
+ availableVersions;
1138
+ constructor(message, availableVersions) {
1139
+ super(message);
1140
+ this.availableVersions = availableVersions;
1141
+ this.name = "CodeNavigationTargetNotFoundError";
1142
+ }
1143
+ }
1144
+
1145
+ class CodeNavigationFileNotFoundError extends Error {
1146
+ filePath;
1147
+ constructor(message, filePath) {
1148
+ super(message);
1149
+ this.filePath = filePath;
1150
+ this.name = "CodeNavigationFileNotFoundError";
1151
+ }
1152
+ }
1153
+
1154
+ class CodeNavigationVersionNotFoundError extends Error {
1155
+ packageName;
1156
+ requestedVersion;
1157
+ latestIndexed;
1158
+ availableVersions;
1159
+ constructor(message, packageName, requestedVersion, latestIndexed, availableVersions) {
1160
+ super(message);
1161
+ this.packageName = packageName;
1162
+ this.requestedVersion = requestedVersion;
1163
+ this.latestIndexed = latestIndexed;
1164
+ this.availableVersions = availableVersions;
1165
+ this.name = "CodeNavigationVersionNotFoundError";
1166
+ }
1167
+ }
1168
+
1169
+ class CodeNavigationValidationError extends Error {
1170
+ constructor(message) {
1171
+ super(message);
1172
+ this.name = "CodeNavigationValidationError";
1173
+ }
1174
+ }
1175
+
1176
+ class CodeNavigationFeatureFlagRequiredError extends Error {
1177
+ constructor(message) {
1178
+ super(message);
1179
+ this.name = "CodeNavigationFeatureFlagRequiredError";
1180
+ }
1181
+ }
1182
+
1183
+ class CodeNavigationNetworkError extends Error {
1184
+ constructor(message, options) {
1185
+ super(message, options);
1186
+ this.name = "CodeNavigationNetworkError";
1187
+ }
1188
+ }
1189
+
1190
+ class CodeNavigationBackendError extends Error {
1191
+ status;
1192
+ graphqlCode;
1193
+ retryable;
1194
+ constructor(message, status, graphqlCode, retryable) {
1195
+ super(message);
1196
+ this.status = status;
1197
+ this.graphqlCode = graphqlCode;
1198
+ this.retryable = retryable;
1199
+ this.name = "CodeNavigationBackendError";
1200
+ }
1201
+ }
1202
+
1203
+ class InvalidSearchSymbolsRequestError extends Error {
1204
+ constructor(message) {
1205
+ super(message);
1206
+ this.name = "InvalidSearchSymbolsRequestError";
1207
+ }
1208
+ }
1209
+ var SEARCH_SYMBOLS_QUERY = `
1210
+ query SearchSymbols(
1211
+ $registry: Registry
1212
+ $packageName: String
1213
+ $repoUrl: String
1214
+ $gitRef: String
1215
+ $query: String
1216
+ $keywords: [String!]
1217
+ $matchMode: MatchMode
1218
+ $kind: SymbolKind
1219
+ $category: SymbolCategory
1220
+ $filePath: String
1221
+ $version: String
1222
+ $limit: Int
1223
+ $fileIntent: FileIntent
1224
+ $waitTimeoutMs: Int
1225
+ ) {
1226
+ searchSymbols(
1227
+ registry: $registry
1228
+ packageName: $packageName
1229
+ repoUrl: $repoUrl
1230
+ gitRef: $gitRef
1231
+ query: $query
1232
+ keywords: $keywords
1233
+ matchMode: $matchMode
1234
+ kind: $kind
1235
+ category: $category
1236
+ filePath: $filePath
1237
+ version: $version
1238
+ limit: $limit
1239
+ fileIntent: $fileIntent
1240
+ mode: DETAILED
1241
+ waitTimeoutMs: $waitTimeoutMs
1242
+ ) {
1243
+ results {
1244
+ name
1245
+ filePath
1246
+ startLine
1247
+ endLine
1248
+ preview
1249
+ code
1250
+ language
1251
+ symbolRef
1252
+ qualifiedPath
1253
+ kind
1254
+ category
1255
+ arity
1256
+ isPublic
1257
+ containedSymbols
1258
+ }
1259
+ totalMatches
1260
+ hasMore
1261
+ indexedVersion
1262
+ resolution {
1263
+ requestedVersion
1264
+ requestedRef
1265
+ resolvedRef
1266
+ commitSha
1267
+ }
1268
+ diagnostics {
1269
+ hint
1270
+ }
1271
+ warning
1272
+ codeIndexState
1273
+ indexingRef
1274
+ availableVersions {
1275
+ version
1276
+ ref
1277
+ }
1278
+ }
1279
+ }`;
1280
+ var UNIFIED_SEARCH_QUERY = `
1281
+ query UnifiedSearch(
1282
+ $targets: [SearchPackageInput!]!
1283
+ $query: String!
1284
+ $sources: [DiscoverySearchSource!]
1285
+ $filters: DiscoverySearchFiltersInput
1286
+ $allowPartialResults: Boolean
1287
+ $limit: Int
1288
+ $offset: Int
1289
+ $waitTimeoutMs: Int
1290
+ ) {
1291
+ search(
1292
+ targets: $targets
1293
+ query: $query
1294
+ sources: $sources
1295
+ filters: $filters
1296
+ allowPartialResults: $allowPartialResults
1297
+ limit: $limit
1298
+ offset: $offset
1299
+ waitTimeoutMs: $waitTimeoutMs
1300
+ ) {
1301
+ completed
1302
+ searchRef
1303
+ result {
1304
+ query
1305
+ queryWarnings
1306
+ sources
1307
+ results {
1308
+ id
1309
+ resultType
1310
+ targetLabel
1311
+ title
1312
+ summary
1313
+ score
1314
+ highlights {
1315
+ title
1316
+ summary
1317
+ }
1318
+ locator {
1319
+ registry
1320
+ packageName
1321
+ version
1322
+ pageId
1323
+ repoUrl
1324
+ gitRef
1325
+ filePath
1326
+ startLine
1327
+ endLine
1328
+ fileContentHash
1329
+ symbolRef
1330
+ qualifiedPath
1331
+ kind
1332
+ category
1333
+ language
1334
+ }
1335
+ }
1336
+ page {
1337
+ offset
1338
+ limit
1339
+ returned
1340
+ hasMore
1341
+ }
1342
+ partialResults
1343
+ sourceStatus {
1344
+ source
1345
+ targetLabel
1346
+ indexingStatus
1347
+ codeIndexState
1348
+ resultCount
1349
+ appliedFilters
1350
+ ignoredFilters
1351
+ incompatibleFilters
1352
+ appliedQueryFeatures
1353
+ ignoredQueryFeatures
1354
+ incompatibleQueryFeatures
1355
+ note
1356
+ }
1357
+ }
1358
+ progress {
1359
+ searchRef
1360
+ status
1361
+ targetsTotal
1362
+ targetsReady
1363
+ elapsedMs
1364
+ query
1365
+ queryWarnings
1366
+ sources
1367
+ expiresAt
1368
+ }
1369
+ }
1370
+ }`;
1371
+ var UNIFIED_SEARCH_STATUS_QUERY = `
1372
+ query UnifiedSearchStatus($searchRef: String!, $includeResults: Boolean!) {
1373
+ discoverySearchProgress(searchRef: $searchRef, includeResults: $includeResults) {
1374
+ searchRef
1375
+ status
1376
+ targetsTotal
1377
+ targetsReady
1378
+ elapsedMs
1379
+ query
1380
+ queryWarnings
1381
+ sources
1382
+ expiresAt
1383
+ results {
1384
+ query
1385
+ queryWarnings
1386
+ sources
1387
+ results {
1388
+ id
1389
+ resultType
1390
+ targetLabel
1391
+ title
1392
+ summary
1393
+ score
1394
+ highlights {
1395
+ title
1396
+ summary
1397
+ }
1398
+ locator {
1399
+ registry
1400
+ packageName
1401
+ version
1402
+ pageId
1403
+ repoUrl
1404
+ gitRef
1405
+ filePath
1406
+ startLine
1407
+ endLine
1408
+ fileContentHash
1409
+ symbolRef
1410
+ qualifiedPath
1411
+ kind
1412
+ category
1413
+ language
1414
+ }
1415
+ }
1416
+ page {
1417
+ offset
1418
+ limit
1419
+ returned
1420
+ hasMore
1421
+ }
1422
+ partialResults
1423
+ sourceStatus {
1424
+ source
1425
+ targetLabel
1426
+ indexingStatus
1427
+ codeIndexState
1428
+ resultCount
1429
+ appliedFilters
1430
+ ignoredFilters
1431
+ incompatibleFilters
1432
+ appliedQueryFeatures
1433
+ ignoredQueryFeatures
1434
+ incompatibleQueryFeatures
1435
+ note
1436
+ }
1437
+ }
1438
+ }
1439
+ }`;
1440
+ var availableVersionSchema = z.object({
1441
+ version: z.string().nullable().optional(),
1442
+ ref: z.string()
1443
+ });
1444
+ var searchSymbolsResultEntrySchema = z.object({
1445
+ name: z.string().nullable().optional(),
1446
+ filePath: z.string().nullable().optional(),
1447
+ startLine: z.number().int().nullable().optional(),
1448
+ endLine: z.number().int().nullable().optional(),
1449
+ preview: z.string().nullable().optional(),
1450
+ code: z.string().nullable().optional(),
1451
+ language: z.string().nullable().optional(),
1452
+ symbolRef: z.string().nullable().optional(),
1453
+ qualifiedPath: z.string().nullable().optional(),
1454
+ kind: z.string().nullable().optional(),
1455
+ category: z.string().nullable().optional(),
1456
+ arity: z.number().int().nullable().optional(),
1457
+ isPublic: z.boolean().nullable().optional(),
1458
+ containedSymbols: z.number().int().nullable().optional()
1459
+ });
1460
+ var searchSymbolsResponseSchema = z.object({
1461
+ results: z.array(searchSymbolsResultEntrySchema),
1462
+ totalMatches: z.number().int(),
1463
+ hasMore: z.boolean(),
1464
+ indexedVersion: z.string().nullable().optional(),
1465
+ resolution: z.object({
1466
+ requestedVersion: z.string().nullable().optional(),
1467
+ requestedRef: z.string().nullable().optional(),
1468
+ resolvedRef: z.string().nullable().optional(),
1469
+ commitSha: z.string().nullable().optional()
1470
+ }).nullable().optional(),
1471
+ diagnostics: z.object({
1472
+ hint: z.string().nullable().optional()
1473
+ }).nullable().optional(),
1474
+ warning: z.string().nullable().optional(),
1475
+ codeIndexState: z.string(),
1476
+ indexingRef: z.string().nullable().optional(),
1477
+ availableVersions: z.array(availableVersionSchema).nullable().optional()
1478
+ });
1479
+ var unifiedSearchSourceSchema = z.enum(["AUTO", "DOCS", "CODE", "SYMBOL"]);
1480
+ var unifiedSearchResultTypeSchema = z.enum([
1481
+ "DOCUMENTATION_PAGE",
1482
+ "REPOSITORY_SYMBOL",
1483
+ "REPOSITORY_CODE",
1484
+ "REPOSITORY_DOC"
1485
+ ]);
1486
+ var unifiedSearchLocatorSchema = z.object({
1487
+ registry: z.string().nullable().optional(),
1488
+ packageName: z.string().nullable().optional(),
1489
+ version: z.string().nullable().optional(),
1490
+ pageId: z.string().nullable().optional(),
1491
+ repoUrl: z.string().nullable().optional(),
1492
+ gitRef: z.string().nullable().optional(),
1493
+ filePath: z.string().nullable().optional(),
1494
+ startLine: z.number().int().nullable().optional(),
1495
+ endLine: z.number().int().nullable().optional(),
1496
+ fileContentHash: z.string().nullable().optional(),
1497
+ symbolRef: z.string().nullable().optional(),
1498
+ qualifiedPath: z.string().nullable().optional(),
1499
+ kind: z.string().nullable().optional(),
1500
+ category: z.string().nullable().optional(),
1501
+ language: z.string().nullable().optional()
1502
+ });
1503
+ var unifiedSearchHitSchema = z.object({
1504
+ id: z.string(),
1505
+ resultType: unifiedSearchResultTypeSchema,
1506
+ targetLabel: z.string(),
1507
+ title: z.string().nullable().optional(),
1508
+ summary: z.string().nullable().optional(),
1509
+ score: z.number().nullable().optional(),
1510
+ highlights: z.object({
1511
+ title: z.array(z.tuple([z.number().int(), z.number().int()])).nullable().optional(),
1512
+ summary: z.array(z.tuple([z.number().int(), z.number().int()])).nullable().optional()
1513
+ }).nullable().optional(),
1514
+ locator: unifiedSearchLocatorSchema
1515
+ });
1516
+ var unifiedSearchPageInfoSchema = z.object({
1517
+ offset: z.number().int(),
1518
+ limit: z.number().int(),
1519
+ returned: z.number().int(),
1520
+ hasMore: z.boolean()
1521
+ });
1522
+ var unifiedSearchSourceStatusSchema = z.object({
1523
+ source: unifiedSearchSourceSchema,
1524
+ targetLabel: z.string(),
1525
+ indexingStatus: z.string().nullable().optional(),
1526
+ codeIndexState: z.string().nullable().optional(),
1527
+ resultCount: z.number().int().nullable().optional(),
1528
+ appliedFilters: z.array(z.string()),
1529
+ ignoredFilters: z.array(z.string()),
1530
+ incompatibleFilters: z.array(z.string()),
1531
+ appliedQueryFeatures: z.array(z.string()),
1532
+ ignoredQueryFeatures: z.array(z.string()),
1533
+ incompatibleQueryFeatures: z.array(z.string()),
1534
+ note: z.string().nullable().optional()
1535
+ });
1536
+ var unifiedSearchResultSchema = z.object({
1537
+ query: z.string(),
1538
+ queryWarnings: z.array(z.string()),
1539
+ sources: z.array(unifiedSearchSourceSchema),
1540
+ results: z.array(unifiedSearchHitSchema),
1541
+ page: unifiedSearchPageInfoSchema,
1542
+ partialResults: z.boolean(),
1543
+ sourceStatus: z.array(unifiedSearchSourceStatusSchema)
1544
+ });
1545
+ var unifiedSearchSessionStatusSchema = z.enum([
1546
+ "PENDING",
1547
+ "INDEXING",
1548
+ "SEARCHING",
1549
+ "COMPLETED",
1550
+ "TIMEOUT",
1551
+ "FAILED"
1552
+ ]);
1553
+ var unifiedSearchProgressSchema = z.object({
1554
+ searchRef: z.string(),
1555
+ status: unifiedSearchSessionStatusSchema,
1556
+ targetsTotal: z.number().int(),
1557
+ targetsReady: z.number().int(),
1558
+ elapsedMs: z.number().int(),
1559
+ query: z.string(),
1560
+ queryWarnings: z.array(z.string()),
1561
+ sources: z.array(unifiedSearchSourceSchema),
1562
+ expiresAt: z.string().nullable().optional(),
1563
+ results: unifiedSearchResultSchema.nullable().optional()
1564
+ });
1565
+ var asyncUnifiedSearchResultSchema = z.object({
1566
+ completed: z.boolean(),
1567
+ searchRef: z.string().nullable().optional(),
1568
+ result: unifiedSearchResultSchema.nullable().optional(),
1569
+ progress: unifiedSearchProgressSchema.nullable().optional()
1570
+ });
1571
+ var graphQLErrorSchema = z.object({
1572
+ message: z.string(),
1573
+ extensions: z.record(z.string(), z.unknown()).optional()
1574
+ });
1575
+ var navigationResolutionSchema = z.object({
1576
+ requestedVersion: z.string().nullable().optional(),
1577
+ requestedRef: z.string().nullable().optional(),
1578
+ resolvedRef: z.string().nullable().optional(),
1579
+ commitSha: z.string().nullable().optional()
1580
+ }).nullable().optional();
1581
+ var navigationDiagnosticsSchema = z.object({
1582
+ hint: z.string().nullable().optional()
1583
+ }).nullable().optional();
1584
+ var repoFileEntrySchema = z.object({
1585
+ path: z.string(),
1586
+ name: z.string().nullable().optional(),
1587
+ language: z.string().nullable().optional(),
1588
+ fileType: z.string().nullable().optional(),
1589
+ byteSize: z.number().int().nullable().optional()
1590
+ });
1591
+ var listRepoFilesResponseSchema = z.object({
1592
+ files: z.array(repoFileEntrySchema),
1593
+ total: z.number().int(),
1594
+ hasMore: z.boolean(),
1595
+ indexedVersion: z.string().nullable().optional(),
1596
+ resolution: navigationResolutionSchema,
1597
+ diagnostics: navigationDiagnosticsSchema,
1598
+ codeIndexState: z.string(),
1599
+ indexingRef: z.string().nullable().optional(),
1600
+ availableVersions: z.array(availableVersionSchema).nullable().optional()
1601
+ });
1602
+ var listRepoFilesGraphQLResponseSchema = z.object({
1603
+ data: z.object({
1604
+ listRepoFiles: listRepoFilesResponseSchema.nullable().optional()
1605
+ }).nullable().optional(),
1606
+ errors: z.array(graphQLErrorSchema).optional()
1607
+ });
1608
+ var LIST_REPO_FILES_QUERY = `
1609
+ query ListRepoFiles(
1610
+ $registry: Registry
1611
+ $packageName: String
1612
+ $repoUrl: String
1613
+ $gitRef: String
1614
+ $version: String
1615
+ $pathPrefix: String
1616
+ $limit: Int
1617
+ $waitTimeoutMs: Int
1618
+ ) {
1619
+ listRepoFiles(
1620
+ registry: $registry
1621
+ packageName: $packageName
1622
+ repoUrl: $repoUrl
1623
+ gitRef: $gitRef
1624
+ version: $version
1625
+ pathPrefix: $pathPrefix
1626
+ limit: $limit
1627
+ waitTimeoutMs: $waitTimeoutMs
1628
+ ) {
1629
+ files {
1630
+ path
1631
+ name
1632
+ language
1633
+ fileType
1634
+ byteSize
1635
+ }
1636
+ total
1637
+ hasMore
1638
+ indexedVersion
1639
+ resolution {
1640
+ requestedVersion
1641
+ requestedRef
1642
+ resolvedRef
1643
+ commitSha
1644
+ }
1645
+ diagnostics {
1646
+ hint
1647
+ }
1648
+ codeIndexState
1649
+ indexingRef
1650
+ availableVersions {
1651
+ version
1652
+ ref
1653
+ }
1654
+ }
1655
+ }`;
1656
+ var codeContextResponseSchema = z.object({
1657
+ content: z.string().nullable().optional(),
1658
+ filePath: z.string().nullable().optional(),
1659
+ language: z.string().nullable().optional(),
1660
+ totalLines: z.number().int().nullable().optional(),
1661
+ startLine: z.number().int().nullable().optional(),
1662
+ endLine: z.number().int().nullable().optional(),
1663
+ repoUrl: z.string().nullable().optional(),
1664
+ gitRef: z.string().nullable().optional(),
1665
+ isBinary: z.boolean().nullable().optional(),
1666
+ codeIndexState: z.string(),
1667
+ indexingRef: z.string().nullable().optional()
1668
+ });
1669
+ var fetchCodeContextGraphQLResponseSchema = z.object({
1670
+ data: z.object({
1671
+ fetchCodeContext: codeContextResponseSchema.nullable().optional()
1672
+ }).nullable().optional(),
1673
+ errors: z.array(graphQLErrorSchema).optional()
1674
+ });
1675
+ var FETCH_CODE_CONTEXT_QUERY = `
1676
+ query FetchCodeContext(
1677
+ $registry: Registry
1678
+ $packageName: String
1679
+ $repoUrl: String
1680
+ $gitRef: String
1681
+ $version: String
1682
+ $filePath: String!
1683
+ $startLine: Int
1684
+ $endLine: Int
1685
+ $waitTimeoutMs: Int
1686
+ ) {
1687
+ fetchCodeContext(
1688
+ registry: $registry
1689
+ packageName: $packageName
1690
+ repoUrl: $repoUrl
1691
+ gitRef: $gitRef
1692
+ version: $version
1693
+ filePath: $filePath
1694
+ startLine: $startLine
1695
+ endLine: $endLine
1696
+ waitTimeoutMs: $waitTimeoutMs
1697
+ ) {
1698
+ content
1699
+ filePath
1700
+ language
1701
+ totalLines
1702
+ startLine
1703
+ endLine
1704
+ repoUrl
1705
+ gitRef
1706
+ isBinary
1707
+ codeIndexState
1708
+ indexingRef
1709
+ }
1710
+ }`;
1711
+ var grepRepoMatchSchema = z.object({
1712
+ filePath: z.string(),
1713
+ line: z.number().int(),
1714
+ matchStartByte: z.number().int(),
1715
+ matchEndByte: z.number().int(),
1716
+ lineContent: z.string(),
1717
+ contextBefore: z.array(z.string()).nullable().optional(),
1718
+ contextAfter: z.array(z.string()).nullable().optional(),
1719
+ fileContentHash: z.string().nullable().optional(),
1720
+ fileIntent: z.string().nullable().optional(),
1721
+ symbolRowId: z.string().nullable().optional(),
1722
+ symbol: z.object({
1723
+ symbolRef: z.string().optional(),
1724
+ name: z.string().optional(),
1725
+ qualifiedPath: z.string().nullable().optional(),
1726
+ kind: z.string().nullable().optional(),
1727
+ category: z.string().nullable().optional(),
1728
+ arity: z.number().int().nullable().optional(),
1729
+ isPublic: z.boolean().nullable().optional(),
1730
+ filePath: z.string().nullable().optional(),
1731
+ startLine: z.number().int().nullable().optional(),
1732
+ endLine: z.number().int().nullable().optional(),
1733
+ code: z.string().nullable().optional(),
1734
+ callerCount: z.number().int().nullable().optional(),
1735
+ contentHash: z.string().nullable().optional(),
1736
+ parentSymbolRef: z.string().nullable().optional(),
1737
+ parentPath: z.string().nullable().optional()
1738
+ }).nullable().optional()
1739
+ });
1740
+ var grepRepoResponseSchema = z.object({
1741
+ matches: z.array(grepRepoMatchSchema),
1742
+ nextCursor: z.string().nullable().optional(),
1743
+ hasMore: z.boolean(),
1744
+ truncatedReason: z.enum([
1745
+ "NONE",
1746
+ "MAX_MATCHES",
1747
+ "MAX_MATCHES_PER_FILE",
1748
+ "DEADLINE"
1749
+ ]),
1750
+ routeTaken: z.enum(["SINGLE_FILE", "CONTENT_INDEX"]).nullable().optional(),
1751
+ filesScanned: z.number().int(),
1752
+ filesInScope: z.number().int(),
1753
+ binaryFilesSkipped: z.number().int(),
1754
+ filesTooLargeSkipped: z.number().int(),
1755
+ totalMatches: z.number().int(),
1756
+ uniqueFilesMatched: z.number().int(),
1757
+ indexedVersion: z.string().nullable().optional(),
1758
+ resolution: navigationResolutionSchema,
1759
+ codeIndexState: z.string(),
1760
+ indexingRef: z.string().nullable().optional(),
1761
+ availableVersions: z.array(availableVersionSchema).nullable().optional()
1762
+ });
1763
+ var grepRepoGraphQLResponseSchema = z.object({
1764
+ data: z.object({
1765
+ grepRepo: grepRepoResponseSchema.nullable().optional()
1766
+ }).nullable().optional(),
1767
+ errors: z.array(graphQLErrorSchema).optional()
1768
+ });
1769
+ var GREP_REPO_SYMBOL_SELECTIONS = {
1770
+ symbol_ref: "symbolRef",
1771
+ name: "name",
1772
+ qualified_path: "qualifiedPath",
1773
+ kind: "kind",
1774
+ category: "category",
1775
+ arity: "arity",
1776
+ is_public: "isPublic",
1777
+ file_path: "filePath",
1778
+ start_line: "startLine",
1779
+ end_line: "endLine",
1780
+ code: "code",
1781
+ caller_count: "callerCount",
1782
+ content_hash: "contentHash",
1783
+ parent_symbol_ref: "parentSymbolRef",
1784
+ parent_path: "parentPath"
1785
+ };
1786
+ function buildGrepRepoQuery(symbolFields) {
1787
+ const symbolSelection = (symbolFields ?? []).map((field) => GREP_REPO_SYMBOL_SELECTIONS[field]).filter((field) => Boolean(field)).filter((field, index, fields) => fields.indexOf(field) === index).join(`
1788
+ `);
1789
+ const symbolBlock = symbolSelection.length > 0 ? `
1790
+ symbol {
1791
+ ${symbolSelection}
1792
+ }` : "";
1793
+ return `
1794
+ query GrepRepo(
1795
+ $registry: Registry
1796
+ $packageName: String
1797
+ $repoUrl: String
1798
+ $gitRef: String
1799
+ $version: String
1800
+ $waitTimeoutMs: Int
1801
+ $pattern: String!
1802
+ $patternType: GrepPatternType
1803
+ $caseSensitive: Boolean
1804
+ $pathSelectors: [GrepPathSelectorInput!]
1805
+ $extensions: [String!]
1806
+ $excludeDocFiles: Boolean
1807
+ $excludeTestFiles: Boolean
1808
+ $allowUnscoped: Boolean
1809
+ $contextLinesBefore: Int
1810
+ $contextLinesAfter: Int
1811
+ $maxMatches: Int
1812
+ $maxMatchesPerFile: Int
1813
+ $cursor: String
1814
+ $symbolFields: [String!]
1815
+ ) {
1816
+ grepRepo(
1817
+ registry: $registry
1818
+ packageName: $packageName
1819
+ repoUrl: $repoUrl
1820
+ gitRef: $gitRef
1821
+ version: $version
1822
+ waitTimeoutMs: $waitTimeoutMs
1823
+ pattern: $pattern
1824
+ patternType: $patternType
1825
+ caseSensitive: $caseSensitive
1826
+ pathSelectors: $pathSelectors
1827
+ extensions: $extensions
1828
+ excludeDocFiles: $excludeDocFiles
1829
+ excludeTestFiles: $excludeTestFiles
1830
+ allowUnscoped: $allowUnscoped
1831
+ contextLinesBefore: $contextLinesBefore
1832
+ contextLinesAfter: $contextLinesAfter
1833
+ maxMatches: $maxMatches
1834
+ maxMatchesPerFile: $maxMatchesPerFile
1835
+ cursor: $cursor
1836
+ symbolFields: $symbolFields
1837
+ ) {
1838
+ matches {
1839
+ filePath
1840
+ line
1841
+ matchStartByte
1842
+ matchEndByte
1843
+ lineContent
1844
+ contextBefore
1845
+ contextAfter
1846
+ fileContentHash
1847
+ fileIntent
1848
+ symbolRowId${symbolBlock}
1849
+ }
1850
+ nextCursor
1851
+ totalMatches
1852
+ hasMore
1853
+ truncatedReason
1854
+ routeTaken
1855
+ filesScanned
1856
+ filesInScope
1857
+ binaryFilesSkipped
1858
+ filesTooLargeSkipped
1859
+ uniqueFilesMatched
1860
+ indexedVersion
1861
+ resolution {
1862
+ requestedVersion
1863
+ requestedRef
1864
+ resolvedRef
1865
+ commitSha
1866
+ }
1867
+ codeIndexState
1868
+ indexingRef
1869
+ availableVersions {
1870
+ version
1871
+ ref
1872
+ }
1873
+ }
1874
+ }`;
1875
+ }
1876
+ var graphQLResponseSchema = z.object({
1877
+ data: z.object({
1878
+ searchSymbols: searchSymbolsResponseSchema.nullable().optional()
1879
+ }).nullable().optional(),
1880
+ errors: z.array(graphQLErrorSchema).optional()
1881
+ });
1882
+ var unifiedSearchGraphQLResponseSchema = z.object({
1883
+ data: z.object({
1884
+ search: asyncUnifiedSearchResultSchema.nullable().optional()
1885
+ }).nullable().optional(),
1886
+ errors: z.array(graphQLErrorSchema).optional()
1887
+ });
1888
+ var unifiedSearchStatusGraphQLResponseSchema = z.object({
1889
+ data: z.object({
1890
+ discoverySearchProgress: unifiedSearchProgressSchema.nullable().optional()
1891
+ }).nullable().optional(),
1892
+ errors: z.array(graphQLErrorSchema).optional()
1893
+ });
1894
+
1895
+ class CodeNavigationServiceImpl {
1896
+ codeNavigationUrl;
1897
+ tokenProvider;
1898
+ fetchFn;
1899
+ constructor(codeNavigationUrl, tokenProvider, fetchFn = globalThis.fetch) {
1900
+ this.codeNavigationUrl = codeNavigationUrl;
1901
+ this.tokenProvider = tokenProvider;
1902
+ this.fetchFn = fetchFn;
1903
+ }
1904
+ async search(params) {
1905
+ return executeWithTokenRefresh({
1906
+ getToken: () => this.tokenProvider.getToken(),
1907
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
1908
+ shouldRefresh: (error) => error instanceof AuthenticationError,
1909
+ executeWithToken: (token) => this.executeUnifiedSearch(token, params)
1910
+ });
1911
+ }
1912
+ async searchStatus(searchRef) {
1913
+ return executeWithTokenRefresh({
1914
+ getToken: () => this.tokenProvider.getToken(),
1915
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
1916
+ shouldRefresh: (error) => error instanceof AuthenticationError,
1917
+ executeWithToken: (token) => this.executeUnifiedSearchStatus(token, searchRef)
1918
+ });
1919
+ }
1920
+ async searchSymbols(params) {
1921
+ return executeWithTokenRefresh({
1922
+ getToken: () => this.tokenProvider.getToken(),
1923
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
1924
+ shouldRefresh: (error) => error instanceof AuthenticationError,
1925
+ executeWithToken: (token) => this.executeSearchSymbols(token, params)
1926
+ });
1927
+ }
1928
+ async executeUnifiedSearch(token, params) {
1929
+ if (params.targets.length === 0) {
1930
+ throw new CodeNavigationValidationError("At least one search target is required.");
1931
+ }
1932
+ let response;
1933
+ try {
1934
+ response = await postPkgseerGraphql({
1935
+ endpointUrl: this.codeNavigationUrl,
1936
+ token,
1937
+ query: UNIFIED_SEARCH_QUERY,
1938
+ variables: {
1939
+ targets: params.targets.map((target) => ({
1940
+ registry: target.registry,
1941
+ name: target.packageName,
1942
+ version: target.version,
1943
+ repoUrl: target.repoUrl,
1944
+ gitRef: target.gitRef
1945
+ })),
1946
+ query: params.query,
1947
+ sources: params.sources,
1948
+ filters: params.filters,
1949
+ allowPartialResults: params.allowPartialResults ?? false,
1950
+ limit: params.limit,
1951
+ offset: params.offset,
1952
+ waitTimeoutMs: params.waitTimeoutMs
1953
+ },
1954
+ fetchFn: this.fetchFn
1955
+ });
1956
+ } catch (cause) {
1957
+ if (cause instanceof PkgseerTransportError) {
1958
+ throw new CodeNavigationNetworkError("Could not reach the code navigation service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
1959
+ }
1960
+ throw cause;
1961
+ }
1962
+ if (response.status < 200 || response.status >= 300) {
1963
+ throw this.createHttpError(response);
1964
+ }
1965
+ const parsed = unifiedSearchGraphQLResponseSchema.safeParse(response.parsedBody);
1966
+ if (!parsed.success) {
1967
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
1968
+ }
1969
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
1970
+ throw this.createGraphQLError(parsed.data.errors);
1971
+ }
1972
+ const data = parsed.data.data?.search;
1973
+ if (!data) {
1974
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
1975
+ }
1976
+ return this.normaliseUnifiedSearchOutcome(data);
1977
+ }
1978
+ async executeUnifiedSearchStatus(token, searchRef) {
1979
+ let response;
1980
+ try {
1981
+ response = await postPkgseerGraphql({
1982
+ endpointUrl: this.codeNavigationUrl,
1983
+ token,
1984
+ query: UNIFIED_SEARCH_STATUS_QUERY,
1985
+ variables: {
1986
+ searchRef,
1987
+ includeResults: true
1988
+ },
1989
+ fetchFn: this.fetchFn
1990
+ });
1991
+ } catch (cause) {
1992
+ if (cause instanceof PkgseerTransportError) {
1993
+ throw new CodeNavigationNetworkError("Could not reach the code navigation service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
1994
+ }
1995
+ throw cause;
1996
+ }
1997
+ if (response.status < 200 || response.status >= 300) {
1998
+ throw this.createHttpError(response);
1999
+ }
2000
+ const parsed = unifiedSearchStatusGraphQLResponseSchema.safeParse(response.parsedBody);
2001
+ if (!parsed.success) {
2002
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2003
+ }
2004
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
2005
+ throw this.createGraphQLError(parsed.data.errors);
2006
+ }
2007
+ const data = parsed.data.data?.discoverySearchProgress;
2008
+ if (!data) {
2009
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2010
+ }
2011
+ const progress = this.normaliseUnifiedSearchProgress(data);
2012
+ const result = data.results ? this.normaliseUnifiedSearchResult(data.results) : undefined;
2013
+ if (result && progress.status === "COMPLETED") {
2014
+ return {
2015
+ state: "completed",
2016
+ completed: true,
2017
+ searchRef: progress.searchRef,
2018
+ result,
2019
+ progress
2020
+ };
2021
+ }
2022
+ return {
2023
+ state: "incomplete",
2024
+ completed: false,
2025
+ searchRef: progress.searchRef,
2026
+ result,
2027
+ progress
2028
+ };
2029
+ }
2030
+ async executeSearchSymbols(token, params) {
2031
+ if (!params.query && (!params.keywords || params.keywords.length === 0)) {
2032
+ throw new InvalidSearchSymbolsRequestError("Either query or keywords must be provided.");
2033
+ }
2034
+ let response;
2035
+ try {
2036
+ response = await postPkgseerGraphql({
2037
+ endpointUrl: this.codeNavigationUrl,
2038
+ token,
2039
+ query: SEARCH_SYMBOLS_QUERY,
2040
+ variables: {
2041
+ registry: params.target.registry,
2042
+ packageName: params.target.packageName,
2043
+ repoUrl: params.target.repoUrl,
2044
+ gitRef: params.target.gitRef,
2045
+ query: params.query,
2046
+ keywords: params.keywords,
2047
+ matchMode: params.matchMode,
2048
+ kind: params.kind,
2049
+ category: params.category,
2050
+ filePath: params.filePath,
2051
+ version: params.target.version,
2052
+ limit: params.limit,
2053
+ fileIntent: params.fileIntent,
2054
+ waitTimeoutMs: params.waitTimeoutMs
2055
+ },
2056
+ fetchFn: this.fetchFn
2057
+ });
2058
+ } catch (cause) {
2059
+ if (cause instanceof PkgseerTransportError) {
2060
+ throw new CodeNavigationNetworkError("Could not reach the code navigation service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
2061
+ }
2062
+ throw cause;
2063
+ }
2064
+ if (response.status < 200 || response.status >= 300) {
2065
+ throw this.createHttpError(response);
2066
+ }
2067
+ const parsed = graphQLResponseSchema.safeParse(response.parsedBody);
2068
+ if (!parsed.success) {
2069
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2070
+ }
2071
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
2072
+ throw this.createGraphQLError(parsed.data.errors);
2073
+ }
2074
+ const data = parsed.data.data?.searchSymbols;
2075
+ if (!data) {
2076
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2077
+ }
2078
+ if (data.codeIndexState === "INDEXING") {
2079
+ throw new CodeNavigationIndexingError(this.createIndexingMessage(data.indexingRef ?? undefined), data.indexingRef ?? undefined, data.availableVersions?.map((entry) => ({
2080
+ version: entry.version ?? undefined,
2081
+ ref: entry.ref
2082
+ })));
2083
+ }
2084
+ if (data.codeIndexState === "UNRESOLVABLE") {
2085
+ throw new CodeNavigationUnresolvableError("The requested target or version could not be resolved.");
2086
+ }
2087
+ return {
2088
+ results: data.results.map((entry) => ({
2089
+ name: entry.name ?? undefined,
2090
+ filePath: entry.filePath ?? undefined,
2091
+ startLine: entry.startLine ?? undefined,
2092
+ endLine: entry.endLine ?? undefined,
2093
+ preview: entry.preview ?? undefined,
2094
+ code: entry.code ?? undefined,
2095
+ language: entry.language ?? undefined,
2096
+ symbolRef: entry.symbolRef ?? undefined,
2097
+ qualifiedPath: entry.qualifiedPath ?? undefined,
2098
+ kind: entry.kind ?? undefined,
2099
+ category: entry.category ?? undefined,
2100
+ arity: entry.arity ?? undefined,
2101
+ isPublic: entry.isPublic ?? undefined,
2102
+ containedSymbols: entry.containedSymbols ?? undefined
2103
+ })),
2104
+ totalMatches: data.totalMatches,
2105
+ hasMore: data.hasMore,
2106
+ version: data.indexedVersion ?? undefined,
2107
+ resolution: data.resolution ? {
2108
+ requestedVersion: data.resolution.requestedVersion ?? undefined,
2109
+ requestedRef: data.resolution.requestedRef ?? undefined,
2110
+ resolvedRef: data.resolution.resolvedRef ?? undefined,
2111
+ commitSha: data.resolution.commitSha ?? undefined
2112
+ } : undefined,
2113
+ hint: data.diagnostics?.hint ?? undefined,
2114
+ warning: data.warning ?? undefined
2115
+ };
2116
+ }
2117
+ createHttpError(response) {
2118
+ const status = response.status;
2119
+ const detail = parseDetail2(response.responseBody);
2120
+ if (status === 401) {
2121
+ return new AuthenticationError("Authentication required. Run `githits login` to authenticate.");
2122
+ }
2123
+ if (status === 403) {
2124
+ return new CodeNavigationAccessError(detail ?? "Code navigation access denied.");
2125
+ }
2126
+ if (status >= 500) {
2127
+ return new CodeNavigationBackendError(detail ? `Server error (${status}): ${detail}` : `Server error (${status})`, status);
2128
+ }
2129
+ return new CodeNavigationBackendError(detail ?? `Request failed with status ${status}`, status);
2130
+ }
2131
+ createGraphQLError(errors) {
2132
+ const message = errors.map((error) => error.message).join(", ");
2133
+ const extensions = getPrimaryExtensions(errors);
2134
+ const code = typeof extensions?.code === "string" ? extensions.code : undefined;
2135
+ const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
2136
+ const indexingRef = getGraphQLIndexingRef(errors);
2137
+ switch (code) {
2138
+ case "PACKAGE_INDEXING":
2139
+ return new CodeNavigationIndexingError(this.createIndexingMessage(indexingRef), indexingRef, parseAvailableVersions(extensions));
2140
+ case "GREP_PATTERN_TOO_SHORT":
2141
+ case "GREP_PATTERN_TOO_LONG":
2142
+ case "GREP_PATTERN_INVALID":
2143
+ case "GREP_INVALID_REGEX":
2144
+ case "GREP_UNSUPPORTED_PATTERN":
2145
+ case "GREP_PATTERN_TOO_UNSELECTIVE":
2146
+ case "GREP_SCOPE_REQUIRED":
2147
+ case "GREP_SELECTOR_INVALID":
2148
+ case "GREP_CURSOR_INVALID":
2149
+ case "GREP_CONTEXT_TOO_LARGE":
2150
+ case "GREP_CONTEXT_NEGATIVE":
2151
+ case "GREP_MAX_MATCHES_TOO_LARGE":
2152
+ case "GREP_MAX_MATCHES_INVALID":
2153
+ return new CodeNavigationValidationError(message);
2154
+ case "VERSION_NOT_FOUND":
2155
+ return new CodeNavigationVersionNotFoundError(message, typeof extensions?.package === "string" ? extensions.package : undefined, typeof extensions?.requested_version === "string" ? extensions.requested_version : undefined, typeof extensions?.latest_indexed === "string" ? extensions.latest_indexed : undefined, parseAvailableVersions(extensions));
2156
+ case "NOT_FOUND":
2157
+ case "PACKAGE_NOT_FOUND":
2158
+ case "NO_REPOSITORY_URL":
2159
+ return new CodeNavigationTargetNotFoundError(message);
2160
+ case "FILE_NOT_FOUND":
2161
+ return new CodeNavigationFileNotFoundError(message, typeof extensions?.file_path === "string" ? extensions.file_path : typeof extensions?.filePath === "string" ? extensions.filePath : undefined);
2162
+ case "UNSUPPORTED_REGISTRY":
2163
+ case "VALIDATION_ERROR":
2164
+ return new CodeNavigationValidationError(message);
2165
+ case "FEATURE_FLAG_REQUIRED":
2166
+ return new CodeNavigationFeatureFlagRequiredError(message);
2167
+ case "UNAUTHORIZED":
2168
+ return new AuthenticationError("Authentication required. Run `githits login` to authenticate.");
2169
+ case "FORBIDDEN":
2170
+ return new CodeNavigationAccessError("Code navigation access denied. This feature may not be enabled for your account.");
2171
+ case "UPSTREAM_ERROR":
2172
+ case "TIMEOUT":
2173
+ case "RATE_LIMITED":
2174
+ case "GREP_FILE_TOO_LARGE":
2175
+ case "GREP_TIMEOUT":
2176
+ case "GREP_SERVICE_UNAVAILABLE":
2177
+ case "GREP_FAILED":
2178
+ case "GREP_INDEX_NOT_AVAILABLE":
2179
+ case "INTERNAL_ERROR":
2180
+ case "UNKNOWN_ERROR":
2181
+ return new CodeNavigationBackendError(message, undefined, code, retryable);
2182
+ default:
2183
+ break;
2184
+ }
2185
+ if (code === undefined) {
2186
+ if (isAuthMessage(message)) {
2187
+ return new CodeNavigationAccessError("Code navigation access denied. This feature may not be enabled for your account.");
2188
+ }
2189
+ if (isUnresolvableMessage(message)) {
2190
+ return new CodeNavigationUnresolvableError(message);
2191
+ }
2192
+ if (isTargetNotFoundMessage(message)) {
2193
+ return new CodeNavigationTargetNotFoundError(message);
2194
+ }
2195
+ }
2196
+ return new CodeNavigationBackendError(message, undefined, code, retryable);
2197
+ }
2198
+ createIndexingMessage(indexingRef) {
2199
+ const base = "Target is still indexing. Indexing usually completes within 30 seconds. Retry this request, or pass a longer wait timeout (CLI: `--wait 60000`, MCP: `wait_timeout_ms: 60000`) to block until ready.";
2200
+ if (indexingRef) {
2201
+ return `${base} Indexing reference: ${indexingRef}.`;
2202
+ }
2203
+ return base;
2204
+ }
2205
+ normaliseUnifiedSearchOutcome(data) {
2206
+ const progress = data.progress ? this.normaliseUnifiedSearchProgress(data.progress) : undefined;
2207
+ if (data.completed) {
2208
+ if (!data.result) {
2209
+ throw new MalformedCodeNavigationResponseError("Completed unified search response missing result payload.");
2210
+ }
2211
+ return {
2212
+ state: "completed",
2213
+ completed: true,
2214
+ searchRef: data.searchRef ?? undefined,
2215
+ result: this.normaliseUnifiedSearchResult(data.result),
2216
+ progress
2217
+ };
2218
+ }
2219
+ const searchRef = data.searchRef ?? progress?.searchRef;
2220
+ if (!searchRef) {
2221
+ throw new MalformedCodeNavigationResponseError("Incomplete unified search response missing search reference.");
2222
+ }
2223
+ const result = data.result ? this.normaliseUnifiedSearchResult(data.result) : undefined;
2224
+ return {
2225
+ state: "incomplete",
2226
+ completed: false,
2227
+ searchRef,
2228
+ result,
2229
+ progress
2230
+ };
2231
+ }
2232
+ normaliseUnifiedSearchResult(result) {
2233
+ return {
2234
+ query: result.query,
2235
+ queryWarnings: result.queryWarnings,
2236
+ sources: result.sources,
2237
+ results: result.results.map((entry) => ({
2238
+ id: entry.id,
2239
+ resultType: entry.resultType,
2240
+ targetLabel: entry.targetLabel,
2241
+ title: entry.title ?? undefined,
2242
+ summary: entry.summary ?? undefined,
2243
+ score: entry.score ?? undefined,
2244
+ highlights: entry.highlights ? {
2245
+ title: entry.highlights.title ?? undefined,
2246
+ summary: entry.highlights.summary ?? undefined
2247
+ } : undefined,
2248
+ locator: {
2249
+ registry: entry.locator.registry ?? undefined,
2250
+ packageName: entry.locator.packageName ?? undefined,
2251
+ version: entry.locator.version ?? undefined,
2252
+ pageId: entry.locator.pageId ?? undefined,
2253
+ repoUrl: entry.locator.repoUrl ?? undefined,
2254
+ gitRef: entry.locator.gitRef ?? undefined,
2255
+ filePath: entry.locator.filePath ?? undefined,
2256
+ startLine: entry.locator.startLine ?? undefined,
2257
+ endLine: entry.locator.endLine ?? undefined,
2258
+ fileContentHash: entry.locator.fileContentHash ?? undefined,
2259
+ symbolRef: entry.locator.symbolRef ?? undefined,
2260
+ qualifiedPath: entry.locator.qualifiedPath ?? undefined,
2261
+ kind: entry.locator.kind ?? undefined,
2262
+ category: entry.locator.category ?? undefined,
2263
+ language: entry.locator.language ?? undefined
2264
+ }
2265
+ })),
2266
+ page: {
2267
+ offset: result.page.offset,
2268
+ limit: result.page.limit,
2269
+ returned: result.page.returned,
2270
+ hasMore: result.page.hasMore
2271
+ },
2272
+ partialResults: result.partialResults,
2273
+ sourceStatus: result.sourceStatus.map((entry) => ({
2274
+ source: entry.source,
2275
+ targetLabel: entry.targetLabel,
2276
+ indexingStatus: entry.indexingStatus ?? undefined,
2277
+ codeIndexState: entry.codeIndexState ?? undefined,
2278
+ resultCount: entry.resultCount ?? undefined,
2279
+ appliedFilters: entry.appliedFilters,
2280
+ ignoredFilters: entry.ignoredFilters,
2281
+ incompatibleFilters: entry.incompatibleFilters,
2282
+ appliedQueryFeatures: entry.appliedQueryFeatures,
2283
+ ignoredQueryFeatures: entry.ignoredQueryFeatures,
2284
+ incompatibleQueryFeatures: entry.incompatibleQueryFeatures,
2285
+ note: entry.note ?? undefined
2286
+ }))
2287
+ };
2288
+ }
2289
+ normaliseUnifiedSearchProgress(progress) {
2290
+ return {
2291
+ searchRef: progress.searchRef,
2292
+ status: progress.status,
2293
+ targetsTotal: progress.targetsTotal,
2294
+ targetsReady: progress.targetsReady,
2295
+ elapsedMs: progress.elapsedMs,
2296
+ query: progress.query,
2297
+ queryWarnings: progress.queryWarnings,
2298
+ sources: progress.sources,
2299
+ expiresAt: progress.expiresAt ?? undefined
2300
+ };
2301
+ }
2302
+ throwIfIndexing(data) {
2303
+ if (data.codeIndexState === "INDEXING") {
2304
+ throw new CodeNavigationIndexingError(this.createIndexingMessage(data.indexingRef ?? undefined), data.indexingRef ?? undefined, data.availableVersions?.map((entry) => ({
2305
+ version: entry.version ?? undefined,
2306
+ ref: entry.ref
2307
+ })));
2308
+ }
2309
+ }
2310
+ async listFiles(params) {
2311
+ return executeWithTokenRefresh({
2312
+ getToken: () => this.tokenProvider.getToken(),
2313
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
2314
+ shouldRefresh: (error) => error instanceof AuthenticationError,
2315
+ executeWithToken: (token) => this.executeListFiles(token, params)
2316
+ });
2317
+ }
2318
+ async executeListFiles(token, params) {
2319
+ let response;
2320
+ try {
2321
+ response = await postPkgseerGraphql({
2322
+ endpointUrl: this.codeNavigationUrl,
2323
+ token,
2324
+ query: LIST_REPO_FILES_QUERY,
2325
+ variables: {
2326
+ registry: params.target.registry,
2327
+ packageName: params.target.packageName,
2328
+ repoUrl: params.target.repoUrl,
2329
+ gitRef: params.target.gitRef,
2330
+ version: params.target.version,
2331
+ pathPrefix: params.pathPrefix,
2332
+ limit: params.limit,
2333
+ waitTimeoutMs: params.waitTimeoutMs
2334
+ },
2335
+ fetchFn: this.fetchFn
2336
+ });
2337
+ } catch (cause) {
2338
+ if (cause instanceof PkgseerTransportError) {
2339
+ throw new CodeNavigationNetworkError("Could not reach the code navigation service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
2340
+ }
2341
+ throw cause;
2342
+ }
2343
+ if (response.status < 200 || response.status >= 300) {
2344
+ throw this.createHttpError(response);
2345
+ }
2346
+ const parsed = listRepoFilesGraphQLResponseSchema.safeParse(response.parsedBody);
2347
+ if (!parsed.success) {
2348
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2349
+ }
2350
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
2351
+ throw this.createGraphQLError(parsed.data.errors);
2352
+ }
2353
+ const data = parsed.data.data?.listRepoFiles;
2354
+ if (!data) {
2355
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2356
+ }
2357
+ this.throwIfIndexing(data);
2358
+ return {
2359
+ files: data.files.map((entry) => ({
2360
+ path: entry.path,
2361
+ name: entry.name ?? undefined,
2362
+ language: entry.language ?? undefined,
2363
+ fileType: entry.fileType ?? undefined,
2364
+ byteSize: entry.byteSize ?? undefined
2365
+ })),
2366
+ total: data.total,
2367
+ hasMore: data.hasMore,
2368
+ indexedVersion: data.indexedVersion ?? undefined,
2369
+ resolution: data.resolution ? {
2370
+ requestedVersion: data.resolution.requestedVersion ?? undefined,
2371
+ requestedRef: data.resolution.requestedRef ?? undefined,
2372
+ resolvedRef: data.resolution.resolvedRef ?? undefined,
2373
+ commitSha: data.resolution.commitSha ?? undefined
2374
+ } : undefined,
2375
+ hint: data.diagnostics?.hint ?? undefined
2376
+ };
2377
+ }
2378
+ async readFile(params) {
2379
+ return executeWithTokenRefresh({
2380
+ getToken: () => this.tokenProvider.getToken(),
2381
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
2382
+ shouldRefresh: (error) => error instanceof AuthenticationError,
2383
+ executeWithToken: (token) => this.executeReadFile(token, params)
2384
+ });
2385
+ }
2386
+ async executeReadFile(token, params) {
2387
+ let response;
2388
+ try {
2389
+ response = await postPkgseerGraphql({
2390
+ endpointUrl: this.codeNavigationUrl,
2391
+ token,
2392
+ query: FETCH_CODE_CONTEXT_QUERY,
2393
+ variables: {
2394
+ registry: params.target.registry,
2395
+ packageName: params.target.packageName,
2396
+ repoUrl: params.target.repoUrl,
2397
+ gitRef: params.target.gitRef,
2398
+ version: params.target.version,
2399
+ filePath: params.filePath,
2400
+ startLine: params.startLine,
2401
+ endLine: params.endLine,
2402
+ waitTimeoutMs: params.waitTimeoutMs
2403
+ },
2404
+ fetchFn: this.fetchFn
2405
+ });
2406
+ } catch (cause) {
2407
+ if (cause instanceof PkgseerTransportError) {
2408
+ throw new CodeNavigationNetworkError("Could not reach the code navigation service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
2409
+ }
2410
+ throw cause;
2411
+ }
2412
+ if (response.status < 200 || response.status >= 300) {
2413
+ throw this.createHttpError(response);
2414
+ }
2415
+ const parsed = fetchCodeContextGraphQLResponseSchema.safeParse(response.parsedBody);
2416
+ if (!parsed.success) {
2417
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2418
+ }
2419
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
2420
+ throw this.createGraphQLError(parsed.data.errors);
2421
+ }
2422
+ const data = parsed.data.data?.fetchCodeContext;
2423
+ if (!data) {
2424
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2425
+ }
2426
+ this.throwIfIndexing({
2427
+ codeIndexState: data.codeIndexState,
2428
+ indexingRef: data.indexingRef
2429
+ });
2430
+ return {
2431
+ filePath: data.filePath ?? undefined,
2432
+ language: data.language ?? undefined,
2433
+ totalLines: data.totalLines ?? undefined,
2434
+ startLine: data.startLine ?? undefined,
2435
+ endLine: data.endLine ?? undefined,
2436
+ content: data.content ?? undefined,
2437
+ isBinary: data.isBinary ?? undefined
2438
+ };
2439
+ }
2440
+ async grepRepo(params) {
2441
+ return executeWithTokenRefresh({
2442
+ getToken: () => this.tokenProvider.getToken(),
2443
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
2444
+ shouldRefresh: (error) => error instanceof AuthenticationError,
2445
+ executeWithToken: (token) => this.executeGrepRepo(token, params)
2446
+ });
2447
+ }
2448
+ async executeGrepRepo(token, params) {
2449
+ let response;
2450
+ try {
2451
+ response = await postPkgseerGraphql({
2452
+ endpointUrl: this.codeNavigationUrl,
2453
+ token,
2454
+ query: buildGrepRepoQuery(params.symbolFields),
2455
+ variables: {
2456
+ registry: params.target.registry,
2457
+ packageName: params.target.packageName,
2458
+ repoUrl: params.target.repoUrl,
2459
+ gitRef: params.target.gitRef,
2460
+ version: params.target.version,
2461
+ waitTimeoutMs: params.waitTimeoutMs,
2462
+ pattern: params.pattern,
2463
+ patternType: params.patternType,
2464
+ caseSensitive: params.caseSensitive,
2465
+ pathSelectors: params.pathSelectors?.map((entry) => ({
2466
+ kind: entry.kind,
2467
+ value: entry.value
2468
+ })),
2469
+ extensions: params.extensions,
2470
+ excludeDocFiles: params.excludeDocFiles,
2471
+ excludeTestFiles: params.excludeTestFiles,
2472
+ allowUnscoped: params.allowUnscoped,
2473
+ contextLinesBefore: params.contextLinesBefore,
2474
+ contextLinesAfter: params.contextLinesAfter,
2475
+ maxMatches: params.maxMatches,
2476
+ maxMatchesPerFile: params.maxMatchesPerFile,
2477
+ cursor: params.cursor,
2478
+ symbolFields: params.symbolFields
2479
+ },
2480
+ fetchFn: this.fetchFn
2481
+ });
2482
+ } catch (cause) {
2483
+ if (cause instanceof PkgseerTransportError) {
2484
+ throw new CodeNavigationNetworkError("Could not reach the code navigation service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
2485
+ }
2486
+ throw cause;
2487
+ }
2488
+ if (response.status < 200 || response.status >= 300) {
2489
+ throw this.createHttpError(response);
2490
+ }
2491
+ const parsed = grepRepoGraphQLResponseSchema.safeParse(response.parsedBody);
2492
+ if (!parsed.success) {
2493
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2494
+ }
2495
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
2496
+ throw this.createGraphQLError(parsed.data.errors);
2497
+ }
2498
+ const data = parsed.data.data?.grepRepo;
2499
+ if (!data) {
2500
+ throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
2501
+ }
2502
+ this.throwIfIndexing(data);
2503
+ return {
2504
+ matches: data.matches.map((entry) => ({
2505
+ filePath: entry.filePath,
2506
+ line: entry.line,
2507
+ matchStartByte: entry.matchStartByte,
2508
+ matchEndByte: entry.matchEndByte,
2509
+ lineContent: entry.lineContent,
2510
+ contextBefore: entry.contextBefore ?? undefined,
2511
+ contextAfter: entry.contextAfter ?? undefined,
2512
+ fileContentHash: entry.fileContentHash ?? undefined,
2513
+ fileIntent: entry.fileIntent ?? undefined,
2514
+ symbolRowId: entry.symbolRowId ?? undefined,
2515
+ symbol: entry.symbol ? {
2516
+ symbolRef: entry.symbol.symbolRef,
2517
+ name: entry.symbol.name,
2518
+ qualifiedPath: entry.symbol.qualifiedPath ?? undefined,
2519
+ kind: entry.symbol.kind ?? undefined,
2520
+ category: entry.symbol.category ?? undefined,
2521
+ arity: entry.symbol.arity ?? undefined,
2522
+ isPublic: entry.symbol.isPublic ?? undefined,
2523
+ filePath: entry.symbol.filePath ?? undefined,
2524
+ startLine: entry.symbol.startLine ?? undefined,
2525
+ endLine: entry.symbol.endLine ?? undefined,
2526
+ code: entry.symbol.code ?? undefined,
2527
+ callerCount: entry.symbol.callerCount ?? undefined,
2528
+ contentHash: entry.symbol.contentHash ?? undefined,
2529
+ parentSymbolRef: entry.symbol.parentSymbolRef ?? undefined,
2530
+ parentPath: entry.symbol.parentPath ?? undefined
2531
+ } : undefined
2532
+ })),
2533
+ nextCursor: data.nextCursor ?? undefined,
2534
+ hasMore: data.hasMore,
2535
+ truncatedReason: data.truncatedReason,
2536
+ routeTaken: data.routeTaken ?? undefined,
2537
+ filesScanned: data.filesScanned,
2538
+ filesInScope: data.filesInScope,
2539
+ binaryFilesSkipped: data.binaryFilesSkipped,
2540
+ filesTooLargeSkipped: data.filesTooLargeSkipped,
2541
+ totalMatches: data.totalMatches,
2542
+ uniqueFilesMatched: data.uniqueFilesMatched,
2543
+ indexedVersion: data.indexedVersion ?? undefined,
2544
+ resolution: data.resolution ? {
2545
+ requestedVersion: data.resolution.requestedVersion ?? undefined,
2546
+ requestedRef: data.resolution.requestedRef ?? undefined,
2547
+ resolvedRef: data.resolution.resolvedRef ?? undefined,
2548
+ commitSha: data.resolution.commitSha ?? undefined
2549
+ } : undefined
2550
+ };
2551
+ }
2552
+ }
2553
+ function parseDetail2(body) {
2554
+ if (!body)
2555
+ return;
2556
+ try {
2557
+ const parsed = JSON.parse(body);
2558
+ if (typeof parsed.detail === "string")
2559
+ return parsed.detail;
2560
+ if (typeof parsed.error === "string")
2561
+ return parsed.error;
2562
+ } catch {
2563
+ return body;
2564
+ }
2565
+ return;
2566
+ }
2567
+ function getPrimaryExtensions(errors) {
2568
+ for (const error of errors) {
2569
+ if (error.extensions && Object.keys(error.extensions).length > 0) {
2570
+ return error.extensions;
2571
+ }
2572
+ }
2573
+ return;
2574
+ }
2575
+ function getGraphQLIndexingRef(errors) {
2576
+ for (const error of errors) {
2577
+ const indexingRef = error.extensions?.indexing_ref ?? error.extensions?.indexingRef;
2578
+ if (typeof indexingRef === "string")
2579
+ return indexingRef;
2580
+ }
2581
+ return;
2582
+ }
2583
+ function parseAvailableVersions(extensions) {
2584
+ const raw = extensions?.available_versions ?? extensions?.availableVersions;
2585
+ if (!Array.isArray(raw))
2586
+ return;
2587
+ const parsed = [];
2588
+ for (const item of raw) {
2589
+ if (item && typeof item === "object" && "ref" in item) {
2590
+ const entry = item;
2591
+ if (typeof entry.ref === "string") {
2592
+ parsed.push({
2593
+ ref: entry.ref,
2594
+ version: typeof entry.version === "string" ? entry.version : undefined
2595
+ });
2596
+ }
2597
+ }
2598
+ }
2599
+ return parsed.length > 0 ? parsed : undefined;
2600
+ }
2601
+ function isAuthMessage(message) {
2602
+ const lower = message.toLowerCase();
2603
+ return lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("permission") || lower.includes("authentication");
2604
+ }
2605
+ function isTargetNotFoundMessage(message) {
2606
+ const lower = message.toLowerCase();
2607
+ return lower.includes("not found") || lower.includes("unknown package") || lower.includes("no such package") || lower.includes("does not exist");
2608
+ }
2609
+ function isUnresolvableMessage(message) {
2610
+ const lower = message.toLowerCase();
2611
+ return lower.includes("could not resolve") || lower.includes("cannot resolve");
2612
+ }
2613
+ // src/services/config.ts
2614
+ var DEFAULT_MCP_URL = "https://mcp.githits.com";
2615
+ var DEFAULT_API_URL = "https://api.githits.com";
2616
+ var DEFAULT_CODE_NAV_URL = "https://pkgseer.dev";
2617
+ function getMcpUrl() {
2618
+ return process.env.GITHITS_MCP_URL ?? DEFAULT_MCP_URL;
2619
+ }
2620
+ function getApiUrl() {
2621
+ return process.env.GITHITS_API_URL ?? DEFAULT_API_URL;
2622
+ }
2623
+ function getCodeNavigationUrl() {
2624
+ const explicitUrl = process.env.GITHITS_CODE_NAV_URL ?? process.env.PKGSEER_URL;
2625
+ if (explicitUrl) {
2626
+ return explicitUrl;
2627
+ }
2628
+ const usingDefaultEnvironment = getMcpUrl() === DEFAULT_MCP_URL && getApiUrl() === DEFAULT_API_URL;
2629
+ return usingDefaultEnvironment ? DEFAULT_CODE_NAV_URL : undefined;
2630
+ }
2631
+ function getEnvApiToken() {
2632
+ return process.env.GITHITS_API_TOKEN;
2633
+ }
2634
+ function isCodeNavigationCliOverrideEnabled() {
2635
+ const raw = process.env.GITHITS_CODE_NAVIGATION;
2636
+ if (!raw)
2637
+ return false;
2638
+ const normalized = raw.toLowerCase();
2639
+ return normalized !== "0" && normalized !== "false" && normalized !== "";
2640
+ }
2641
+ // src/services/exec-service.ts
2642
+ import { spawn } from "node:child_process";
2643
+
2644
+ class ExecServiceImpl {
2645
+ async exec(command, args) {
2646
+ return new Promise((resolve, reject) => {
2647
+ const child = spawn(command, args, {
2648
+ stdio: ["ignore", "pipe", "pipe"],
2649
+ env: { ...process.env },
2650
+ ...process.platform === "win32" && { shell: true }
2651
+ });
2652
+ const stdoutChunks = [];
2653
+ const stderrChunks = [];
2654
+ child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
2655
+ child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
2656
+ child.on("error", (error) => {
2657
+ reject(error);
2658
+ });
2659
+ child.on("close", (code) => {
2660
+ resolve({
2661
+ exitCode: code ?? 1,
2662
+ stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
2663
+ stderr: Buffer.concat(stderrChunks).toString("utf-8")
2664
+ });
2665
+ });
2666
+ });
2667
+ }
2668
+ }
2669
+ // src/services/filesystem-service.ts
2670
+ import {
2671
+ mkdir,
2672
+ readdir,
2673
+ readFile,
2674
+ rename,
2675
+ stat,
2676
+ unlink,
2677
+ writeFile
2678
+ } from "node:fs/promises";
2679
+ import { homedir } from "node:os";
2680
+ import { dirname, join } from "node:path";
2681
+
2682
+ class FileSystemServiceImpl {
2683
+ async readFile(path) {
2684
+ return readFile(path, "utf-8");
2685
+ }
2686
+ async writeFile(path, contents, mode) {
2687
+ await writeFile(path, contents, { mode });
2688
+ }
2689
+ async deleteFile(path) {
2690
+ try {
2691
+ await unlink(path);
2692
+ } catch (error) {
2693
+ if (error.code !== "ENOENT") {
2694
+ throw error;
2695
+ }
2696
+ }
2697
+ }
2698
+ async exists(path) {
2699
+ try {
2700
+ await stat(path);
2701
+ return true;
2702
+ } catch {
2703
+ return false;
2704
+ }
2705
+ }
2706
+ async ensureDir(path, mode) {
2707
+ await mkdir(path, { recursive: true, mode });
2708
+ }
2709
+ getHomeDir() {
2710
+ return homedir();
2711
+ }
2712
+ joinPath(...segments) {
2713
+ return join(...segments);
2714
+ }
2715
+ getCwd() {
2716
+ return process.cwd();
2717
+ }
2718
+ getDirname(path) {
2719
+ return dirname(path);
2720
+ }
2721
+ async readdir(path) {
2722
+ return readdir(path);
2723
+ }
2724
+ async isDirectory(path) {
2725
+ try {
2726
+ const stats = await stat(path);
2727
+ return stats.isDirectory();
2728
+ } catch {
2729
+ return false;
2730
+ }
2731
+ }
2732
+ async atomicWriteFile(path, contents) {
2733
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
2734
+ let mode = 384;
2735
+ try {
2736
+ const existing = await stat(path);
2737
+ mode = existing.mode & 511;
2738
+ } catch {}
2739
+ try {
2740
+ await writeFile(tmpPath, contents, { mode });
2741
+ await rename(tmpPath, path);
2742
+ } catch (error) {
2743
+ try {
2744
+ await unlink(tmpPath);
2745
+ } catch {}
2746
+ throw error;
2747
+ }
2748
+ }
2749
+ }
2750
+ // src/services/keychain-auth-storage.ts
2751
+ var SERVICE_NAME = "githits";
2752
+ var TOKEN_PREFIX = "v1:tokens:";
2753
+ var CLIENT_PREFIX = "v1:client:";
2754
+ function parseJsonOrNull2(json) {
2755
+ if (json === null)
2756
+ return null;
2757
+ try {
2758
+ const parsed = JSON.parse(json);
2759
+ if (typeof parsed !== "object" || parsed === null)
2760
+ return null;
2761
+ return parsed;
2762
+ } catch {
2763
+ return null;
2764
+ }
2765
+ }
2766
+ function isValidTokenData(data) {
2767
+ if (typeof data !== "object" || data === null)
2768
+ return false;
2769
+ const d = data;
2770
+ return typeof d.accessToken === "string" && d.accessToken.length > 0 && typeof d.refreshToken === "string" && d.refreshToken.length > 0 && typeof d.createdAt === "string" && d.createdAt.length > 0 && (d.expiresAt === null || typeof d.expiresAt === "string" && d.expiresAt.length > 0);
2771
+ }
2772
+ function isValidClientRegistration(data) {
2773
+ if (typeof data !== "object" || data === null)
2774
+ return false;
2775
+ const d = data;
2776
+ return typeof d.clientId === "string" && d.clientId.length > 0 && typeof d.clientSecret === "string" && d.clientSecret.length > 0 && typeof d.redirectUri === "string" && d.redirectUri.length > 0 && typeof d.registeredAt === "string" && d.registeredAt.length > 0;
2777
+ }
2778
+
2779
+ class KeychainAuthStorage {
2780
+ keyring;
2781
+ constructor(keyring) {
2782
+ this.keyring = keyring;
2783
+ }
2784
+ async loadTokens(baseUrl2) {
2785
+ const key = `${TOKEN_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2786
+ const json = this.keyring.getPassword(SERVICE_NAME, key);
2787
+ const data = parseJsonOrNull2(json);
2788
+ if (data !== null && !isValidTokenData(data))
2789
+ return null;
2790
+ return data;
2791
+ }
2792
+ async saveTokens(baseUrl2, data) {
2793
+ const key = `${TOKEN_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2794
+ this.keyring.setPassword(SERVICE_NAME, key, JSON.stringify(data));
2795
+ }
2796
+ async clearTokens(baseUrl2) {
2797
+ const key = `${TOKEN_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2798
+ this.keyring.deletePassword(SERVICE_NAME, key);
2799
+ }
2800
+ async loadClient(baseUrl2) {
2801
+ const key = `${CLIENT_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2802
+ const json = this.keyring.getPassword(SERVICE_NAME, key);
2803
+ const data = parseJsonOrNull2(json);
2804
+ if (data !== null && !isValidClientRegistration(data))
2805
+ return null;
2806
+ return data;
2807
+ }
2808
+ async saveClient(baseUrl2, data) {
2809
+ const key = `${CLIENT_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2810
+ this.keyring.setPassword(SERVICE_NAME, key, JSON.stringify(data));
2811
+ }
2812
+ async clearClient(baseUrl2) {
2813
+ const key = `${CLIENT_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2814
+ this.keyring.deletePassword(SERVICE_NAME, key);
2815
+ }
2816
+ getStorageLocation() {
2817
+ switch (process.platform) {
2818
+ case "darwin":
2819
+ return "macOS Keychain (githits)";
2820
+ case "win32":
2821
+ return "Windows Credential Manager (githits)";
2822
+ default:
2823
+ return "System keychain (githits)";
2824
+ }
2825
+ }
2826
+ }
2827
+ // src/services/keyring-service.ts
2828
+ import { Entry } from "@napi-rs/keyring";
2829
+
2830
+ class KeychainUnavailableError extends Error {
2831
+ constructor(message, cause) {
2832
+ super(message);
2833
+ this.name = "KeychainUnavailableError";
2834
+ this.cause = cause;
2835
+ }
2836
+ }
2837
+ function wrapKeyringError(error) {
2838
+ const message = error instanceof Error ? error.message : String(error);
2839
+ throw new KeychainUnavailableError(`System keychain unavailable: ${message}`, error);
2840
+ }
2841
+
2842
+ class KeyringServiceImpl {
2843
+ getPassword(service, account) {
2844
+ try {
2845
+ return new Entry(service, account).getPassword();
2846
+ } catch (error) {
2847
+ wrapKeyringError(error);
2848
+ }
2849
+ }
2850
+ setPassword(service, account, password) {
2851
+ try {
2852
+ new Entry(service, account).setPassword(password);
2853
+ } catch (error) {
2854
+ wrapKeyringError(error);
2855
+ }
2856
+ }
2857
+ deletePassword(service, account) {
2858
+ try {
2859
+ return new Entry(service, account).deleteCredential();
2860
+ } catch (error) {
2861
+ wrapKeyringError(error);
2862
+ }
2863
+ }
2864
+ }
2865
+ // src/services/migrating-auth-storage.ts
2866
+ class MigratingAuthStorage {
2867
+ primary;
2868
+ legacy;
2869
+ onPrimaryUnavailable;
2870
+ primaryAvailable = true;
2871
+ warnedOnPrimaryFailure = false;
2872
+ constructor(primary, legacy, onPrimaryUnavailable = () => {}) {
2873
+ this.primary = primary;
2874
+ this.legacy = legacy;
2875
+ this.onPrimaryUnavailable = onPrimaryUnavailable;
2876
+ }
2877
+ async loadTokens(baseUrl2) {
2878
+ if (this.primaryAvailable) {
2879
+ try {
2880
+ const tokens = await this.primary.loadTokens(baseUrl2);
2881
+ if (tokens)
2882
+ return tokens;
2883
+ } catch (error) {
2884
+ if (!this.handlePrimaryFailure(error))
2885
+ throw error;
2886
+ }
2887
+ }
2888
+ const legacyTokens = await this.legacy.loadTokens(baseUrl2);
2889
+ if (!legacyTokens)
2890
+ return null;
2891
+ if (!this.primaryAvailable)
2892
+ return legacyTokens;
2893
+ try {
2894
+ await this.primary.saveTokens(baseUrl2, legacyTokens);
2895
+ } catch (error) {
2896
+ if (!this.handlePrimaryFailure(error))
2897
+ throw error;
2898
+ return legacyTokens;
2899
+ }
2900
+ try {
2901
+ await this.legacy.clearTokens(baseUrl2);
2902
+ } catch {}
2903
+ return legacyTokens;
2904
+ }
2905
+ async saveTokens(baseUrl2, data) {
2906
+ if (this.primaryAvailable) {
2907
+ try {
2908
+ await this.primary.saveTokens(baseUrl2, data);
2909
+ return;
2910
+ } catch (error) {
2911
+ if (!this.handlePrimaryFailure(error))
2912
+ throw error;
2913
+ }
2914
+ }
2915
+ await this.legacy.saveTokens(baseUrl2, data);
2916
+ }
2917
+ async clearTokens(baseUrl2) {
2918
+ let primaryError;
2919
+ if (this.primaryAvailable) {
2920
+ try {
2921
+ await this.primary.clearTokens(baseUrl2);
2922
+ } catch (error) {
2923
+ if (!this.handlePrimaryFailure(error)) {
2924
+ primaryError = error;
2925
+ }
2926
+ }
2927
+ }
2928
+ try {
2929
+ await this.legacy.clearTokens(baseUrl2);
2930
+ } catch {}
2931
+ if (primaryError)
2932
+ throw primaryError;
2933
+ }
2934
+ async loadClient(baseUrl2) {
2935
+ if (this.primaryAvailable) {
2936
+ try {
2937
+ const client = await this.primary.loadClient(baseUrl2);
2938
+ if (client)
2939
+ return client;
2940
+ } catch (error) {
2941
+ if (!this.handlePrimaryFailure(error))
2942
+ throw error;
2943
+ }
2944
+ }
2945
+ const legacyClient = await this.legacy.loadClient(baseUrl2);
2946
+ if (!legacyClient)
2947
+ return null;
2948
+ if (!this.primaryAvailable)
2949
+ return legacyClient;
2950
+ try {
2951
+ await this.primary.saveClient(baseUrl2, legacyClient);
2952
+ } catch (error) {
2953
+ if (!this.handlePrimaryFailure(error))
2954
+ throw error;
2955
+ return legacyClient;
2956
+ }
2957
+ try {
2958
+ await this.legacy.clearClient(baseUrl2);
2959
+ } catch {}
2960
+ return legacyClient;
2961
+ }
2962
+ async saveClient(baseUrl2, data) {
2963
+ if (this.primaryAvailable) {
2964
+ try {
2965
+ await this.primary.saveClient(baseUrl2, data);
2966
+ return;
2967
+ } catch (error) {
2968
+ if (!this.handlePrimaryFailure(error))
2969
+ throw error;
2970
+ }
2971
+ }
2972
+ await this.legacy.saveClient(baseUrl2, data);
2973
+ }
2974
+ async clearClient(baseUrl2) {
2975
+ let primaryError;
2976
+ if (this.primaryAvailable) {
2977
+ try {
2978
+ await this.primary.clearClient(baseUrl2);
2979
+ } catch (error) {
2980
+ if (!this.handlePrimaryFailure(error)) {
2981
+ primaryError = error;
2982
+ }
2983
+ }
2984
+ }
2985
+ try {
2986
+ await this.legacy.clearClient(baseUrl2);
2987
+ } catch {}
2988
+ if (primaryError)
2989
+ throw primaryError;
2990
+ }
2991
+ getStorageLocation() {
2992
+ return this.primaryAvailable ? this.primary.getStorageLocation() : this.legacy.getStorageLocation();
2993
+ }
2994
+ handlePrimaryFailure(error) {
2995
+ if (!(error instanceof KeychainUnavailableError)) {
2996
+ return false;
2997
+ }
2998
+ this.primaryAvailable = false;
2999
+ if (!this.warnedOnPrimaryFailure) {
3000
+ this.warnedOnPrimaryFailure = true;
3001
+ this.onPrimaryUnavailable(error);
3002
+ }
3003
+ return true;
3004
+ }
3005
+ }
3006
+ // src/services/package-intelligence-service.ts
3007
+ import { z as z2 } from "zod";
3008
+
3009
+ // src/services/promote-version-not-found.ts
3010
+ function promoteGenericVersionNotFound(error, params) {
3011
+ if (!(error instanceof PackageIntelligenceBackendError))
3012
+ return error;
3013
+ if (error.graphqlCode !== undefined)
3014
+ return error;
3015
+ const requestedVersion = pickRequestedVersion(params);
3016
+ if (!requestedVersion)
3017
+ return error;
3018
+ if (!/no matching version/i.test(error.message))
3019
+ return error;
3020
+ const qualifiedName = synthesizeQualifiedName(params);
3021
+ return new PackageIntelligenceVersionNotFoundError(error.message, qualifiedName, requestedVersion, undefined);
3022
+ }
3023
+ function pickRequestedVersion(params) {
3024
+ if (params.version)
3025
+ return params.version;
3026
+ if (params.fromVersion)
3027
+ return params.fromVersion;
3028
+ if (params.toVersion)
3029
+ return params.toVersion;
3030
+ return;
3031
+ }
3032
+ function synthesizeQualifiedName(params) {
3033
+ if (!params.registry || !params.packageName)
3034
+ return;
3035
+ return `${params.registry.toLowerCase()}:${params.packageName}`;
3036
+ }
3037
+
3038
+ // src/services/package-intelligence-service.ts
3039
+ class PackageIntelligenceAccessError extends Error {
3040
+ constructor(message) {
3041
+ super(message);
3042
+ this.name = "PackageIntelligenceAccessError";
3043
+ }
3044
+ }
3045
+
3046
+ class PackageIntelligenceFeatureFlagRequiredError extends Error {
3047
+ constructor(message) {
3048
+ super(message);
3049
+ this.name = "PackageIntelligenceFeatureFlagRequiredError";
3050
+ }
3051
+ }
3052
+
3053
+ class PackageIntelligenceNetworkError extends Error {
3054
+ constructor(message, options) {
3055
+ super(message, options);
3056
+ this.name = "PackageIntelligenceNetworkError";
3057
+ }
3058
+ }
3059
+
3060
+ class PackageIntelligenceBackendError extends Error {
3061
+ status;
3062
+ graphqlCode;
3063
+ retryable;
3064
+ constructor(message, status, graphqlCode, retryable) {
3065
+ super(message);
3066
+ this.status = status;
3067
+ this.graphqlCode = graphqlCode;
3068
+ this.retryable = retryable;
3069
+ this.name = "PackageIntelligenceBackendError";
3070
+ }
3071
+ }
3072
+
3073
+ class PackageIntelligenceGraphQLError extends Error {
3074
+ code;
3075
+ constructor(message, code) {
3076
+ super(message);
3077
+ this.code = code;
3078
+ this.name = "PackageIntelligenceGraphQLError";
3079
+ }
3080
+ }
3081
+
3082
+ class PackageIntelligenceTargetNotFoundError extends Error {
3083
+ constructor(message) {
3084
+ super(message);
3085
+ this.name = "PackageIntelligenceTargetNotFoundError";
3086
+ }
3087
+ }
3088
+
3089
+ class PackageIntelligenceValidationError extends Error {
3090
+ constructor(message) {
3091
+ super(message);
3092
+ this.name = "PackageIntelligenceValidationError";
3093
+ }
3094
+ }
3095
+
3096
+ class PackageIntelligenceVersionNotFoundError extends Error {
3097
+ packageName;
3098
+ requestedVersion;
3099
+ availableVersions;
3100
+ constructor(message, packageName, requestedVersion, availableVersions) {
3101
+ super(message);
3102
+ this.packageName = packageName;
3103
+ this.requestedVersion = requestedVersion;
3104
+ this.availableVersions = availableVersions;
3105
+ this.name = "PackageIntelligenceVersionNotFoundError";
3106
+ }
3107
+ }
3108
+
3109
+ class MalformedPackageIntelligenceResponseError extends Error {
3110
+ constructor(message) {
3111
+ super(message);
3112
+ this.name = "MalformedPackageIntelligenceResponseError";
3113
+ }
3114
+ }
3115
+
3116
+ class PackageIntelligenceChangelogSourceNotFoundError extends Error {
3117
+ constructor(message) {
3118
+ super(message);
3119
+ this.name = "PackageIntelligenceChangelogSourceNotFoundError";
3120
+ }
3121
+ }
3122
+ var githubRepositorySchema = z2.object({
3123
+ stargazersCount: z2.number().int().nullable().optional(),
3124
+ forksCount: z2.number().int().nullable().optional(),
3125
+ openIssuesCount: z2.number().int().nullable().optional(),
3126
+ archived: z2.boolean().nullable().optional(),
3127
+ language: z2.string().nullable().optional(),
3128
+ topics: z2.array(z2.string()).nullable().optional(),
3129
+ pushedAt: z2.string().nullable().optional()
3130
+ }).nullable().optional();
3131
+ var packageIdentitySchema = z2.object({
3132
+ name: z2.string().nullable().optional(),
3133
+ registry: z2.string().nullable().optional(),
3134
+ description: z2.string().nullable().optional(),
3135
+ latestVersion: z2.string().nullable().optional(),
3136
+ latestVersionPublishedAt: z2.string().nullable().optional(),
3137
+ homepage: z2.string().nullable().optional(),
3138
+ repositoryUrl: z2.string().nullable().optional(),
3139
+ license: z2.string().nullable().optional(),
3140
+ downloadsLastMonth: z2.number().int().nullable().optional(),
3141
+ downloadsTotal: z2.number().int().nullable().optional(),
3142
+ githubRepository: githubRepositorySchema
3143
+ });
3144
+ var vulnerabilityOverviewSchema = z2.object({
3145
+ osvId: z2.string().nullable().optional(),
3146
+ summary: z2.string().nullable().optional(),
3147
+ severityScore: z2.number().nullable().optional(),
3148
+ publishedAt: z2.string().nullable().optional()
3149
+ });
3150
+ var packageSecurityOverviewSchema = z2.object({
3151
+ vulnerabilityCount: z2.number().int().nullable().optional(),
3152
+ hasCurrentVulnerabilities: z2.boolean().nullable().optional(),
3153
+ recentVulnerabilities: z2.array(vulnerabilityOverviewSchema).nullable().optional()
3154
+ }).nullable().optional();
3155
+ var quickstartInfoSchema = z2.object({
3156
+ installCommand: z2.string().nullable().optional(),
3157
+ usageExample: z2.string().nullable().optional()
3158
+ }).nullable().optional();
3159
+ var changelogEntrySchema = z2.object({
3160
+ version: z2.string().nullable().optional(),
3161
+ publishedAt: z2.string().nullable().optional(),
3162
+ body: z2.string().nullable().optional()
3163
+ });
3164
+ var packageSummaryResponseSchema = z2.object({
3165
+ package: packageIdentitySchema.nullable().optional(),
3166
+ security: packageSecurityOverviewSchema,
3167
+ quickstart: quickstartInfoSchema,
3168
+ latestChangelogs: z2.array(changelogEntrySchema).nullable().optional()
3169
+ });
3170
+ var graphQLErrorSchema2 = z2.object({
3171
+ message: z2.string(),
3172
+ extensions: z2.record(z2.string(), z2.unknown()).optional()
3173
+ });
3174
+ var graphQLResponseSchema2 = z2.object({
3175
+ data: z2.object({
3176
+ packageSummary: packageSummaryResponseSchema.nullable().optional()
3177
+ }).nullable().optional(),
3178
+ errors: z2.array(graphQLErrorSchema2).optional()
3179
+ });
3180
+ var PACKAGE_SUMMARY_QUERY = `
3181
+ query PackageSummary($registry: Registry!, $name: String!) {
3182
+ packageSummary(registry: $registry, name: $name) {
3183
+ package {
3184
+ name
3185
+ registry
3186
+ description
3187
+ latestVersion
3188
+ latestVersionPublishedAt
3189
+ homepage
3190
+ repositoryUrl
3191
+ license
3192
+ downloadsLastMonth
3193
+ downloadsTotal
3194
+ githubRepository {
3195
+ stargazersCount
3196
+ forksCount
3197
+ openIssuesCount
3198
+ archived
3199
+ language
3200
+ topics
3201
+ pushedAt
3202
+ }
3203
+ }
3204
+ security {
3205
+ vulnerabilityCount
3206
+ hasCurrentVulnerabilities
3207
+ recentVulnerabilities {
3208
+ osvId
3209
+ summary
3210
+ severityScore
3211
+ publishedAt
3212
+ }
3213
+ }
3214
+ quickstart {
3215
+ installCommand
3216
+ usageExample
3217
+ }
3218
+ latestChangelogs(limit: 3) {
3219
+ version
3220
+ publishedAt
3221
+ body
3222
+ }
3223
+ }
3224
+ }`;
3225
+ var packageVersionIdentitySchema = z2.object({
3226
+ name: z2.string().nullable().optional(),
3227
+ registry: z2.string().nullable().optional(),
3228
+ version: z2.string().nullable().optional()
3229
+ });
3230
+ var vulnerabilityDetailSchema = z2.object({
3231
+ osvId: z2.string().nullable().optional(),
3232
+ summary: z2.string().nullable().optional(),
3233
+ severityScore: z2.number().nullable().optional(),
3234
+ severityType: z2.string().nullable().optional(),
3235
+ affectedVersionRanges: z2.array(z2.string()).nullable().optional(),
3236
+ fixedInVersions: z2.array(z2.string()).nullable().optional(),
3237
+ publishedAt: z2.string().nullable().optional(),
3238
+ modifiedAt: z2.string().nullable().optional(),
3239
+ withdrawnAt: z2.string().nullable().optional(),
3240
+ aliases: z2.array(z2.string()).nullable().optional(),
3241
+ isMalicious: z2.boolean().nullable().optional()
3242
+ });
3243
+ var vulnerabilitySecurityDetailsSchema = z2.object({
3244
+ vulnerabilityCount: z2.number().int().nullable().optional(),
3245
+ currentVersionAffected: z2.boolean().nullable().optional(),
3246
+ vulnerabilities: z2.array(vulnerabilityDetailSchema).nullable().optional(),
3247
+ upgradePaths: z2.array(z2.string()).nullable().optional()
3248
+ }).nullable().optional();
3249
+ var vulnerabilityReportResponseSchema = z2.object({
3250
+ package: packageVersionIdentitySchema.nullable().optional(),
3251
+ security: vulnerabilitySecurityDetailsSchema
3252
+ });
3253
+ var vulnerabilitiesGraphQLResponseSchema = z2.object({
3254
+ data: z2.object({
3255
+ packageVulnerabilities: vulnerabilityReportResponseSchema.nullable().optional()
3256
+ }).nullable().optional(),
3257
+ errors: z2.array(graphQLErrorSchema2).optional()
3258
+ });
3259
+ var PACKAGE_VULNERABILITIES_QUERY = `
3260
+ query PackageVulnerabilities(
3261
+ $registry: Registry!
3262
+ $name: String!
3263
+ $version: String
3264
+ $minSeverity: Float
3265
+ $includeWithdrawn: Boolean
3266
+ ) {
3267
+ packageVulnerabilities(
3268
+ registry: $registry
3269
+ name: $name
3270
+ version: $version
3271
+ minSeverity: $minSeverity
3272
+ includeWithdrawn: $includeWithdrawn
3273
+ ) {
3274
+ package {
3275
+ name
3276
+ registry
3277
+ version
3278
+ }
3279
+ security {
3280
+ vulnerabilityCount
3281
+ currentVersionAffected
3282
+ upgradePaths
3283
+ vulnerabilities {
3284
+ osvId
3285
+ summary
3286
+ severityScore
3287
+ severityType
3288
+ affectedVersionRanges
3289
+ fixedInVersions
3290
+ publishedAt
3291
+ modifiedAt
3292
+ withdrawnAt
3293
+ aliases
3294
+ isMalicious
3295
+ }
3296
+ }
3297
+ }
3298
+ }`;
3299
+ var directDependencySchema = z2.object({
3300
+ name: z2.string().nullable().optional(),
3301
+ versionConstraint: z2.string().nullable().optional(),
3302
+ type: z2.string().nullable().optional()
3303
+ });
3304
+ var dependencyGraphNodeSchema = z2.object({
3305
+ registry: z2.string(),
3306
+ name: z2.string(),
3307
+ version: z2.string().nullable().optional()
3308
+ });
3309
+ var dependencyGraphEdgeSchema = z2.object({
3310
+ fromIndex: z2.number().int().nullable().optional(),
3311
+ toIndex: z2.number().int(),
3312
+ constraint: z2.string().nullable().optional(),
3313
+ dependencyType: z2.string().nullable().optional()
3314
+ });
3315
+ var dependencyGraphSchema = z2.object({
3316
+ formatVersion: z2.number().int(),
3317
+ nodes: z2.array(dependencyGraphNodeSchema),
3318
+ edges: z2.array(dependencyGraphEdgeSchema)
3319
+ });
3320
+ var dependencyConflictEdgeSchema = z2.object({
3321
+ fromIndex: z2.number().int().nullable().optional(),
3322
+ toIndex: z2.number().int(),
3323
+ versionConstraint: z2.string(),
3324
+ dependencyType: z2.string()
3325
+ });
3326
+ var dependencyConflictSchema = z2.object({
3327
+ packageName: z2.string(),
3328
+ requiredVersions: z2.array(z2.string()),
3329
+ conflictingEdges: z2.array(dependencyConflictEdgeSchema)
3330
+ });
3331
+ var circularDependencyCycleSchema = z2.object({
3332
+ cycleStart: z2.string(),
3333
+ circularPath: z2.array(z2.string()),
3334
+ displayChain: z2.string()
3335
+ });
3336
+ var environmentMarkerSchema = z2.object({
3337
+ type: z2.string().nullable().optional(),
3338
+ value: z2.string().nullable().optional(),
3339
+ raw: z2.string().nullable().optional()
3340
+ });
3341
+ var transitiveDependencySchema = z2.object({
3342
+ totalEdges: z2.number().int().nullable().optional(),
3343
+ uniquePackagesCount: z2.number().int().nullable().optional(),
3344
+ uniqueDependencies: z2.array(z2.string()).nullable().optional(),
3345
+ dependencyConflicts: z2.array(dependencyConflictSchema).nullable().optional(),
3346
+ circularDependencyCycles: z2.array(circularDependencyCycleSchema).nullable().optional(),
3347
+ dependencyGraph: dependencyGraphSchema.nullable().optional()
3348
+ }).nullable().optional();
3349
+ var dependencyBundleSchema = z2.object({
3350
+ direct: z2.array(directDependencySchema).nullable().optional(),
3351
+ transitive: transitiveDependencySchema
3352
+ }).nullable().optional();
3353
+ var groupDependencySchema = z2.object({
3354
+ name: z2.string(),
3355
+ constraint: z2.string().nullable().optional()
3356
+ });
3357
+ var dependencyGroupSchema = z2.object({
3358
+ name: z2.string(),
3359
+ lifecycle: z2.string(),
3360
+ conditionType: z2.string(),
3361
+ conditionValue: z2.string().nullable().optional(),
3362
+ selectionMode: z2.string(),
3363
+ exclusiveGroup: z2.string().nullable().optional(),
3364
+ fallbackPriority: z2.number().int().nullable().optional(),
3365
+ compatibleWith: z2.array(z2.string()).nullable().optional(),
3366
+ defaultEnabled: z2.boolean().nullable().optional(),
3367
+ dependencies: z2.array(groupDependencySchema)
3368
+ });
3369
+ var dependencyGroupsInfoSchema = z2.object({
3370
+ primaryGroup: z2.string().nullable().optional(),
3371
+ environmentMarkers: z2.array(environmentMarkerSchema).nullable().optional(),
3372
+ groups: z2.array(dependencyGroupSchema)
3373
+ }).nullable().optional();
3374
+ var dependencyReportResponseSchema = z2.object({
3375
+ package: packageVersionIdentitySchema.nullable().optional(),
3376
+ dependencies: dependencyBundleSchema,
3377
+ dependencyGroups: dependencyGroupsInfoSchema
3378
+ });
3379
+ var dependenciesGraphQLResponseSchema = z2.object({
3380
+ data: z2.object({
3381
+ packageDependencies: dependencyReportResponseSchema.nullable().optional()
3382
+ }).nullable().optional(),
3383
+ errors: z2.array(graphQLErrorSchema2).optional()
3384
+ });
3385
+ var PACKAGE_DEPENDENCIES_QUERY = `
3386
+ query PackageDependencies(
3387
+ $registry: Registry!
3388
+ $name: String!
3389
+ $version: String
3390
+ $includeTransitive: Boolean
3391
+ $maxDepth: Int
3392
+ $lifecycle: [String!]
3393
+ ) {
3394
+ packageDependencies(
3395
+ registry: $registry
3396
+ name: $name
3397
+ version: $version
3398
+ includeTransitive: $includeTransitive
3399
+ maxDepth: $maxDepth
3400
+ lifecycle: $lifecycle
3401
+ ) {
3402
+ package {
3403
+ name
3404
+ registry
3405
+ version
3406
+ }
3407
+ dependencies {
3408
+ # Backend-side summary block intentionally not selected — our
3409
+ # envelope computes runtime.count client-side from direct[].length
3410
+ # so the invariant runtime.count === runtime.items.length always
3411
+ # holds regardless of backend-side drift.
3412
+ direct {
3413
+ name
3414
+ versionConstraint
3415
+ type
3416
+ }
3417
+ transitive {
3418
+ totalEdges
3419
+ uniquePackagesCount
3420
+ uniqueDependencies
3421
+ dependencyConflicts {
3422
+ packageName
3423
+ requiredVersions
3424
+ conflictingEdges {
3425
+ fromIndex
3426
+ toIndex
3427
+ versionConstraint
3428
+ dependencyType
3429
+ }
3430
+ }
3431
+ circularDependencyCycles {
3432
+ cycleStart
3433
+ circularPath
3434
+ displayChain
3435
+ }
3436
+ dependencyGraph {
3437
+ formatVersion
3438
+ nodes {
3439
+ registry
3440
+ name
3441
+ version
3442
+ }
3443
+ edges {
3444
+ fromIndex
3445
+ toIndex
3446
+ constraint
3447
+ dependencyType
3448
+ }
3449
+ }
3450
+ }
3451
+ }
3452
+ dependencyGroups {
3453
+ primaryGroup
3454
+ environmentMarkers {
3455
+ type
3456
+ value
3457
+ raw
3458
+ }
3459
+ groups {
3460
+ name
3461
+ lifecycle
3462
+ conditionType
3463
+ conditionValue
3464
+ selectionMode
3465
+ exclusiveGroup
3466
+ fallbackPriority
3467
+ compatibleWith
3468
+ defaultEnabled
3469
+ dependencies {
3470
+ name
3471
+ constraint
3472
+ }
3473
+ }
3474
+ }
3475
+ }
3476
+ }`;
3477
+ var changelogPackageInfoSchema = z2.object({
3478
+ name: z2.string().nullable().optional(),
3479
+ registry: z2.string().nullable().optional(),
3480
+ repoUrl: z2.string().nullable().optional(),
3481
+ fromVersion: z2.string().nullable().optional(),
3482
+ toVersion: z2.string().nullable().optional(),
3483
+ limit: z2.number().int().nullable().optional()
3484
+ }).nullable().optional();
3485
+ var changelogEntryDetailSchema = z2.object({
3486
+ version: z2.string().nullable().optional(),
3487
+ normalizedVersion: z2.string().nullable().optional(),
3488
+ body: z2.string().nullable().optional(),
3489
+ htmlUrl: z2.string().nullable().optional(),
3490
+ publishedAt: z2.string().nullable().optional(),
3491
+ metadata: z2.unknown().nullable().optional()
3492
+ });
3493
+ var changelogReportResponseSchema = z2.object({
3494
+ package: changelogPackageInfoSchema,
3495
+ source: z2.string().nullable().optional(),
3496
+ entries: z2.array(changelogEntryDetailSchema).nullable().optional()
3497
+ });
3498
+ var changelogGraphQLResponseSchema = z2.object({
3499
+ data: z2.object({
3500
+ packageChangelog: changelogReportResponseSchema.nullable().optional()
3501
+ }).nullable().optional(),
3502
+ errors: z2.array(graphQLErrorSchema2).optional()
3503
+ });
3504
+ var PACKAGE_CHANGELOG_QUERY = `
3505
+ query PackageChangelog(
3506
+ $registry: Registry
3507
+ $name: String
3508
+ $repoUrl: String
3509
+ $gitRef: String
3510
+ $fromVersion: String
3511
+ $toVersion: String
3512
+ $limit: Int
3513
+ ) {
3514
+ packageChangelog(
3515
+ registry: $registry
3516
+ name: $name
3517
+ repoUrl: $repoUrl
3518
+ gitRef: $gitRef
3519
+ fromVersion: $fromVersion
3520
+ toVersion: $toVersion
3521
+ limit: $limit
3522
+ ) {
3523
+ package {
3524
+ name
3525
+ registry
3526
+ repoUrl
3527
+ fromVersion
3528
+ toVersion
3529
+ limit
3530
+ }
3531
+ source
3532
+ entries {
3533
+ version
3534
+ normalizedVersion
3535
+ body
3536
+ htmlUrl
3537
+ publishedAt
3538
+ metadata
3539
+ }
3540
+ }
3541
+ }`;
3542
+
3543
+ class PackageIntelligenceServiceImpl {
3544
+ endpointUrl;
3545
+ tokenProvider;
3546
+ fetchFn;
3547
+ constructor(endpointUrl, tokenProvider, fetchFn = globalThis.fetch) {
3548
+ this.endpointUrl = endpointUrl;
3549
+ this.tokenProvider = tokenProvider;
3550
+ this.fetchFn = fetchFn;
3551
+ }
3552
+ async packageSummary(params) {
3553
+ return withTelemetrySpan("pkg-intel.summary.request", () => executeWithTokenRefresh({
3554
+ getToken: () => this.tokenProvider.getToken(),
3555
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
3556
+ shouldRefresh: (error) => error instanceof AuthenticationError,
3557
+ executeWithToken: (token) => this.executePackageSummary(token, params)
3558
+ }));
3559
+ }
3560
+ async executePackageSummary(token, params) {
3561
+ let response;
3562
+ try {
3563
+ response = await postPkgseerGraphql({
3564
+ endpointUrl: this.endpointUrl,
3565
+ token,
3566
+ query: PACKAGE_SUMMARY_QUERY,
3567
+ variables: {
3568
+ registry: params.registry,
3569
+ name: params.packageName
3570
+ },
3571
+ fetchFn: this.fetchFn
3572
+ });
3573
+ } catch (cause) {
3574
+ if (cause instanceof PkgseerTransportError) {
3575
+ throw new PackageIntelligenceNetworkError("Could not reach the package intelligence service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
3576
+ }
3577
+ throw cause;
3578
+ }
3579
+ if (response.status < 200 || response.status >= 300) {
3580
+ throw this.createHttpError(response);
3581
+ }
3582
+ const parsed = graphQLResponseSchema2.safeParse(response.parsedBody);
3583
+ if (!parsed.success) {
3584
+ throw new MalformedPackageIntelligenceResponseError("Malformed response from the package-intelligence service.");
3585
+ }
3586
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
3587
+ throw this.createGraphQLError(parsed.data.errors);
3588
+ }
3589
+ const data = parsed.data.data?.packageSummary;
3590
+ if (!data) {
3591
+ throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
3592
+ }
3593
+ return this.normalise(data);
3594
+ }
3595
+ createHttpError(response) {
3596
+ const status = response.status;
3597
+ const detail = parseDetail3(response.responseBody);
3598
+ if (status === 401) {
3599
+ return new AuthenticationError("Authentication required. Run `githits login` to authenticate.");
3600
+ }
3601
+ if (status === 403) {
3602
+ return new PackageIntelligenceAccessError(detail ?? "Access denied.");
3603
+ }
3604
+ if (status >= 500) {
3605
+ return new PackageIntelligenceBackendError(detail ? `Server error (${status}): ${detail}` : `Server error (${status})`, status);
3606
+ }
3607
+ return new PackageIntelligenceBackendError(detail ?? `Request failed with status ${status}`, status);
3608
+ }
3609
+ createGraphQLError(errors) {
3610
+ const message = errors.map((error) => error.message).join(", ");
3611
+ const extensions = getPrimaryExtensions2(errors);
3612
+ const code = typeof extensions?.code === "string" ? extensions.code : undefined;
3613
+ const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
3614
+ switch (code) {
3615
+ case "NOT_FOUND":
3616
+ case "PACKAGE_NOT_FOUND":
3617
+ return new PackageIntelligenceTargetNotFoundError(message);
3618
+ case "VERSION_NOT_FOUND":
3619
+ return new PackageIntelligenceVersionNotFoundError(message, typeof extensions?.package === "string" ? extensions.package : undefined, typeof extensions?.requested_version === "string" ? extensions.requested_version : undefined, parseVersionList(extensions?.available_versions ?? extensions?.availableVersions));
3620
+ case "UNSUPPORTED_REGISTRY":
3621
+ case "VALIDATION_ERROR":
3622
+ return new PackageIntelligenceValidationError(message);
3623
+ case "FEATURE_FLAG_REQUIRED":
3624
+ return new PackageIntelligenceFeatureFlagRequiredError(message);
3625
+ case "UNAUTHORIZED":
3626
+ return new AuthenticationError("Authentication required. Run `githits login` to authenticate.");
3627
+ case "FORBIDDEN":
3628
+ return new PackageIntelligenceAccessError("Access denied. This feature may not be enabled for your account.");
3629
+ case "UPSTREAM_ERROR":
3630
+ case "TIMEOUT":
3631
+ case "RATE_LIMITED":
3632
+ case "INTERNAL_ERROR":
3633
+ case "UNKNOWN_ERROR":
3634
+ return new PackageIntelligenceBackendError(message, undefined, code, retryable);
3635
+ default:
3636
+ break;
3637
+ }
3638
+ return new PackageIntelligenceBackendError(message, undefined, code, retryable);
3639
+ }
3640
+ normalise(data) {
3641
+ const name = data.package?.name ?? undefined;
3642
+ const latestVersion = data.package?.latestVersion ?? undefined;
3643
+ if (!name || !latestVersion) {
3644
+ throw new MalformedPackageIntelligenceResponseError("Package summary response missing required name/latestVersion.");
3645
+ }
3646
+ const pkg = data.package;
3647
+ const github = pkg?.githubRepository;
3648
+ const identity = {
3649
+ name,
3650
+ latestVersion,
3651
+ registry: pkg?.registry ?? undefined,
3652
+ description: pkg?.description ?? undefined,
3653
+ latestVersionPublishedAt: pkg?.latestVersionPublishedAt ?? undefined,
3654
+ homepage: pkg?.homepage ?? undefined,
3655
+ repositoryUrl: pkg?.repositoryUrl ?? undefined,
3656
+ license: pkg?.license ?? undefined,
3657
+ downloadsLastMonth: pkg?.downloadsLastMonth ?? undefined,
3658
+ downloadsTotal: pkg?.downloadsTotal ?? undefined,
3659
+ githubRepository: github ? {
3660
+ stargazersCount: github.stargazersCount ?? undefined,
3661
+ forksCount: github.forksCount ?? undefined,
3662
+ openIssuesCount: github.openIssuesCount ?? undefined,
3663
+ archived: github.archived ?? undefined,
3664
+ language: github.language ?? undefined,
3665
+ topics: github.topics ?? undefined,
3666
+ pushedAt: github.pushedAt ?? undefined
3667
+ } : undefined
3668
+ };
3669
+ const security = data.security ? {
3670
+ vulnerabilityCount: data.security.vulnerabilityCount ?? undefined,
3671
+ hasCurrentVulnerabilities: data.security.hasCurrentVulnerabilities ?? undefined,
3672
+ recentVulnerabilities: data.security.recentVulnerabilities?.map((vuln) => ({
3673
+ osvId: vuln.osvId ?? undefined,
3674
+ summary: vuln.summary ?? undefined,
3675
+ severityScore: vuln.severityScore ?? undefined,
3676
+ publishedAt: vuln.publishedAt ?? undefined
3677
+ })) ?? undefined
3678
+ } : undefined;
3679
+ const quickstart = data.quickstart ? {
3680
+ installCommand: data.quickstart.installCommand ?? undefined,
3681
+ usageExample: data.quickstart.usageExample ?? undefined
3682
+ } : undefined;
3683
+ const latestChangelogs = data.latestChangelogs?.map((entry) => ({
3684
+ version: entry.version ?? undefined,
3685
+ publishedAt: entry.publishedAt ?? undefined,
3686
+ body: entry.body ?? undefined
3687
+ })) ?? undefined;
3688
+ return {
3689
+ package: identity,
3690
+ security,
3691
+ quickstart,
3692
+ latestChangelogs
3693
+ };
3694
+ }
3695
+ async packageVulnerabilities(params) {
3696
+ return withTelemetrySpan("pkg-intel.vulnerabilities.request", () => executeWithTokenRefresh({
3697
+ getToken: () => this.tokenProvider.getToken(),
3698
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
3699
+ shouldRefresh: (error) => error instanceof AuthenticationError,
3700
+ executeWithToken: (token) => this.executePackageVulnerabilities(token, params)
3701
+ }));
3702
+ }
3703
+ async executePackageVulnerabilities(token, params) {
3704
+ let response;
3705
+ try {
3706
+ response = await postPkgseerGraphql({
3707
+ endpointUrl: this.endpointUrl,
3708
+ token,
3709
+ query: PACKAGE_VULNERABILITIES_QUERY,
3710
+ variables: {
3711
+ registry: params.registry,
3712
+ name: params.packageName,
3713
+ version: params.version,
3714
+ minSeverity: params.minSeverity,
3715
+ includeWithdrawn: params.includeWithdrawn
3716
+ },
3717
+ fetchFn: this.fetchFn
3718
+ });
3719
+ } catch (cause) {
3720
+ if (cause instanceof PkgseerTransportError) {
3721
+ throw new PackageIntelligenceNetworkError("Could not reach the package intelligence service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
3722
+ }
3723
+ throw cause;
3724
+ }
3725
+ if (response.status < 200 || response.status >= 300) {
3726
+ throw this.createHttpError(response);
3727
+ }
3728
+ const parsed = vulnerabilitiesGraphQLResponseSchema.safeParse(response.parsedBody);
3729
+ if (!parsed.success) {
3730
+ throw new MalformedPackageIntelligenceResponseError("Malformed response from the package-intelligence service.");
3731
+ }
3732
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
3733
+ throw promoteGenericVersionNotFound(this.createGraphQLError(parsed.data.errors), params);
3734
+ }
3735
+ const data = parsed.data.data?.packageVulnerabilities;
3736
+ if (!data) {
3737
+ throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
3738
+ }
3739
+ return this.normaliseVulnerabilityReport(data);
3740
+ }
3741
+ normaliseVulnerabilityReport(data) {
3742
+ const name = data.package?.name ?? undefined;
3743
+ const version2 = data.package?.version ?? undefined;
3744
+ if (!name || !version2) {
3745
+ throw new MalformedPackageIntelligenceResponseError("Vulnerability report response missing required name/version.");
3746
+ }
3747
+ const identity = {
3748
+ name,
3749
+ version: version2,
3750
+ registry: data.package?.registry ?? undefined
3751
+ };
3752
+ const security = data.security ? {
3753
+ vulnerabilityCount: data.security.vulnerabilityCount ?? undefined,
3754
+ currentVersionAffected: data.security.currentVersionAffected ?? undefined,
3755
+ vulnerabilities: data.security.vulnerabilities?.map((vuln) => ({
3756
+ osvId: vuln.osvId ?? undefined,
3757
+ summary: vuln.summary ?? undefined,
3758
+ severityScore: vuln.severityScore ?? undefined,
3759
+ severityType: vuln.severityType ?? undefined,
3760
+ affectedVersionRanges: vuln.affectedVersionRanges ?? undefined,
3761
+ fixedInVersions: vuln.fixedInVersions ?? undefined,
3762
+ publishedAt: vuln.publishedAt ?? undefined,
3763
+ modifiedAt: vuln.modifiedAt ?? undefined,
3764
+ withdrawnAt: vuln.withdrawnAt ?? undefined,
3765
+ aliases: vuln.aliases ?? undefined,
3766
+ isMalicious: vuln.isMalicious ?? undefined
3767
+ })) ?? undefined,
3768
+ upgradePaths: data.security.upgradePaths ?? undefined
3769
+ } : undefined;
3770
+ return {
3771
+ package: identity,
3772
+ security
3773
+ };
3774
+ }
3775
+ async packageDependencies(params) {
3776
+ return withTelemetrySpan("pkg-intel.dependencies.request", () => executeWithTokenRefresh({
3777
+ getToken: () => this.tokenProvider.getToken(),
3778
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
3779
+ shouldRefresh: (error) => error instanceof AuthenticationError,
3780
+ executeWithToken: (token) => this.executePackageDependencies(token, params)
3781
+ }));
3782
+ }
3783
+ async executePackageDependencies(token, params) {
3784
+ let response;
3785
+ try {
3786
+ response = await postPkgseerGraphql({
3787
+ endpointUrl: this.endpointUrl,
3788
+ token,
3789
+ query: PACKAGE_DEPENDENCIES_QUERY,
3790
+ variables: {
3791
+ registry: params.registry,
3792
+ name: params.packageName,
3793
+ version: params.version,
3794
+ includeTransitive: params.includeTransitive,
3795
+ maxDepth: params.maxDepth,
3796
+ lifecycle: params.lifecycle && params.lifecycle.length > 0 ? params.lifecycle : undefined
3797
+ },
3798
+ fetchFn: this.fetchFn
3799
+ });
3800
+ } catch (cause) {
3801
+ if (cause instanceof PkgseerTransportError) {
3802
+ throw new PackageIntelligenceNetworkError("Could not reach the package intelligence service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
3803
+ }
3804
+ throw cause;
3805
+ }
3806
+ if (response.status < 200 || response.status >= 300) {
3807
+ throw this.createHttpError(response);
3808
+ }
3809
+ const parsed = dependenciesGraphQLResponseSchema.safeParse(response.parsedBody);
3810
+ if (!parsed.success) {
3811
+ throw new MalformedPackageIntelligenceResponseError("Malformed response from the package-intelligence service.");
3812
+ }
3813
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
3814
+ throw promoteGenericVersionNotFound(this.createGraphQLError(parsed.data.errors), params);
3815
+ }
3816
+ const data = parsed.data.data?.packageDependencies;
3817
+ if (!data) {
3818
+ throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
3819
+ }
3820
+ return this.normaliseDependencyReport(data);
3821
+ }
3822
+ normaliseDependencyReport(data) {
3823
+ const name = data.package?.name ?? undefined;
3824
+ const version2 = data.package?.version ?? undefined;
3825
+ if (!name || !version2) {
3826
+ throw new MalformedPackageIntelligenceResponseError("Package dependencies response missing required name/version.");
3827
+ }
3828
+ const identity = {
3829
+ name,
3830
+ version: version2,
3831
+ registry: data.package?.registry ?? undefined
3832
+ };
3833
+ const bundle = data.dependencies;
3834
+ const dependencies = bundle ? {
3835
+ direct: bundle.direct?.map((entry) => {
3836
+ if (!entry.name) {
3837
+ throw new MalformedPackageIntelligenceResponseError("Dependency entry missing required name.");
3838
+ }
3839
+ return {
3840
+ name: entry.name,
3841
+ versionConstraint: entry.versionConstraint ?? undefined,
3842
+ type: entry.type ?? undefined
3843
+ };
3844
+ }) ?? undefined,
3845
+ transitive: bundle.transitive ? {
3846
+ totalEdges: bundle.transitive.totalEdges ?? undefined,
3847
+ uniquePackagesCount: bundle.transitive.uniquePackagesCount ?? undefined,
3848
+ uniqueDependencies: bundle.transitive.uniqueDependencies ?? undefined,
3849
+ dependencyConflicts: bundle.transitive.dependencyConflicts?.map((c) => ({
3850
+ packageName: c.packageName,
3851
+ requiredVersions: c.requiredVersions,
3852
+ conflictingEdges: c.conflictingEdges.map((edge) => ({
3853
+ fromIndex: edge.fromIndex ?? undefined,
3854
+ toIndex: edge.toIndex,
3855
+ versionConstraint: edge.versionConstraint,
3856
+ dependencyType: edge.dependencyType
3857
+ }))
3858
+ })) ?? undefined,
3859
+ circularDependencyCycles: bundle.transitive.circularDependencyCycles?.map((cycle) => ({
3860
+ cycleStart: cycle.cycleStart,
3861
+ circularPath: cycle.circularPath,
3862
+ displayChain: cycle.displayChain
3863
+ })) ?? undefined,
3864
+ dependencyGraph: bundle.transitive.dependencyGraph ? {
3865
+ formatVersion: bundle.transitive.dependencyGraph.formatVersion,
3866
+ nodes: bundle.transitive.dependencyGraph.nodes.map((n) => ({
3867
+ registry: n.registry,
3868
+ name: n.name,
3869
+ version: n.version ?? undefined
3870
+ })),
3871
+ edges: bundle.transitive.dependencyGraph.edges.map((e) => ({
3872
+ fromIndex: e.fromIndex ?? undefined,
3873
+ toIndex: e.toIndex,
3874
+ constraint: e.constraint ?? undefined,
3875
+ dependencyType: e.dependencyType ?? undefined
3876
+ }))
3877
+ } : undefined
3878
+ } : undefined
3879
+ } : undefined;
3880
+ const dependencyGroups = data.dependencyGroups ? {
3881
+ primaryGroup: data.dependencyGroups.primaryGroup ?? undefined,
3882
+ environmentMarkers: data.dependencyGroups.environmentMarkers?.map((m) => ({
3883
+ type: m.type ?? undefined,
3884
+ value: m.value ?? undefined,
3885
+ raw: m.raw ?? undefined
3886
+ })) ?? undefined,
3887
+ groups: data.dependencyGroups.groups.map((group) => ({
3888
+ name: group.name,
3889
+ lifecycle: group.lifecycle,
3890
+ conditionType: group.conditionType,
3891
+ conditionValue: group.conditionValue ?? undefined,
3892
+ selectionMode: group.selectionMode,
3893
+ exclusiveGroup: group.exclusiveGroup ?? undefined,
3894
+ fallbackPriority: group.fallbackPriority ?? undefined,
3895
+ compatibleWith: group.compatibleWith ?? undefined,
3896
+ defaultEnabled: group.defaultEnabled ?? undefined,
3897
+ dependencies: group.dependencies.map((entry) => ({
3898
+ name: entry.name,
3899
+ constraint: entry.constraint ?? undefined
3900
+ }))
3901
+ }))
3902
+ } : undefined;
3903
+ return {
3904
+ package: identity,
3905
+ dependencies,
3906
+ dependencyGroups
3907
+ };
3908
+ }
3909
+ async packageChangelog(params) {
3910
+ return withTelemetrySpan("pkg-intel.changelog.request", () => executeWithTokenRefresh({
3911
+ getToken: () => this.tokenProvider.getToken(),
3912
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
3913
+ shouldRefresh: (error) => error instanceof AuthenticationError,
3914
+ executeWithToken: (token) => this.executePackageChangelog(token, params)
3915
+ }));
3916
+ }
3917
+ async executePackageChangelog(token, params) {
3918
+ let response;
3919
+ try {
3920
+ response = await postPkgseerGraphql({
3921
+ endpointUrl: this.endpointUrl,
3922
+ token,
3923
+ query: PACKAGE_CHANGELOG_QUERY,
3924
+ variables: {
3925
+ registry: params.registry,
3926
+ name: params.packageName,
3927
+ repoUrl: params.repoUrl,
3928
+ gitRef: params.gitRef,
3929
+ fromVersion: params.fromVersion,
3930
+ toVersion: params.toVersion,
3931
+ limit: params.limit
3932
+ },
3933
+ fetchFn: this.fetchFn
3934
+ });
3935
+ } catch (cause) {
3936
+ if (cause instanceof PkgseerTransportError) {
3937
+ throw new PackageIntelligenceNetworkError("Could not reach the package intelligence service. Check your connection or set GITHITS_CODE_NAV_URL.", { cause });
3938
+ }
3939
+ throw cause;
3940
+ }
3941
+ if (response.status < 200 || response.status >= 300) {
3942
+ throw this.createHttpError(response);
3943
+ }
3944
+ const parsed = changelogGraphQLResponseSchema.safeParse(response.parsedBody);
3945
+ if (!parsed.success) {
3946
+ throw new MalformedPackageIntelligenceResponseError("Malformed response from the package-intelligence service.");
3947
+ }
3948
+ if (parsed.data.errors && parsed.data.errors.length > 0) {
3949
+ throw promoteGenericVersionNotFound(this.createGraphQLError(parsed.data.errors), params);
3950
+ }
3951
+ const data = parsed.data.data?.packageChangelog;
3952
+ if (!data) {
3953
+ throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
3954
+ }
3955
+ return this.normaliseChangelogReport(data, params);
3956
+ }
3957
+ normaliseChangelogReport(data, params) {
3958
+ const source = data.source ?? undefined;
3959
+ if (!source) {
3960
+ const target = params.repoUrl ?? (params.registry && params.packageName ? `${params.registry.toLowerCase()}:${params.packageName}` : "package");
3961
+ throw new PackageIntelligenceChangelogSourceNotFoundError(`No changelog source available for ${target} (tried GitHub Releases, CHANGELOG.md, and HexDocs).`);
3962
+ }
3963
+ const rawEntries = data.entries ?? [];
3964
+ const entries = rawEntries.map((entry) => ({
3965
+ version: entry.version ?? undefined,
3966
+ normalizedVersion: entry.normalizedVersion ?? undefined,
3967
+ body: entry.body ?? undefined,
3968
+ htmlUrl: entry.htmlUrl ?? undefined,
3969
+ publishedAt: entry.publishedAt ?? undefined,
3970
+ metadata: entry.metadata ?? undefined
3971
+ }));
3972
+ const packageInfo = data.package ? {
3973
+ name: data.package.name ?? undefined,
3974
+ registry: data.package.registry ?? undefined,
3975
+ repoUrl: data.package.repoUrl ?? undefined,
3976
+ fromVersion: data.package.fromVersion ?? undefined,
3977
+ toVersion: data.package.toVersion ?? undefined,
3978
+ limit: data.package.limit ?? undefined
3979
+ } : undefined;
3980
+ return {
3981
+ package: packageInfo,
3982
+ source,
3983
+ entries
3984
+ };
3985
+ }
3986
+ }
3987
+ function parseDetail3(body) {
3988
+ if (!body)
3989
+ return;
3990
+ try {
3991
+ const parsed = JSON.parse(body);
3992
+ if (typeof parsed.detail === "string")
3993
+ return parsed.detail;
3994
+ if (typeof parsed.error === "string")
3995
+ return parsed.error;
3996
+ } catch {
3997
+ return body;
3998
+ }
3999
+ return;
4000
+ }
4001
+ function getPrimaryExtensions2(errors) {
4002
+ for (const error of errors) {
4003
+ if (error.extensions && Object.keys(error.extensions).length > 0) {
4004
+ return error.extensions;
4005
+ }
4006
+ }
4007
+ return;
4008
+ }
4009
+ function parseVersionList(raw) {
4010
+ if (!Array.isArray(raw))
4011
+ return;
4012
+ const versions = [];
4013
+ for (const item of raw) {
4014
+ if (typeof item === "string" && item.length > 0) {
4015
+ versions.push(item);
4016
+ }
4017
+ }
4018
+ return versions.length > 0 ? versions : undefined;
4019
+ }
4020
+ // src/services/prompt-service.ts
4021
+ import { checkbox, select } from "@inquirer/prompts";
4022
+
4023
+ class PromptServiceImpl {
4024
+ async checkbox(message, choices) {
4025
+ return checkbox({ message, choices });
4026
+ }
4027
+ async confirm3(message) {
4028
+ return select({
4029
+ message,
4030
+ choices: [
4031
+ { value: "yes", name: "Yes" },
4032
+ { value: "no", name: "No" },
4033
+ {
4034
+ value: "always",
4035
+ name: "Yes to all",
4036
+ description: "Skip confirmation for remaining agents"
4037
+ }
4038
+ ]
4039
+ });
4040
+ }
4041
+ }
4042
+ // src/services/refreshing-githits-service.ts
4043
+ class RefreshingGitHitsService {
4044
+ apiUrl;
4045
+ tokenProvider;
4046
+ serviceFactory;
4047
+ constructor(apiUrl, tokenProvider, serviceFactory = (url, token) => new GitHitsServiceImpl(url, token)) {
4048
+ this.apiUrl = apiUrl;
4049
+ this.tokenProvider = tokenProvider;
4050
+ this.serviceFactory = serviceFactory;
4051
+ }
4052
+ async search(params) {
4053
+ return this.withTokenRefresh((service) => service.search(params));
4054
+ }
4055
+ async getLanguages() {
4056
+ return this.withTokenRefresh((service) => service.getLanguages());
4057
+ }
4058
+ async submitFeedback(params) {
4059
+ return this.withTokenRefresh((service) => service.submitFeedback(params));
4060
+ }
4061
+ async withTokenRefresh(operation) {
4062
+ return executeWithTokenRefresh({
4063
+ getToken: () => this.tokenProvider.getToken(),
4064
+ forceRefresh: () => this.tokenProvider.forceRefresh(),
4065
+ shouldRefresh: (error) => error instanceof AuthenticationError,
4066
+ executeWithToken: async (token) => {
4067
+ const service = this.serviceFactory(this.apiUrl, token);
4068
+ return operation(service);
4069
+ }
4070
+ });
4071
+ }
4072
+ }
4073
+ // src/services/token-manager.ts
4074
+ var PROACTIVE_REFRESH_RATIO = 0.9;
4075
+ function shouldRefreshToken(token, ratio, now) {
4076
+ if (!token.expiresAt) {
4077
+ return { expired: false, shouldRefresh: false };
4078
+ }
4079
+ const expiresAt = new Date(token.expiresAt).getTime();
4080
+ const nowMs = now.getTime();
4081
+ if (nowMs >= expiresAt) {
4082
+ return { expired: true, shouldRefresh: true };
4083
+ }
4084
+ const createdAt = new Date(token.createdAt).getTime();
4085
+ const lifetime = expiresAt - createdAt;
4086
+ if (lifetime <= 0) {
4087
+ return { expired: false, shouldRefresh: false };
4088
+ }
4089
+ const threshold = createdAt + lifetime * ratio;
4090
+ return { expired: false, shouldRefresh: nowMs >= threshold };
4091
+ }
4092
+ async function refreshExpiredToken(authService, authStorage, mcpUrl) {
4093
+ const manager = new TokenManager({ authService, authStorage, mcpUrl });
4094
+ return manager.forceRefresh();
4095
+ }
4096
+
4097
+ class TokenManager {
4098
+ authService;
4099
+ authStorage;
4100
+ mcpUrl;
4101
+ cachedToken = null;
4102
+ refreshPromise = null;
4103
+ constructor(deps) {
4104
+ this.authService = deps.authService;
4105
+ this.authStorage = deps.authStorage;
4106
+ this.mcpUrl = deps.mcpUrl;
4107
+ }
4108
+ async getToken() {
4109
+ return withTelemetrySpan("token-manager.get-token", async () => {
4110
+ if (!this.cachedToken) {
4111
+ this.cachedToken = await withTelemetrySpan("token-manager.load-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
4112
+ if (!this.cachedToken)
4113
+ return;
4114
+ }
4115
+ const currentToken = this.cachedToken.accessToken;
4116
+ const { expired, shouldRefresh } = shouldRefreshToken(this.cachedToken, PROACTIVE_REFRESH_RATIO, new Date);
4117
+ if (!shouldRefresh) {
4118
+ return currentToken;
4119
+ }
4120
+ const refreshedToken = await this.doRefresh();
4121
+ if (refreshedToken) {
4122
+ return refreshedToken;
4123
+ }
4124
+ if (!expired) {
4125
+ return currentToken;
4126
+ }
4127
+ return;
4128
+ });
4129
+ }
4130
+ async forceRefresh() {
4131
+ return withTelemetrySpan("token-manager.force-refresh", () => this.doRefresh());
4132
+ }
4133
+ async doRefresh() {
4134
+ if (this.refreshPromise) {
4135
+ return this.refreshPromise;
4136
+ }
4137
+ this.refreshPromise = this.executeRefresh();
4138
+ try {
4139
+ return await this.refreshPromise;
4140
+ } finally {
4141
+ this.refreshPromise = null;
4142
+ }
4143
+ }
4144
+ async executeRefresh() {
4145
+ return withTelemetrySpan("token-manager.refresh", async () => {
4146
+ const tokens = this.cachedToken ?? await withTelemetrySpan("token-manager.load-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
4147
+ if (!tokens)
4148
+ return;
4149
+ const client = await withTelemetrySpan("token-manager.load-client", () => this.authStorage.loadClient(this.mcpUrl));
4150
+ if (!client)
4151
+ return;
4152
+ let response;
4153
+ try {
4154
+ const metadata = await withTelemetrySpan("token-manager.discover-endpoints", () => this.authService.discoverEndpoints(this.mcpUrl));
4155
+ response = await withTelemetrySpan("token-manager.refresh-access-token", () => this.authService.refreshAccessToken({
4156
+ tokenEndpoint: metadata.tokenEndpoint,
4157
+ clientId: client.clientId,
4158
+ clientSecret: client.clientSecret,
4159
+ refreshToken: tokens.refreshToken
4160
+ }));
4161
+ } catch {
4162
+ const isExpired = tokens.expiresAt ? new Date >= new Date(tokens.expiresAt) : false;
4163
+ if (isExpired) {
4164
+ this.cachedToken = null;
4165
+ await withTelemetrySpan("token-manager.clear-tokens", () => this.authStorage.clearTokens(this.mcpUrl));
4166
+ }
4167
+ return;
4168
+ }
4169
+ const newTokenData = {
4170
+ accessToken: response.accessToken,
4171
+ refreshToken: response.refreshToken,
4172
+ expiresAt: new Date(Date.now() + response.expiresIn * 1000).toISOString(),
4173
+ createdAt: new Date().toISOString()
4174
+ };
4175
+ await withTelemetrySpan("token-manager.save-tokens", () => this.authStorage.saveTokens(this.mcpUrl, newTokenData));
4176
+ this.cachedToken = newTokenData;
4177
+ return response.accessToken;
4178
+ });
4179
+ }
4180
+ }
4181
+ // src/container.ts
4182
+ function createAuthStorage(fileSystemService) {
4183
+ return withTelemetrySpanSync("container.create-auth-storage", () => {
4184
+ const fileStorage = new AuthStorageImpl(fileSystemService);
4185
+ const rawKeyring = new KeyringServiceImpl;
4186
+ const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
4187
+ const keychainStorage = new KeychainAuthStorage(keyring);
4188
+ return new MigratingAuthStorage(keychainStorage, fileStorage, (error) => {
4189
+ if (!(error instanceof KeychainUnavailableError))
4190
+ return;
4191
+ console.error("Warning: System keychain unavailable. Falling back to file-based credential storage.");
4192
+ });
4193
+ });
4194
+ }
4195
+ function createStaticTokenProvider(token) {
4196
+ return {
4197
+ getToken: async () => token,
4198
+ forceRefresh: async () => {
4199
+ return;
4200
+ }
4201
+ };
4202
+ }
4203
+ async function createContainer() {
4204
+ return withTelemetrySpan("container.create", async () => {
4205
+ const mcpUrl = getMcpUrl();
4206
+ const apiUrl = getApiUrl();
4207
+ const codeNavigationUrl = getCodeNavigationUrl();
4208
+ const codeNavigationCliOverrideEnabled = isCodeNavigationCliOverrideEnabled();
4209
+ const fileSystemService = new FileSystemServiceImpl;
4210
+ const authStorage = createAuthStorage(fileSystemService);
4211
+ const authService = new AuthServiceImpl;
4212
+ const browserService = new BrowserServiceImpl;
4213
+ const envToken = getEnvApiToken();
4214
+ if (envToken) {
4215
+ const tokenProvider = createStaticTokenProvider(envToken);
4216
+ const codeNavigationService2 = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
4217
+ const packageIntelligenceService2 = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
4218
+ return {
4219
+ authStorage,
4220
+ authService,
4221
+ browserService,
4222
+ fileSystemService,
4223
+ mcpUrl,
4224
+ apiUrl,
4225
+ apiToken: envToken,
4226
+ hasValidToken: true,
4227
+ envApiToken: envToken,
4228
+ codeNavigationCapability: getCodeNavigationCapability(envToken),
4229
+ codeNavigationCliOverrideEnabled,
4230
+ codeNavigationUrl,
4231
+ codeNavigationService: codeNavigationService2,
4232
+ packageIntelligenceService: packageIntelligenceService2,
4233
+ githitsService: new GitHitsServiceImpl(apiUrl, envToken)
4234
+ };
4235
+ }
4236
+ const tokenManager = new TokenManager({ authService, authStorage, mcpUrl });
4237
+ const apiToken = await withTelemetrySpan("container.token.get", () => tokenManager.getToken());
4238
+ const codeNavigationService = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager) : undefined;
4239
+ const packageIntelligenceService = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenManager) : undefined;
4240
+ return {
4241
+ authStorage,
4242
+ authService,
4243
+ browserService,
4244
+ fileSystemService,
4245
+ mcpUrl,
4246
+ apiUrl,
4247
+ apiToken,
4248
+ hasValidToken: apiToken !== undefined,
4249
+ envApiToken: undefined,
4250
+ codeNavigationCapability: getCodeNavigationCapability(apiToken),
4251
+ codeNavigationCliOverrideEnabled,
4252
+ codeNavigationUrl,
4253
+ codeNavigationService,
4254
+ packageIntelligenceService,
4255
+ githitsService: new RefreshingGitHitsService(apiUrl, tokenManager)
4256
+ };
4257
+ });
4258
+ }
4259
+ async function resolveStartupCodeNavigationCapability() {
4260
+ const state = await resolveStartupCodeNavigationRegistrationState();
4261
+ return state.capability;
4262
+ }
4263
+ async function resolveStartupCodeNavigationRegistrationState() {
4264
+ return withTelemetrySpan("startup.resolve-code-nav-registration-state", async () => {
4265
+ const envToken = getEnvApiToken();
4266
+ if (envToken) {
4267
+ return {
4268
+ capability: getCodeNavigationCapability(envToken),
4269
+ expiredStoredAuth: false
4270
+ };
4271
+ }
4272
+ const tokens = await loadStartupTokens(getMcpUrl());
4273
+ if (tokens?.expiresAt && new Date(tokens.expiresAt) < new Date) {
4274
+ return { capability: "unknown", expiredStoredAuth: true };
4275
+ }
4276
+ return {
4277
+ capability: getCodeNavigationCapability(tokens?.accessToken),
4278
+ expiredStoredAuth: false
4279
+ };
4280
+ });
4281
+ }
4282
+ async function loadStartupTokens(mcpUrl) {
4283
+ return withTelemetrySpan("startup.load-tokens", async () => {
4284
+ const fileSystemService = new FileSystemServiceImpl;
4285
+ const fileStorage = new AuthStorageImpl(fileSystemService);
4286
+ try {
4287
+ const rawKeyring = new KeyringServiceImpl;
4288
+ const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
4289
+ const keychainStorage = new KeychainAuthStorage(keyring);
4290
+ const keychainTokens = await keychainStorage.loadTokens(mcpUrl);
4291
+ if (keychainTokens) {
4292
+ return keychainTokens;
4293
+ }
4294
+ } catch (error) {
4295
+ if (!(error instanceof KeychainUnavailableError)) {
4296
+ throw error;
4297
+ }
4298
+ }
4299
+ return fileStorage.loadTokens(mcpUrl);
4300
+ });
4301
+ }
4302
+
4303
+ export { getCodeNavigationCapability, debugLog, setMcpClientVersionProvider, setClientMode, isTelemetryEnabled, withTelemetrySpan, startTelemetrySpan, endTelemetrySpan, flushTelemetry, AuthenticationError, CodeNavigationAccessError, CodeNavigationGraphQLError, CodeNavigationIndexingError, CodeNavigationUnresolvableError, MalformedCodeNavigationResponseError, CodeNavigationTargetNotFoundError, CodeNavigationFileNotFoundError, CodeNavigationVersionNotFoundError, CodeNavigationValidationError, CodeNavigationFeatureFlagRequiredError, CodeNavigationNetworkError, CodeNavigationBackendError, getCodeNavigationUrl, isCodeNavigationCliOverrideEnabled, ExecServiceImpl, FileSystemServiceImpl, PackageIntelligenceAccessError, PackageIntelligenceFeatureFlagRequiredError, PackageIntelligenceNetworkError, PackageIntelligenceBackendError, PackageIntelligenceGraphQLError, PackageIntelligenceTargetNotFoundError, PackageIntelligenceValidationError, PackageIntelligenceVersionNotFoundError, MalformedPackageIntelligenceResponseError, PackageIntelligenceChangelogSourceNotFoundError, PromptServiceImpl, refreshExpiredToken, createContainer, resolveStartupCodeNavigationCapability, resolveStartupCodeNavigationRegistrationState };