githits 0.1.11 → 0.2.1

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