githits 0.2.3 → 0.3.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.
@@ -1,7 +1,94 @@
1
1
  import {
2
+ __require,
2
3
  version
3
- } from "./chunk-js72s35a.js";
4
+ } from "./chunk-m1xx8hzw.js";
4
5
 
6
+ // src/services/app-config-paths.ts
7
+ var APP_DIR = "githits";
8
+ function getAppConfigDir(fs) {
9
+ const home = fs.getHomeDir();
10
+ switch (process.platform) {
11
+ case "win32":
12
+ return fs.joinPath(process.env.APPDATA ?? fs.joinPath(home, "AppData", "Roaming"), APP_DIR);
13
+ case "darwin":
14
+ return fs.joinPath(home, "Library", "Application Support", APP_DIR);
15
+ default:
16
+ return fs.joinPath(process.env.XDG_CONFIG_HOME ?? fs.joinPath(home, ".config"), APP_DIR);
17
+ }
18
+ }
19
+ function getAuthConfigPath(fs) {
20
+ return fs.joinPath(getAppConfigDir(fs), "config.toml");
21
+ }
22
+ function getAuthFileStorageDir(fs) {
23
+ return fs.joinPath(getAppConfigDir(fs), "auth");
24
+ }
25
+ function getLegacyAuthStorageDir(fs) {
26
+ return fs.joinPath(fs.getHomeDir(), ".githits");
27
+ }
28
+ // src/services/auth-config.ts
29
+ import { parse as parseToml } from "smol-toml";
30
+ import { z } from "zod";
31
+ var AUTH_STORAGE_MODES = ["keychain", "file"];
32
+ var AUTH_STORAGE_MODE_VALUES = new Set(AUTH_STORAGE_MODES);
33
+ var CONFIG_SCHEMA = z.object({
34
+ auth: z.object({
35
+ storage: z.string().optional()
36
+ }).optional()
37
+ }).passthrough();
38
+
39
+ class AuthConfigError extends Error {
40
+ constructor(message) {
41
+ super(message);
42
+ this.name = "AuthConfigError";
43
+ }
44
+ }
45
+ function parseAuthStorageMode(value) {
46
+ const normalized = value.trim().toLowerCase();
47
+ if (AUTH_STORAGE_MODE_VALUES.has(normalized)) {
48
+ return normalized;
49
+ }
50
+ throw new AuthConfigError(`Invalid auth storage mode "${value}". Use "keychain" or "file". File mode stores OAuth credentials unencrypted on disk.`);
51
+ }
52
+ async function loadAuthConfig(fs) {
53
+ const configPath = getAuthConfigPath(fs);
54
+ const envMode = process.env.GITHITS_AUTH_STORAGE;
55
+ if (envMode !== undefined && envMode.trim() !== "") {
56
+ try {
57
+ return { storage: parseAuthStorageMode(envMode), configPath };
58
+ } catch (error) {
59
+ if (error instanceof AuthConfigError) {
60
+ throw new AuthConfigError(`Invalid GITHITS_AUTH_STORAGE: ${error.message}`);
61
+ }
62
+ throw error;
63
+ }
64
+ }
65
+ if (!await fs.exists(configPath)) {
66
+ return { storage: "keychain", configPath };
67
+ }
68
+ let rawConfig;
69
+ try {
70
+ rawConfig = parseToml(await fs.readFile(configPath));
71
+ } catch (error) {
72
+ const message = error instanceof Error ? error.message : String(error);
73
+ throw new AuthConfigError(`Cannot parse GitHits config at ${configPath}: ${message}`);
74
+ }
75
+ const parsed = CONFIG_SCHEMA.safeParse(rawConfig);
76
+ if (!parsed.success) {
77
+ throw new AuthConfigError(`Invalid GitHits config at ${configPath}: ${z.prettifyError(parsed.error)}`);
78
+ }
79
+ const configuredMode = parsed.data.auth?.storage;
80
+ if (configuredMode === undefined || configuredMode.trim() === "") {
81
+ return { storage: "keychain", configPath };
82
+ }
83
+ try {
84
+ return { storage: parseAuthStorageMode(configuredMode), configPath };
85
+ } catch (error) {
86
+ if (error instanceof AuthConfigError) {
87
+ throw new AuthConfigError(`Invalid GitHits config at ${configPath}: ${error.message}`);
88
+ }
89
+ throw error;
90
+ }
91
+ }
5
92
  // src/services/auth-service.ts
6
93
  import { createServer } from "node:http";
7
94
 
@@ -164,7 +251,7 @@ class AuthServiceImpl {
164
251
  const error = await response.text();
165
252
  throw new Error(`Token refresh failed: ${error}`);
166
253
  }
167
- return parseTokenResponse(await response.json());
254
+ return parseRefreshTokenResponse(await response.json());
168
255
  }
169
256
  }
170
257
  function parseTokenResponse(data) {
@@ -178,6 +265,17 @@ function parseTokenResponse(data) {
178
265
  expiresIn: d.expires_in || 3600
179
266
  };
180
267
  }
268
+ function parseRefreshTokenResponse(data) {
269
+ const d = data;
270
+ if (!d.access_token) {
271
+ throw new Error("Token response missing required fields");
272
+ }
273
+ return {
274
+ accessToken: d.access_token,
275
+ refreshToken: typeof d.refresh_token === "string" ? d.refresh_token : undefined,
276
+ expiresIn: d.expires_in || 3600
277
+ };
278
+ }
181
279
  function successHtml(title = "Authentication successful") {
182
280
  return `<!DOCTYPE html>
183
281
  <html><head><title>GitHits CLI</title>
@@ -310,11 +408,9 @@ function escapeHtml(text) {
310
408
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
311
409
  }
312
410
  // src/services/auth-storage.ts
313
- var CONFIG_DIR = ".githits";
314
411
  var AUTH_FILE = "auth.json";
315
412
  var CLIENT_FILE = "client.json";
316
413
  var DIR_MODE = 448;
317
- var FILE_MODE = 384;
318
414
 
319
415
  class AuthStorageImpl {
320
416
  fs;
@@ -323,7 +419,7 @@ class AuthStorageImpl {
323
419
  clientPath;
324
420
  constructor(fs, configDir) {
325
421
  this.fs = fs;
326
- this.configDir = configDir ?? fs.joinPath(fs.getHomeDir(), CONFIG_DIR);
422
+ this.configDir = configDir ?? getAuthFileStorageDir(fs);
327
423
  this.authPath = fs.joinPath(this.configDir, AUTH_FILE);
328
424
  this.clientPath = fs.joinPath(this.configDir, CLIENT_FILE);
329
425
  }
@@ -343,7 +439,14 @@ class AuthStorageImpl {
343
439
  };
344
440
  stored.tokens[normalizeBaseUrl(baseUrl)] = data;
345
441
  await this.fs.ensureDir(this.configDir, DIR_MODE);
346
- await this.fs.writeFile(this.authPath, JSON.stringify(stored, null, 2), FILE_MODE);
442
+ await this.fs.atomicWriteFile(this.authPath, JSON.stringify(stored, null, 2));
443
+ }
444
+ async saveTokensIfUnchanged(baseUrl, expected, data) {
445
+ const current = await this.loadTokens(baseUrl);
446
+ if (!sameTokenData(current, expected))
447
+ return false;
448
+ await this.saveTokens(baseUrl, data);
449
+ return true;
347
450
  }
348
451
  async clearTokens(baseUrl) {
349
452
  const stored = await this.loadAuthFile();
@@ -353,9 +456,16 @@ class AuthStorageImpl {
353
456
  if (Object.keys(stored.tokens).length === 0) {
354
457
  await this.fs.deleteFile(this.authPath);
355
458
  } else {
356
- await this.fs.writeFile(this.authPath, JSON.stringify(stored, null, 2), FILE_MODE);
459
+ await this.fs.atomicWriteFile(this.authPath, JSON.stringify(stored, null, 2));
357
460
  }
358
461
  }
462
+ async clearTokensIfUnchanged(baseUrl, expected) {
463
+ const current = await this.loadTokens(baseUrl);
464
+ if (!sameTokenData(current, expected))
465
+ return false;
466
+ await this.clearTokens(baseUrl);
467
+ return true;
468
+ }
359
469
  async loadClient(baseUrl) {
360
470
  const stored = await this.loadClientFile();
361
471
  if (!stored)
@@ -370,7 +480,7 @@ class AuthStorageImpl {
370
480
  if (Object.keys(stored.clients).length === 0) {
371
481
  await this.fs.deleteFile(this.clientPath);
372
482
  } else {
373
- await this.fs.writeFile(this.clientPath, JSON.stringify(stored, null, 2), FILE_MODE);
483
+ await this.fs.atomicWriteFile(this.clientPath, JSON.stringify(stored, null, 2));
374
484
  }
375
485
  }
376
486
  async saveClient(baseUrl, data) {
@@ -380,7 +490,14 @@ class AuthStorageImpl {
380
490
  };
381
491
  stored.clients[normalizeBaseUrl(baseUrl)] = data;
382
492
  await this.fs.ensureDir(this.configDir, DIR_MODE);
383
- await this.fs.writeFile(this.clientPath, JSON.stringify(stored, null, 2), FILE_MODE);
493
+ await this.fs.atomicWriteFile(this.clientPath, JSON.stringify(stored, null, 2));
494
+ }
495
+ async saveAuthSession(baseUrl, client, tokens) {
496
+ await this.saveClient(baseUrl, client);
497
+ await this.saveTokens(baseUrl, tokens);
498
+ }
499
+ async clearAuthSession(baseUrl) {
500
+ await clearAuthSessionBestEffort(() => this.clearTokens(baseUrl), () => this.clearClient(baseUrl));
384
501
  }
385
502
  async loadAuthFile() {
386
503
  if (!await this.fs.exists(this.authPath))
@@ -412,6 +529,26 @@ class AuthStorageImpl {
412
529
  function normalizeBaseUrl(url) {
413
530
  return url.replace(/\/+$/, "");
414
531
  }
532
+ function sameTokenData(a, b) {
533
+ if (a === null || b === null)
534
+ return a === b;
535
+ return a.accessToken === b.accessToken && a.refreshToken === b.refreshToken && a.expiresAt === b.expiresAt && a.createdAt === b.createdAt;
536
+ }
537
+ async function clearAuthSessionBestEffort(clearTokens, clearClient) {
538
+ let firstError;
539
+ try {
540
+ await clearTokens();
541
+ } catch (error) {
542
+ firstError = error;
543
+ }
544
+ try {
545
+ await clearClient();
546
+ } catch (error) {
547
+ firstError ??= error;
548
+ }
549
+ if (firstError)
550
+ throw firstError;
551
+ }
415
552
  // src/services/browser-service.ts
416
553
  import open from "open";
417
554
 
@@ -531,54 +668,34 @@ class ChunkingKeyringService {
531
668
  }
532
669
  }
533
670
  }
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;
671
+ // src/services/client-update-required-error.ts
672
+ var CLIENT_UPDATE_REQUIRED_REASON = "Backend protocol changed";
673
+
674
+ class ClientUpdateRequiredError extends Error {
675
+ reason;
676
+ currentVersion;
677
+ constructor(message = `Update required: ${CLIENT_UPDATE_REQUIRED_REASON}`, reason = CLIENT_UPDATE_REQUIRED_REASON, currentVersion = version) {
678
+ super(message);
679
+ this.reason = reason;
680
+ this.currentVersion = currentVersion;
681
+ this.name = "ClientUpdateRequiredError";
552
682
  }
553
- return null;
554
- }
555
- function isStringArray(value) {
556
- return Array.isArray(value) && value.every((entry) => typeof entry === "string");
557
683
  }
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;
684
+ function isClientUpdateRequiredGraphQLError(input) {
685
+ if (input.code === "CLIENT_UPDATE_REQUIRED") {
686
+ return true;
687
+ }
688
+ const message = input.message;
689
+ if (!isGraphQLSchemaMismatchMessage(message)) {
690
+ return false;
571
691
  }
692
+ return !input.code || input.code === "GRAPHQL_VALIDATION_FAILED" || input.code === "BAD_USER_INPUT";
572
693
  }
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), "=");
694
+ function isGraphQLSchemaMismatchMessage(message) {
695
+ return /Cannot query field|Field .* does not exist|Unknown argument|Unknown type|Unknown field/i.test(message);
579
696
  }
580
697
  // src/services/code-navigation-service.ts
581
- import { z } from "zod";
698
+ import { z as z2 } from "zod";
582
699
 
583
700
  // src/shared/debug-log.ts
584
701
  function debugLog(area, payload) {
@@ -903,17 +1020,6 @@ async function withTelemetrySpan(name, operation, attributes) {
903
1020
  throw error;
904
1021
  }
905
1022
  }
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
1023
  function startTelemetrySpan(name, attributes) {
918
1024
  return telemetryCollector.startSpan(name, attributes);
919
1025
  }
@@ -1420,80 +1526,80 @@ function asRecord(value) {
1420
1526
  }
1421
1527
  return;
1422
1528
  }
1423
- var availableVersionSchema = z.object({
1424
- version: z.string().nullable().optional(),
1425
- ref: z.string()
1529
+ var availableVersionSchema = z2.object({
1530
+ version: z2.string().nullable().optional(),
1531
+ ref: z2.string()
1426
1532
  });
1427
- var unifiedSearchSourceSchema = z.enum(["AUTO", "DOCS", "CODE", "SYMBOL"]);
1428
- var unifiedSearchResultTypeSchema = z.enum([
1533
+ var unifiedSearchSourceSchema = z2.enum(["AUTO", "DOCS", "CODE", "SYMBOL"]);
1534
+ var unifiedSearchResultTypeSchema = z2.enum([
1429
1535
  "DOCUMENTATION_PAGE",
1430
1536
  "REPOSITORY_SYMBOL",
1431
1537
  "REPOSITORY_CODE",
1432
1538
  "REPOSITORY_DOC"
1433
1539
  ]);
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()
1540
+ var unifiedSearchLocatorSchema = z2.object({
1541
+ registry: z2.string().nullable().optional(),
1542
+ packageName: z2.string().nullable().optional(),
1543
+ version: z2.string().nullable().optional(),
1544
+ pageId: z2.string().nullable().optional(),
1545
+ sourceKind: z2.string().nullable().optional(),
1546
+ sourceUrl: z2.string().nullable().optional(),
1547
+ repoUrl: z2.string().nullable().optional(),
1548
+ gitRef: z2.string().nullable().optional(),
1549
+ requestedRef: z2.string().nullable().optional(),
1550
+ filePath: z2.string().nullable().optional(),
1551
+ startLine: z2.number().int().nullable().optional(),
1552
+ endLine: z2.number().int().nullable().optional(),
1553
+ fileContentHash: z2.string().nullable().optional(),
1554
+ symbolRef: z2.string().nullable().optional(),
1555
+ qualifiedPath: z2.string().nullable().optional(),
1556
+ kind: z2.string().nullable().optional(),
1557
+ category: z2.string().nullable().optional(),
1558
+ language: z2.string().nullable().optional()
1453
1559
  });
1454
- var unifiedSearchHitSchema = z.object({
1455
- id: z.string(),
1560
+ var unifiedSearchHitSchema = z2.object({
1561
+ id: z2.string(),
1456
1562
  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()
1563
+ targetLabel: z2.string(),
1564
+ title: z2.string().nullable().optional(),
1565
+ summary: z2.string().nullable().optional(),
1566
+ score: z2.number().nullable().optional(),
1567
+ highlights: z2.object({
1568
+ title: z2.array(z2.tuple([z2.number().int(), z2.number().int()])).nullable().optional(),
1569
+ summary: z2.array(z2.tuple([z2.number().int(), z2.number().int()])).nullable().optional()
1464
1570
  }).nullable().optional(),
1465
1571
  locator: unifiedSearchLocatorSchema
1466
1572
  });
1467
- var unifiedSearchPageInfoSchema = z.object({
1468
- offset: z.number().int(),
1469
- limit: z.number().int(),
1470
- returned: z.number().int(),
1471
- hasMore: z.boolean()
1573
+ var unifiedSearchPageInfoSchema = z2.object({
1574
+ offset: z2.number().int(),
1575
+ limit: z2.number().int(),
1576
+ returned: z2.number().int(),
1577
+ hasMore: z2.boolean()
1472
1578
  });
1473
- var unifiedSearchSourceStatusSchema = z.object({
1579
+ var unifiedSearchSourceStatusSchema = z2.object({
1474
1580
  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()
1581
+ targetLabel: z2.string(),
1582
+ indexingStatus: z2.string().nullable().optional(),
1583
+ codeIndexState: z2.string().nullable().optional(),
1584
+ resultCount: z2.number().int().nullable().optional(),
1585
+ appliedFilters: z2.array(z2.string()),
1586
+ ignoredFilters: z2.array(z2.string()),
1587
+ incompatibleFilters: z2.array(z2.string()),
1588
+ appliedQueryFeatures: z2.array(z2.string()),
1589
+ ignoredQueryFeatures: z2.array(z2.string()),
1590
+ incompatibleQueryFeatures: z2.array(z2.string()),
1591
+ note: z2.string().nullable().optional()
1486
1592
  });
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),
1593
+ var unifiedSearchResultSchema = z2.object({
1594
+ query: z2.string(),
1595
+ queryWarnings: z2.array(z2.string()),
1596
+ sources: z2.array(unifiedSearchSourceSchema),
1597
+ results: z2.array(unifiedSearchHitSchema),
1492
1598
  page: unifiedSearchPageInfoSchema,
1493
- partialResults: z.boolean(),
1494
- sourceStatus: z.array(unifiedSearchSourceStatusSchema)
1599
+ partialResults: z2.boolean(),
1600
+ sourceStatus: z2.array(unifiedSearchSourceStatusSchema)
1495
1601
  });
1496
- var unifiedSearchSessionStatusSchema = z.enum([
1602
+ var unifiedSearchSessionStatusSchema = z2.enum([
1497
1603
  "PENDING",
1498
1604
  "INDEXING",
1499
1605
  "SEARCHING",
@@ -1501,60 +1607,60 @@ var unifiedSearchSessionStatusSchema = z.enum([
1501
1607
  "TIMEOUT",
1502
1608
  "FAILED"
1503
1609
  ]);
1504
- var unifiedSearchProgressSchema = z.object({
1505
- searchRef: z.string(),
1610
+ var unifiedSearchProgressSchema = z2.object({
1611
+ searchRef: z2.string(),
1506
1612
  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(),
1613
+ targetsTotal: z2.number().int(),
1614
+ targetsReady: z2.number().int(),
1615
+ elapsedMs: z2.number().int(),
1616
+ query: z2.string(),
1617
+ queryWarnings: z2.array(z2.string()),
1618
+ sources: z2.array(unifiedSearchSourceSchema),
1619
+ expiresAt: z2.string().nullable().optional(),
1514
1620
  results: unifiedSearchResultSchema.nullable().optional()
1515
1621
  });
1516
- var asyncUnifiedSearchResultSchema = z.object({
1517
- completed: z.boolean(),
1518
- searchRef: z.string().nullable().optional(),
1622
+ var asyncUnifiedSearchResultSchema = z2.object({
1623
+ completed: z2.boolean(),
1624
+ searchRef: z2.string().nullable().optional(),
1519
1625
  result: unifiedSearchResultSchema.nullable().optional(),
1520
1626
  progress: unifiedSearchProgressSchema.nullable().optional()
1521
1627
  });
1522
- var graphQLErrorSchema = z.object({
1523
- message: z.string(),
1524
- extensions: z.record(z.string(), z.unknown()).optional()
1628
+ var graphQLErrorSchema = z2.object({
1629
+ message: z2.string(),
1630
+ extensions: z2.record(z2.string(), z2.unknown()).optional()
1525
1631
  });
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()
1632
+ var navigationResolutionSchema = z2.object({
1633
+ requestedVersion: z2.string().nullable().optional(),
1634
+ requestedRef: z2.string().nullable().optional(),
1635
+ resolvedRef: z2.string().nullable().optional(),
1636
+ commitSha: z2.string().nullable().optional()
1531
1637
  }).nullable().optional();
1532
- var navigationDiagnosticsSchema = z.object({
1533
- hint: z.string().nullable().optional()
1638
+ var navigationDiagnosticsSchema = z2.object({
1639
+ hint: z2.string().nullable().optional()
1534
1640
  }).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()
1641
+ var repoFileEntrySchema = z2.object({
1642
+ path: z2.string(),
1643
+ name: z2.string().nullable().optional(),
1644
+ language: z2.string().nullable().optional(),
1645
+ fileType: z2.string().nullable().optional(),
1646
+ byteSize: z2.number().int().nullable().optional()
1541
1647
  });
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(),
1648
+ var listRepoFilesResponseSchema = z2.object({
1649
+ files: z2.array(repoFileEntrySchema),
1650
+ total: z2.number().int(),
1651
+ hasMore: z2.boolean(),
1652
+ indexedVersion: z2.string().nullable().optional(),
1547
1653
  resolution: navigationResolutionSchema,
1548
1654
  diagnostics: navigationDiagnosticsSchema,
1549
- codeIndexState: z.string(),
1550
- indexingRef: z.string().nullable().optional(),
1551
- availableVersions: z.array(availableVersionSchema).nullable().optional()
1655
+ codeIndexState: z2.string(),
1656
+ indexingRef: z2.string().nullable().optional(),
1657
+ availableVersions: z2.array(availableVersionSchema).nullable().optional()
1552
1658
  });
1553
- var listRepoFilesGraphQLResponseSchema = z.object({
1554
- data: z.object({
1659
+ var listRepoFilesGraphQLResponseSchema = z2.object({
1660
+ data: z2.object({
1555
1661
  listRepoFiles: listRepoFilesResponseSchema.nullable().optional()
1556
1662
  }).nullable().optional(),
1557
- errors: z.array(graphQLErrorSchema).optional()
1663
+ errors: z2.array(graphQLErrorSchema).optional()
1558
1664
  });
1559
1665
  var LIST_REPO_FILES_QUERY = `
1560
1666
  query ListRepoFiles(
@@ -1604,24 +1710,24 @@ query ListRepoFiles(
1604
1710
  }
1605
1711
  }
1606
1712
  }`;
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()
1713
+ var codeContextResponseSchema = z2.object({
1714
+ content: z2.string().nullable().optional(),
1715
+ filePath: z2.string().nullable().optional(),
1716
+ language: z2.string().nullable().optional(),
1717
+ totalLines: z2.number().int().nullable().optional(),
1718
+ startLine: z2.number().int().nullable().optional(),
1719
+ endLine: z2.number().int().nullable().optional(),
1720
+ repoUrl: z2.string().nullable().optional(),
1721
+ gitRef: z2.string().nullable().optional(),
1722
+ isBinary: z2.boolean().nullable().optional(),
1723
+ codeIndexState: z2.string(),
1724
+ indexingRef: z2.string().nullable().optional()
1619
1725
  });
1620
- var fetchCodeContextGraphQLResponseSchema = z.object({
1621
- data: z.object({
1726
+ var fetchCodeContextGraphQLResponseSchema = z2.object({
1727
+ data: z2.object({
1622
1728
  fetchCodeContext: codeContextResponseSchema.nullable().optional()
1623
1729
  }).nullable().optional(),
1624
- errors: z.array(graphQLErrorSchema).optional()
1730
+ errors: z2.array(graphQLErrorSchema).optional()
1625
1731
  });
1626
1732
  var FETCH_CODE_CONTEXT_QUERY = `
1627
1733
  query FetchCodeContext(
@@ -1659,63 +1765,63 @@ query FetchCodeContext(
1659
1765
  indexingRef
1660
1766
  }
1661
1767
  }`;
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()
1768
+ var grepRepoMatchSchema = z2.object({
1769
+ filePath: z2.string(),
1770
+ line: z2.number().int(),
1771
+ matchStartByte: z2.number().int(),
1772
+ matchEndByte: z2.number().int(),
1773
+ lineContent: z2.string(),
1774
+ contextBefore: z2.array(z2.string()).nullable().optional(),
1775
+ contextAfter: z2.array(z2.string()).nullable().optional(),
1776
+ fileContentHash: z2.string().nullable().optional(),
1777
+ fileIntent: z2.string().nullable().optional(),
1778
+ symbolRowId: z2.string().nullable().optional(),
1779
+ symbol: z2.object({
1780
+ symbolRef: z2.string().optional(),
1781
+ name: z2.string().optional(),
1782
+ qualifiedPath: z2.string().nullable().optional(),
1783
+ kind: z2.string().nullable().optional(),
1784
+ category: z2.string().nullable().optional(),
1785
+ arity: z2.number().int().nullable().optional(),
1786
+ isPublic: z2.boolean().nullable().optional(),
1787
+ filePath: z2.string().nullable().optional(),
1788
+ startLine: z2.number().int().nullable().optional(),
1789
+ endLine: z2.number().int().nullable().optional(),
1790
+ code: z2.string().nullable().optional(),
1791
+ callerCount: z2.number().int().nullable().optional(),
1792
+ contentHash: z2.string().nullable().optional(),
1793
+ parentSymbolRef: z2.string().nullable().optional(),
1794
+ parentPath: z2.string().nullable().optional()
1689
1795
  }).nullable().optional()
1690
1796
  });
1691
- var grepRepoResponseSchema = z.object({
1692
- matches: z.array(grepRepoMatchSchema),
1693
- nextCursor: z.string().nullable().optional(),
1694
- hasMore: z.boolean(),
1695
- truncatedReason: z.enum([
1797
+ var grepRepoResponseSchema = z2.object({
1798
+ matches: z2.array(grepRepoMatchSchema),
1799
+ nextCursor: z2.string().nullable().optional(),
1800
+ hasMore: z2.boolean(),
1801
+ truncatedReason: z2.enum([
1696
1802
  "NONE",
1697
1803
  "MAX_MATCHES",
1698
1804
  "MAX_MATCHES_PER_FILE",
1699
1805
  "DEADLINE"
1700
1806
  ]),
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(),
1807
+ routeTaken: z2.enum(["SINGLE_FILE", "CONTENT_INDEX"]).nullable().optional(),
1808
+ filesScanned: z2.number().int(),
1809
+ filesInScope: z2.number().int(),
1810
+ binaryFilesSkipped: z2.number().int(),
1811
+ filesTooLargeSkipped: z2.number().int(),
1812
+ totalMatches: z2.number().int(),
1813
+ uniqueFilesMatched: z2.number().int(),
1814
+ indexedVersion: z2.string().nullable().optional(),
1709
1815
  resolution: navigationResolutionSchema,
1710
- codeIndexState: z.string(),
1711
- indexingRef: z.string().nullable().optional(),
1712
- availableVersions: z.array(availableVersionSchema).nullable().optional()
1816
+ codeIndexState: z2.string(),
1817
+ indexingRef: z2.string().nullable().optional(),
1818
+ availableVersions: z2.array(availableVersionSchema).nullable().optional()
1713
1819
  });
1714
- var grepRepoGraphQLResponseSchema = z.object({
1715
- data: z.object({
1820
+ var grepRepoGraphQLResponseSchema = z2.object({
1821
+ data: z2.object({
1716
1822
  grepRepo: grepRepoResponseSchema.nullable().optional()
1717
1823
  }).nullable().optional(),
1718
- errors: z.array(graphQLErrorSchema).optional()
1824
+ errors: z2.array(graphQLErrorSchema).optional()
1719
1825
  });
1720
1826
  var GREP_REPO_SYMBOL_SELECTIONS = {
1721
1827
  symbol_ref: "symbolRef",
@@ -1824,17 +1930,17 @@ query GrepRepo(
1824
1930
  }
1825
1931
  }`;
1826
1932
  }
1827
- var unifiedSearchGraphQLResponseSchema = z.object({
1828
- data: z.object({
1933
+ var unifiedSearchGraphQLResponseSchema = z2.object({
1934
+ data: z2.object({
1829
1935
  search: asyncUnifiedSearchResultSchema.nullable().optional()
1830
1936
  }).nullable().optional(),
1831
- errors: z.array(graphQLErrorSchema).optional()
1937
+ errors: z2.array(graphQLErrorSchema).optional()
1832
1938
  });
1833
- var unifiedSearchStatusGraphQLResponseSchema = z.object({
1834
- data: z.object({
1939
+ var unifiedSearchStatusGraphQLResponseSchema = z2.object({
1940
+ data: z2.object({
1835
1941
  discoverySearchProgress: unifiedSearchProgressSchema.nullable().optional()
1836
1942
  }).nullable().optional(),
1837
- errors: z.array(graphQLErrorSchema).optional()
1943
+ errors: z2.array(graphQLErrorSchema).optional()
1838
1944
  });
1839
1945
 
1840
1946
  class CodeNavigationServiceImpl {
@@ -1987,6 +2093,9 @@ class CodeNavigationServiceImpl {
1987
2093
  const code = typeof extensions?.code === "string" ? extensions.code : undefined;
1988
2094
  const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
1989
2095
  const indexingRef = getGraphQLIndexingRef(errors);
2096
+ if (isClientUpdateRequiredGraphQLError({ message, code })) {
2097
+ return new ClientUpdateRequiredError;
2098
+ }
1990
2099
  switch (code) {
1991
2100
  case "PACKAGE_INDEXING":
1992
2101
  return new CodeNavigationIndexingError(this.createIndexingMessage(indexingRef), indexingRef, parseAvailableVersions(extensions));
@@ -2481,19 +2590,11 @@ function getCodeNavigationUrl() {
2481
2590
  if (explicitUrl) {
2482
2591
  return explicitUrl;
2483
2592
  }
2484
- const usingDefaultEnvironment = getMcpUrl() === DEFAULT_MCP_URL && getApiUrl() === DEFAULT_API_URL;
2485
- return usingDefaultEnvironment ? DEFAULT_CODE_NAV_URL : undefined;
2593
+ return DEFAULT_CODE_NAV_URL;
2486
2594
  }
2487
2595
  function getEnvApiToken() {
2488
2596
  return process.env.GITHITS_API_TOKEN;
2489
2597
  }
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
2598
  // src/services/exec-service.ts
2498
2599
  import { spawn } from "node:child_process";
2499
2600
 
@@ -2523,6 +2624,7 @@ class ExecServiceImpl {
2523
2624
  }
2524
2625
  }
2525
2626
  // src/services/filesystem-service.ts
2627
+ import { randomUUID as randomUUID2 } from "node:crypto";
2526
2628
  import {
2527
2629
  mkdir,
2528
2630
  readdir,
@@ -2586,7 +2688,7 @@ class FileSystemServiceImpl {
2586
2688
  }
2587
2689
  }
2588
2690
  async atomicWriteFile(path, contents) {
2589
- const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
2691
+ const tmpPath = `${path}.${process.pid}.${randomUUID2()}.tmp`;
2590
2692
  let mode = 384;
2591
2693
  try {
2592
2694
  const existing = await stat(path);
@@ -2649,10 +2751,24 @@ class KeychainAuthStorage {
2649
2751
  const key = `${TOKEN_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2650
2752
  this.keyring.setPassword(SERVICE_NAME, key, JSON.stringify(data));
2651
2753
  }
2754
+ async saveTokensIfUnchanged(baseUrl2, expected, data) {
2755
+ const current = await this.loadTokens(baseUrl2);
2756
+ if (!sameTokenData(current, expected))
2757
+ return false;
2758
+ await this.saveTokens(baseUrl2, data);
2759
+ return true;
2760
+ }
2652
2761
  async clearTokens(baseUrl2) {
2653
2762
  const key = `${TOKEN_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2654
2763
  this.keyring.deletePassword(SERVICE_NAME, key);
2655
2764
  }
2765
+ async clearTokensIfUnchanged(baseUrl2, expected) {
2766
+ const current = await this.loadTokens(baseUrl2);
2767
+ if (!sameTokenData(current, expected))
2768
+ return false;
2769
+ await this.clearTokens(baseUrl2);
2770
+ return true;
2771
+ }
2656
2772
  async loadClient(baseUrl2) {
2657
2773
  const key = `${CLIENT_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2658
2774
  const json = this.keyring.getPassword(SERVICE_NAME, key);
@@ -2669,6 +2785,13 @@ class KeychainAuthStorage {
2669
2785
  const key = `${CLIENT_PREFIX}${normalizeBaseUrl(baseUrl2)}`;
2670
2786
  this.keyring.deletePassword(SERVICE_NAME, key);
2671
2787
  }
2788
+ async saveAuthSession(baseUrl2, client, tokens) {
2789
+ await this.saveClient(baseUrl2, client);
2790
+ await this.saveTokens(baseUrl2, tokens);
2791
+ }
2792
+ async clearAuthSession(baseUrl2) {
2793
+ await clearAuthSessionBestEffort(() => this.clearTokens(baseUrl2), () => this.clearClient(baseUrl2));
2794
+ }
2672
2795
  getStorageLocation() {
2673
2796
  switch (process.platform) {
2674
2797
  case "darwin":
@@ -2718,179 +2841,644 @@ class KeyringServiceImpl {
2718
2841
  }
2719
2842
  }
2720
2843
  }
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;
2844
+ // src/services/locked-auth-storage.ts
2845
+ import { execFile } from "node:child_process";
2846
+ import { randomUUID as randomUUID3 } from "node:crypto";
2847
+ import { mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "node:fs/promises";
2848
+ import { dirname as dirname2 } from "node:path";
2849
+ import { promisify } from "node:util";
2850
+ var LOCK_DIR = "auth.lock";
2851
+ var LOCK_TIMEOUT_MS = 1e4;
2852
+ var LOCK_RETRY_MS = 25;
2853
+ var ORPHANED_LOCK_MS = 5000;
2854
+ var OWNER_FILE = "owner.json";
2855
+ var execFileAsync = promisify(execFile);
2856
+
2857
+ class AuthStorageLockTimeoutError extends Error {
2858
+ constructor(message) {
2859
+ super(message);
2860
+ this.name = "AuthStorageLockTimeoutError";
2732
2861
  }
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;
2862
+ }
2863
+
2864
+ class LockedAuthStorage {
2865
+ storage;
2866
+ lockPath;
2867
+ lockTimeoutMs;
2868
+ isOwnerAlive;
2869
+ currentOwner = null;
2870
+ constructor(storage, fileSystemService, options = {}) {
2871
+ this.storage = storage;
2872
+ this.lockTimeoutMs = options.lockTimeoutMs ?? LOCK_TIMEOUT_MS;
2873
+ this.isOwnerAlive = options.isOwnerAlive ?? isOriginalProcessAlive;
2874
+ this.lockPath = fileSystemService.joinPath(getAppConfigDir(fileSystemService), LOCK_DIR);
2875
+ }
2876
+ loadTokens(baseUrl2) {
2877
+ return this.storage.loadTokens(baseUrl2);
2878
+ }
2879
+ saveTokens(baseUrl2, data) {
2880
+ return this.withLock(() => this.storage.saveTokens(baseUrl2, data));
2881
+ }
2882
+ saveTokensIfUnchanged(baseUrl2, expected, data) {
2883
+ return this.withLock(() => this.storage.saveTokensIfUnchanged(baseUrl2, expected, data));
2884
+ }
2885
+ clearTokens(baseUrl2) {
2886
+ return this.withLock(() => this.storage.clearTokens(baseUrl2));
2887
+ }
2888
+ clearTokensIfUnchanged(baseUrl2, expected) {
2889
+ return this.withLock(() => this.storage.clearTokensIfUnchanged(baseUrl2, expected));
2890
+ }
2891
+ loadClient(baseUrl2) {
2892
+ return this.storage.loadClient(baseUrl2);
2893
+ }
2894
+ saveClient(baseUrl2, data) {
2895
+ return this.withLock(() => this.storage.saveClient(baseUrl2, data));
2896
+ }
2897
+ clearClient(baseUrl2) {
2898
+ return this.withLock(() => this.storage.clearClient(baseUrl2));
2899
+ }
2900
+ saveAuthSession(baseUrl2, client, tokens) {
2901
+ return this.withLock(() => this.storage.saveAuthSession(baseUrl2, client, tokens));
2902
+ }
2903
+ clearAuthSession(baseUrl2) {
2904
+ return this.withLock(() => this.storage.clearAuthSession(baseUrl2));
2905
+ }
2906
+ getStorageLocation() {
2907
+ return this.storage.getStorageLocation();
2908
+ }
2909
+ async withLock(fn) {
2910
+ await this.acquireLock();
2749
2911
  try {
2750
- await this.primary.saveTokens(baseUrl2, legacyTokens);
2751
- } catch (error) {
2752
- if (!this.handlePrimaryFailure(error))
2753
- throw error;
2754
- return legacyTokens;
2912
+ return await fn();
2913
+ } finally {
2914
+ await this.releaseLock();
2755
2915
  }
2756
- try {
2757
- await this.legacy.clearTokens(baseUrl2);
2758
- } catch {}
2759
- return legacyTokens;
2760
2916
  }
2761
- async saveTokens(baseUrl2, data) {
2762
- if (this.primaryAvailable) {
2917
+ async acquireLock() {
2918
+ const startedAt = Date.now();
2919
+ await mkdir2(dirname2(this.lockPath), { recursive: true, mode: 448 });
2920
+ while (true) {
2763
2921
  try {
2764
- await this.primary.saveTokens(baseUrl2, data);
2922
+ await mkdir2(this.lockPath, { recursive: false, mode: 448 });
2923
+ try {
2924
+ await this.writeOwner();
2925
+ } catch (error) {
2926
+ await rm(this.lockPath, { recursive: true, force: true }).catch(() => {
2927
+ return;
2928
+ });
2929
+ throw error;
2930
+ }
2765
2931
  return;
2766
2932
  } catch (error) {
2767
- if (!this.handlePrimaryFailure(error))
2933
+ if (error.code !== "EEXIST")
2768
2934
  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;
2935
+ await this.reclaimStaleLock();
2936
+ if (Date.now() - startedAt >= this.lockTimeoutMs) {
2937
+ throw new AuthStorageLockTimeoutError(`Timed out waiting for GitHits auth storage lock at ${this.lockPath}. If no githits process is running, remove this directory and retry.`);
2781
2938
  }
2939
+ await sleep(LOCK_RETRY_MS);
2782
2940
  }
2783
2941
  }
2784
- try {
2785
- await this.legacy.clearTokens(baseUrl2);
2786
- } catch {}
2787
- if (primaryError)
2788
- throw primaryError;
2789
2942
  }
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;
2943
+ async writeOwner() {
2944
+ const owner = {
2945
+ id: randomUUID3(),
2946
+ pid: process.pid,
2947
+ createdAt: new Date().toISOString(),
2948
+ processStartedAt: await getProcessStartedAt(process.pid)
2949
+ };
2950
+ this.currentOwner = owner;
2951
+ await writeFile2(this.ownerPath(), JSON.stringify(owner), { mode: 384 });
2817
2952
  }
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
- }
2953
+ async reclaimStaleLock() {
2954
+ const owner = await this.readOwner();
2955
+ if (!owner) {
2956
+ await this.reclaimOldOwnerlessLock();
2957
+ return;
2827
2958
  }
2828
- await this.legacy.saveClient(baseUrl2, data);
2959
+ const ownerDead = !await this.isOwnerAlive(owner.pid, owner.processStartedAt);
2960
+ if (!ownerDead)
2961
+ return;
2962
+ const currentOwner = await this.readOwner();
2963
+ if (!currentOwner || currentOwner.id !== owner.id)
2964
+ return;
2965
+ await rm(this.lockPath, { recursive: true, force: true }).catch(() => {
2966
+ return;
2967
+ });
2829
2968
  }
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
- }
2969
+ async reclaimOldOwnerlessLock() {
2970
+ const createdAtMs = await lockCreatedAtMs(this.lockPath);
2971
+ if (Date.now() - createdAtMs < ORPHANED_LOCK_MS)
2972
+ return;
2973
+ await rm(this.lockPath, { recursive: true, force: true }).catch(() => {
2974
+ return;
2975
+ });
2976
+ }
2977
+ async readOwner() {
2978
+ try {
2979
+ const raw = await readFile2(this.ownerPath(), "utf8");
2980
+ const parsed = JSON.parse(raw);
2981
+ if (typeof parsed.id !== "string" || typeof parsed.pid !== "number" || typeof parsed.createdAt !== "string" || !(typeof parsed.processStartedAt === "string" || parsed.processStartedAt === null)) {
2982
+ return null;
2839
2983
  }
2984
+ return {
2985
+ id: parsed.id,
2986
+ pid: parsed.pid,
2987
+ createdAt: parsed.createdAt,
2988
+ processStartedAt: parsed.processStartedAt
2989
+ };
2990
+ } catch {
2991
+ return null;
2840
2992
  }
2841
- try {
2842
- await this.legacy.clearClient(baseUrl2);
2843
- } catch {}
2844
- if (primaryError)
2845
- throw primaryError;
2846
2993
  }
2847
- getStorageLocation() {
2848
- return this.primaryAvailable ? this.primary.getStorageLocation() : this.legacy.getStorageLocation();
2994
+ async releaseLock() {
2995
+ const owner = this.currentOwner;
2996
+ this.currentOwner = null;
2997
+ if (!owner)
2998
+ return;
2999
+ const currentOwner = await this.readOwner();
3000
+ if (!currentOwner || currentOwner.id !== owner.id)
3001
+ return;
3002
+ await rm(this.lockPath, { recursive: true, force: true }).catch(() => {
3003
+ return;
3004
+ });
2849
3005
  }
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;
3006
+ ownerPath() {
3007
+ return `${this.lockPath}/${OWNER_FILE}`;
2860
3008
  }
2861
3009
  }
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);
3010
+ function sleep(ms) {
3011
+ return new Promise((resolve) => setTimeout(resolve, ms));
2878
3012
  }
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}`;
3013
+ async function isOriginalProcessAlive(pid, processStartedAt) {
3014
+ if (!isProcessAlive(pid))
3015
+ return false;
3016
+ if (!processStartedAt)
3017
+ return true;
3018
+ return await getProcessStartedAt(pid) === processStartedAt;
2892
3019
  }
2893
-
3020
+ function isProcessAlive(pid) {
3021
+ if (pid <= 0)
3022
+ return false;
3023
+ try {
3024
+ process.kill(pid, 0);
3025
+ return true;
3026
+ } catch (error) {
3027
+ const code = error.code;
3028
+ return code === "EPERM";
3029
+ }
3030
+ }
3031
+ async function getProcessStartedAt(pid) {
3032
+ try {
3033
+ if (process.platform === "win32") {
3034
+ const { stdout: stdout2 } = await execFileAsync("powershell.exe", [
3035
+ "-NoProfile",
3036
+ "-Command",
3037
+ `(Get-Process -Id ${pid}).StartTime.ToUniversalTime().ToString('o')`
3038
+ ]);
3039
+ return stdout2.trim() || null;
3040
+ }
3041
+ const { stdout } = await execFileAsync("ps", [
3042
+ "-p",
3043
+ String(pid),
3044
+ "-o",
3045
+ "lstart="
3046
+ ]);
3047
+ const parsed = Date.parse(stdout.trim());
3048
+ return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
3049
+ } catch {
3050
+ return null;
3051
+ }
3052
+ }
3053
+ async function lockCreatedAtMs(path) {
3054
+ try {
3055
+ const { stat: stat2 } = await import("node:fs/promises");
3056
+ return (await stat2(path)).mtimeMs;
3057
+ } catch {
3058
+ return 0;
3059
+ }
3060
+ }
3061
+ // src/services/mode-aware-file-auth-storage.ts
3062
+ class AuthStoragePolicyError extends Error {
3063
+ constructor(message) {
3064
+ super(message);
3065
+ this.name = "AuthStoragePolicyError";
3066
+ }
3067
+ }
3068
+ function createFileAuthStorageGuidance(configPath) {
3069
+ return `OAuth credentials were not saved to plaintext file storage.
3070
+
3071
+ Options:
3072
+ 1. Unlock or fix your system keychain.
3073
+ 2. Use GITHITS_API_TOKEN for CI/automation.
3074
+ 3. If you accept storing OAuth credentials unencrypted on disk, set:
3075
+
3076
+ [auth]
3077
+ storage = "file"
3078
+
3079
+ in ${configPath}, or run with GITHITS_AUTH_STORAGE=file.
3080
+
3081
+ Warning: file storage is plaintext. Use it only on machines where local file access is trusted.`;
3082
+ }
3083
+
3084
+ class ModeAwareFileAuthStorage {
3085
+ storage;
3086
+ mode;
3087
+ configPath;
3088
+ constructor(storage, mode, configPath = "your GitHits config.toml") {
3089
+ this.storage = storage;
3090
+ this.mode = mode;
3091
+ this.configPath = configPath;
3092
+ }
3093
+ loadTokens(baseUrl2) {
3094
+ return this.storage.loadTokens(baseUrl2);
3095
+ }
3096
+ async saveTokens(baseUrl2, data) {
3097
+ this.assertFileMode();
3098
+ await this.storage.saveTokens(baseUrl2, data);
3099
+ }
3100
+ async saveTokensIfUnchanged(baseUrl2, expected, data) {
3101
+ this.assertFileMode();
3102
+ return this.storage.saveTokensIfUnchanged(baseUrl2, expected, data);
3103
+ }
3104
+ clearTokens(baseUrl2) {
3105
+ return this.storage.clearTokens(baseUrl2);
3106
+ }
3107
+ clearTokensIfUnchanged(baseUrl2, expected) {
3108
+ return this.storage.clearTokensIfUnchanged(baseUrl2, expected);
3109
+ }
3110
+ loadClient(baseUrl2) {
3111
+ return this.storage.loadClient(baseUrl2);
3112
+ }
3113
+ async saveClient(baseUrl2, data) {
3114
+ this.assertFileMode();
3115
+ await this.storage.saveClient(baseUrl2, data);
3116
+ }
3117
+ clearClient(baseUrl2) {
3118
+ return this.storage.clearClient(baseUrl2);
3119
+ }
3120
+ async saveAuthSession(baseUrl2, client, tokens) {
3121
+ this.assertFileMode();
3122
+ await this.storage.saveAuthSession(baseUrl2, client, tokens);
3123
+ }
3124
+ clearAuthSession(baseUrl2) {
3125
+ return this.storage.clearAuthSession(baseUrl2);
3126
+ }
3127
+ getStorageLocation() {
3128
+ return this.storage.getStorageLocation();
3129
+ }
3130
+ assertFileMode() {
3131
+ if (this.mode === "file")
3132
+ return;
3133
+ throw new AuthStoragePolicyError(createFileAuthStorageGuidance(this.configPath));
3134
+ }
3135
+ }
3136
+
3137
+ // src/services/migrating-auth-storage.ts
3138
+ class MigratingAuthStorage {
3139
+ primary;
3140
+ file;
3141
+ legacy;
3142
+ mode;
3143
+ configPath;
3144
+ onWarning;
3145
+ warnedFileModeKeychainExport = false;
3146
+ warnedAmbiguousPlaintext = false;
3147
+ constructor(primary, file, legacy, mode, configPath = "your GitHits config.toml", onWarning = () => {}) {
3148
+ this.primary = primary;
3149
+ this.file = file;
3150
+ this.legacy = legacy;
3151
+ this.mode = mode;
3152
+ this.configPath = configPath;
3153
+ this.onWarning = onWarning;
3154
+ }
3155
+ async loadTokens(baseUrl2) {
3156
+ if (this.mode === "file") {
3157
+ return this.loadTokensFileMode(baseUrl2);
3158
+ }
3159
+ return this.loadTokensKeychainMode(baseUrl2);
3160
+ }
3161
+ async saveTokens(baseUrl2, data) {
3162
+ if (this.mode === "file") {
3163
+ await this.file.saveTokens(baseUrl2, data);
3164
+ return;
3165
+ }
3166
+ try {
3167
+ await this.primary.saveTokens(baseUrl2, data);
3168
+ } catch (error) {
3169
+ throw this.toPolicyError(error);
3170
+ }
3171
+ }
3172
+ async saveTokensIfUnchanged(baseUrl2, expected, data) {
3173
+ const current = await this.loadTokens(baseUrl2);
3174
+ if (!this.sameTokenData(current, expected))
3175
+ return false;
3176
+ await this.saveTokens(baseUrl2, data);
3177
+ return true;
3178
+ }
3179
+ async clearTokens(baseUrl2) {
3180
+ const primaryError = await this.clearBestEffort(() => this.primary.clearTokens(baseUrl2));
3181
+ await this.clearBestEffort(() => this.file.clearTokens(baseUrl2));
3182
+ await this.clearBestEffort(() => this.legacy.clearTokens(baseUrl2));
3183
+ if (primaryError && !(primaryError instanceof KeychainUnavailableError)) {
3184
+ throw primaryError;
3185
+ }
3186
+ }
3187
+ async clearTokensIfUnchanged(baseUrl2, expected) {
3188
+ const current = await this.loadTokens(baseUrl2);
3189
+ if (!this.sameTokenData(current, expected))
3190
+ return false;
3191
+ await this.clearTokens(baseUrl2);
3192
+ return true;
3193
+ }
3194
+ async loadClient(baseUrl2) {
3195
+ if (this.mode === "file") {
3196
+ return this.loadClientFileMode(baseUrl2);
3197
+ }
3198
+ return this.loadClientKeychainMode(baseUrl2);
3199
+ }
3200
+ async saveClient(baseUrl2, data) {
3201
+ if (this.mode === "file") {
3202
+ await this.file.saveClient(baseUrl2, data);
3203
+ return;
3204
+ }
3205
+ try {
3206
+ await this.primary.saveClient(baseUrl2, data);
3207
+ } catch (error) {
3208
+ throw this.toPolicyError(error);
3209
+ }
3210
+ }
3211
+ async clearClient(baseUrl2) {
3212
+ const primaryError = await this.clearBestEffort(() => this.primary.clearClient(baseUrl2));
3213
+ await this.clearBestEffort(() => this.file.clearClient(baseUrl2));
3214
+ await this.clearBestEffort(() => this.legacy.clearClient(baseUrl2));
3215
+ if (primaryError && !(primaryError instanceof KeychainUnavailableError)) {
3216
+ throw primaryError;
3217
+ }
3218
+ }
3219
+ async saveAuthSession(baseUrl2, client, tokens) {
3220
+ if (this.mode === "file") {
3221
+ await this.file.saveAuthSession(baseUrl2, client, tokens);
3222
+ return;
3223
+ }
3224
+ try {
3225
+ await this.primary.saveAuthSession(baseUrl2, client, tokens);
3226
+ } catch (error) {
3227
+ throw this.toPolicyError(error);
3228
+ }
3229
+ }
3230
+ async clearAuthSession(baseUrl2) {
3231
+ const primaryError = await this.clearBestEffort(() => this.primary.clearAuthSession(baseUrl2));
3232
+ await this.clearBestEffort(() => this.file.clearAuthSession(baseUrl2));
3233
+ await this.clearBestEffort(() => this.legacy.clearAuthSession(baseUrl2));
3234
+ if (primaryError && !(primaryError instanceof KeychainUnavailableError)) {
3235
+ throw primaryError;
3236
+ }
3237
+ }
3238
+ getStorageLocation() {
3239
+ return this.mode === "file" ? this.file.getStorageLocation() : this.primary.getStorageLocation();
3240
+ }
3241
+ async loadTokensKeychainMode(baseUrl2) {
3242
+ try {
3243
+ const primaryTokens = await this.primary.loadTokens(baseUrl2);
3244
+ if (primaryTokens)
3245
+ return primaryTokens;
3246
+ } catch (error) {
3247
+ if (!(error instanceof KeychainUnavailableError))
3248
+ throw error;
3249
+ }
3250
+ const candidate = await this.selectPlaintextTokenCandidate(baseUrl2);
3251
+ if (!candidate)
3252
+ return null;
3253
+ try {
3254
+ await this.primary.saveTokens(baseUrl2, candidate.data);
3255
+ } catch (error) {
3256
+ if (error instanceof KeychainUnavailableError)
3257
+ return candidate.data;
3258
+ throw error;
3259
+ }
3260
+ await this.clearMigratedPlaintext(baseUrl2, "tokens", candidate.source, candidate.ambiguous);
3261
+ return candidate.data;
3262
+ }
3263
+ async loadTokensFileMode(baseUrl2) {
3264
+ const candidate = await this.selectPlaintextTokenCandidate(baseUrl2);
3265
+ if (candidate) {
3266
+ if (candidate.source === "legacy") {
3267
+ await this.file.saveTokens(baseUrl2, candidate.data);
3268
+ await this.clearBestEffort(() => this.legacy.clearTokens(baseUrl2));
3269
+ }
3270
+ return candidate.data;
3271
+ }
3272
+ let primaryTokens = null;
3273
+ try {
3274
+ primaryTokens = await this.primary.loadTokens(baseUrl2);
3275
+ } catch (error) {
3276
+ if (!(error instanceof KeychainUnavailableError))
3277
+ throw error;
3278
+ }
3279
+ if (!primaryTokens)
3280
+ return null;
3281
+ this.warnKeychainExport();
3282
+ await this.file.saveTokens(baseUrl2, primaryTokens);
3283
+ return primaryTokens;
3284
+ }
3285
+ async loadClientKeychainMode(baseUrl2) {
3286
+ try {
3287
+ const primaryClient = await this.primary.loadClient(baseUrl2);
3288
+ if (primaryClient)
3289
+ return primaryClient;
3290
+ } catch (error) {
3291
+ if (!(error instanceof KeychainUnavailableError))
3292
+ throw error;
3293
+ }
3294
+ const candidate = await this.selectPlaintextClientCandidate(baseUrl2);
3295
+ if (!candidate)
3296
+ return null;
3297
+ try {
3298
+ await this.primary.saveClient(baseUrl2, candidate.data);
3299
+ } catch (error) {
3300
+ if (error instanceof KeychainUnavailableError)
3301
+ return candidate.data;
3302
+ throw error;
3303
+ }
3304
+ await this.clearMigratedPlaintext(baseUrl2, "client", candidate.source, candidate.ambiguous);
3305
+ return candidate.data;
3306
+ }
3307
+ async loadClientFileMode(baseUrl2) {
3308
+ const candidate = await this.selectPlaintextClientCandidate(baseUrl2);
3309
+ if (candidate) {
3310
+ if (candidate.source === "legacy") {
3311
+ await this.file.saveClient(baseUrl2, candidate.data);
3312
+ await this.clearBestEffort(() => this.legacy.clearClient(baseUrl2));
3313
+ }
3314
+ return candidate.data;
3315
+ }
3316
+ let primaryClient = null;
3317
+ try {
3318
+ primaryClient = await this.primary.loadClient(baseUrl2);
3319
+ } catch (error) {
3320
+ if (!(error instanceof KeychainUnavailableError))
3321
+ throw error;
3322
+ }
3323
+ if (!primaryClient)
3324
+ return null;
3325
+ this.warnKeychainExport();
3326
+ await this.file.saveClient(baseUrl2, primaryClient);
3327
+ return primaryClient;
3328
+ }
3329
+ async selectPlaintextTokenCandidate(baseUrl2) {
3330
+ const candidates = [];
3331
+ const fileTokens = await this.file.loadTokens(baseUrl2);
3332
+ if (fileTokens) {
3333
+ candidates.push({
3334
+ data: fileTokens,
3335
+ source: "file",
3336
+ timestamp: fileTokens.createdAt,
3337
+ ambiguous: false
3338
+ });
3339
+ }
3340
+ const legacyTokens = await this.legacy.loadTokens(baseUrl2);
3341
+ if (legacyTokens) {
3342
+ candidates.push({
3343
+ data: legacyTokens,
3344
+ source: "legacy",
3345
+ timestamp: legacyTokens.createdAt,
3346
+ ambiguous: false
3347
+ });
3348
+ }
3349
+ return this.selectNewestCandidate(candidates);
3350
+ }
3351
+ async selectPlaintextClientCandidate(baseUrl2) {
3352
+ const candidates = [];
3353
+ const fileClient = await this.file.loadClient(baseUrl2);
3354
+ if (fileClient) {
3355
+ candidates.push({
3356
+ data: fileClient,
3357
+ source: "file",
3358
+ timestamp: fileClient.registeredAt,
3359
+ ambiguous: false
3360
+ });
3361
+ }
3362
+ const legacyClient = await this.legacy.loadClient(baseUrl2);
3363
+ if (legacyClient) {
3364
+ candidates.push({
3365
+ data: legacyClient,
3366
+ source: "legacy",
3367
+ timestamp: legacyClient.registeredAt,
3368
+ ambiguous: false
3369
+ });
3370
+ }
3371
+ return this.selectNewestCandidate(candidates);
3372
+ }
3373
+ selectNewestCandidate(candidates) {
3374
+ if (candidates.length === 0)
3375
+ return null;
3376
+ if (candidates.length === 1)
3377
+ return candidates[0] ?? null;
3378
+ const parsed = candidates.map((candidate) => ({
3379
+ candidate,
3380
+ timestampMs: Date.parse(candidate.timestamp)
3381
+ }));
3382
+ if (parsed.some((entry) => Number.isNaN(entry.timestampMs))) {
3383
+ this.warnAmbiguousPlaintext();
3384
+ const selected = candidates.find((candidate) => candidate.source === "file") ?? candidates[0] ?? null;
3385
+ if (selected)
3386
+ selected.ambiguous = true;
3387
+ return selected;
3388
+ }
3389
+ const sorted = [...parsed].sort((a, b) => b.timestampMs - a.timestampMs);
3390
+ const first = sorted[0];
3391
+ const second = sorted[1];
3392
+ if (!first)
3393
+ return candidates[0] ?? null;
3394
+ if (second && first.timestampMs === second.timestampMs) {
3395
+ this.warnAmbiguousPlaintext();
3396
+ const selected = candidates.find((candidate) => candidate.source === "file") ?? first.candidate;
3397
+ selected.ambiguous = true;
3398
+ return selected;
3399
+ }
3400
+ return first.candidate;
3401
+ }
3402
+ async clearMigratedPlaintext(baseUrl2, kind, source, ambiguous = false) {
3403
+ if (!ambiguous) {
3404
+ await this.clearPlaintextSource(this.file, baseUrl2, kind);
3405
+ await this.clearPlaintextSource(this.legacy, baseUrl2, kind);
3406
+ return;
3407
+ }
3408
+ if (source === "file") {
3409
+ await this.clearPlaintextSource(this.file, baseUrl2, kind);
3410
+ return;
3411
+ }
3412
+ if (source === "legacy") {
3413
+ await this.clearPlaintextSource(this.legacy, baseUrl2, kind);
3414
+ }
3415
+ }
3416
+ async clearPlaintextSource(storage, baseUrl2, kind) {
3417
+ await this.clearBestEffort(() => kind === "tokens" ? storage.clearTokens(baseUrl2) : storage.clearClient(baseUrl2));
3418
+ }
3419
+ async clearBestEffort(fn) {
3420
+ try {
3421
+ await fn();
3422
+ return;
3423
+ } catch (error) {
3424
+ return error;
3425
+ }
3426
+ }
3427
+ toPolicyError(error) {
3428
+ if (!(error instanceof KeychainUnavailableError))
3429
+ return error;
3430
+ return new AuthStoragePolicyError(`System keychain is unavailable. ${createFileAuthStorageGuidance(this.configPath)}`);
3431
+ }
3432
+ warnKeychainExport() {
3433
+ if (this.warnedFileModeKeychainExport)
3434
+ return;
3435
+ this.warnedFileModeKeychainExport = true;
3436
+ this.onWarning("Warning: auth.storage=file is exporting existing keychain credentials to plaintext file storage.");
3437
+ }
3438
+ warnAmbiguousPlaintext() {
3439
+ if (this.warnedAmbiguousPlaintext)
3440
+ return;
3441
+ this.warnedAmbiguousPlaintext = true;
3442
+ this.onWarning("Warning: multiple plaintext auth entries exist with ambiguous timestamps; using the new config auth path and leaving the other entry intact.");
3443
+ }
3444
+ sameTokenData(a, b) {
3445
+ if (a === null || b === null)
3446
+ return a === b;
3447
+ return a.accessToken === b.accessToken && a.refreshToken === b.refreshToken && a.expiresAt === b.expiresAt && a.createdAt === b.createdAt;
3448
+ }
3449
+ }
3450
+ // src/services/package-intelligence-service.ts
3451
+ import { z as z3 } from "zod";
3452
+
3453
+ // src/services/promote-version-not-found.ts
3454
+ function promoteGenericVersionNotFound(error, params) {
3455
+ if (!(error instanceof PackageIntelligenceBackendError))
3456
+ return error;
3457
+ if (error.graphqlCode !== undefined)
3458
+ return error;
3459
+ const requestedVersion = pickRequestedVersion(params);
3460
+ if (!requestedVersion)
3461
+ return error;
3462
+ if (!/no matching version/i.test(error.message))
3463
+ return error;
3464
+ const qualifiedName = synthesizeQualifiedName(params);
3465
+ return new PackageIntelligenceVersionNotFoundError(error.message, qualifiedName, requestedVersion, undefined);
3466
+ }
3467
+ function pickRequestedVersion(params) {
3468
+ if (params.version)
3469
+ return params.version;
3470
+ if (params.fromVersion)
3471
+ return params.fromVersion;
3472
+ if (params.toVersion)
3473
+ return params.toVersion;
3474
+ return;
3475
+ }
3476
+ function synthesizeQualifiedName(params) {
3477
+ if (!params.registry || !params.packageName)
3478
+ return;
3479
+ return `${params.registry.toLowerCase()}:${params.packageName}`;
3480
+ }
3481
+
2894
3482
  // src/services/package-intelligence-service.ts
2895
3483
  class PackageIntelligenceAccessError extends Error {
2896
3484
  constructor(message) {
@@ -2975,63 +3563,63 @@ class PackageIntelligenceChangelogSourceNotFoundError extends Error {
2975
3563
  this.name = "PackageIntelligenceChangelogSourceNotFoundError";
2976
3564
  }
2977
3565
  }
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()
3566
+ var githubRepositorySchema = z3.object({
3567
+ stargazersCount: z3.number().int().nullable().optional(),
3568
+ forksCount: z3.number().int().nullable().optional(),
3569
+ openIssuesCount: z3.number().int().nullable().optional(),
3570
+ archived: z3.boolean().nullable().optional(),
3571
+ language: z3.string().nullable().optional(),
3572
+ topics: z3.array(z3.string()).nullable().optional(),
3573
+ pushedAt: z3.string().nullable().optional()
2986
3574
  }).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(),
3575
+ var packageIdentitySchema = z3.object({
3576
+ name: z3.string().nullable().optional(),
3577
+ registry: z3.string().nullable().optional(),
3578
+ description: z3.string().nullable().optional(),
3579
+ latestVersion: z3.string().nullable().optional(),
3580
+ latestVersionPublishedAt: z3.string().nullable().optional(),
3581
+ homepage: z3.string().nullable().optional(),
3582
+ repositoryUrl: z3.string().nullable().optional(),
3583
+ license: z3.string().nullable().optional(),
3584
+ downloadsLastMonth: z3.number().int().nullable().optional(),
3585
+ downloadsTotal: z3.number().int().nullable().optional(),
2998
3586
  githubRepository: githubRepositorySchema
2999
3587
  });
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()
3588
+ var vulnerabilityOverviewSchema = z3.object({
3589
+ osvId: z3.string().nullable().optional(),
3590
+ summary: z3.string().nullable().optional(),
3591
+ severityScore: z3.number().nullable().optional(),
3592
+ publishedAt: z3.string().nullable().optional()
3005
3593
  });
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()
3594
+ var packageSecurityOverviewSchema = z3.object({
3595
+ vulnerabilityCount: z3.number().int().nullable().optional(),
3596
+ hasCurrentVulnerabilities: z3.boolean().nullable().optional(),
3597
+ recentVulnerabilities: z3.array(vulnerabilityOverviewSchema).nullable().optional()
3010
3598
  }).nullable().optional();
3011
- var quickstartInfoSchema = z2.object({
3012
- installCommand: z2.string().nullable().optional(),
3013
- usageExample: z2.string().nullable().optional()
3599
+ var quickstartInfoSchema = z3.object({
3600
+ installCommand: z3.string().nullable().optional(),
3601
+ usageExample: z3.string().nullable().optional()
3014
3602
  }).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()
3603
+ var changelogEntrySchema = z3.object({
3604
+ version: z3.string().nullable().optional(),
3605
+ publishedAt: z3.string().nullable().optional(),
3606
+ body: z3.string().nullable().optional()
3019
3607
  });
3020
- var packageSummaryResponseSchema = z2.object({
3608
+ var packageSummaryResponseSchema = z3.object({
3021
3609
  package: packageIdentitySchema.nullable().optional(),
3022
3610
  security: packageSecurityOverviewSchema,
3023
3611
  quickstart: quickstartInfoSchema,
3024
- latestChangelogs: z2.array(changelogEntrySchema).nullable().optional()
3612
+ latestChangelogs: z3.array(changelogEntrySchema).nullable().optional()
3025
3613
  });
3026
- var graphQLErrorSchema2 = z2.object({
3027
- message: z2.string(),
3028
- extensions: z2.record(z2.string(), z2.unknown()).optional()
3614
+ var graphQLErrorSchema2 = z3.object({
3615
+ message: z3.string(),
3616
+ extensions: z3.record(z3.string(), z3.unknown()).optional()
3029
3617
  });
3030
- var graphQLResponseSchema = z2.object({
3031
- data: z2.object({
3618
+ var graphQLResponseSchema = z3.object({
3619
+ data: z3.object({
3032
3620
  packageSummary: packageSummaryResponseSchema.nullable().optional()
3033
3621
  }).nullable().optional(),
3034
- errors: z2.array(graphQLErrorSchema2).optional()
3622
+ errors: z3.array(graphQLErrorSchema2).optional()
3035
3623
  });
3036
3624
  var PACKAGE_SUMMARY_QUERY = `
3037
3625
  query PackageSummary($registry: Registry!, $name: String!) {
@@ -3078,39 +3666,55 @@ query PackageSummary($registry: Registry!, $name: String!) {
3078
3666
  }
3079
3667
  }
3080
3668
  }`;
3081
- var packageVersionIdentitySchema = z2.object({
3082
- name: z2.string().nullable().optional(),
3083
- registry: z2.string().nullable().optional(),
3084
- version: z2.string().nullable().optional()
3669
+ var packageVersionIdentitySchema = z3.object({
3670
+ name: z3.string().nullable().optional(),
3671
+ registry: z3.string().nullable().optional(),
3672
+ version: z3.string().nullable().optional()
3085
3673
  });
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()
3674
+ var vulnerabilityDetailSchema = z3.object({
3675
+ osvId: z3.string().nullable().optional(),
3676
+ summary: z3.string().nullable().optional(),
3677
+ severityScore: z3.number().nullable().optional(),
3678
+ severityType: z3.string().nullable().optional(),
3679
+ affectedVersionRanges: z3.array(z3.string()).nullable().optional(),
3680
+ affectedVersionRangesCount: z3.number().int(),
3681
+ affectedVersionRangesTruncated: z3.boolean(),
3682
+ fixedInVersions: z3.array(z3.string()).nullable().optional(),
3683
+ publishedAt: z3.string().nullable().optional(),
3684
+ modifiedAt: z3.string().nullable().optional(),
3685
+ withdrawnAt: z3.string().nullable().optional(),
3686
+ aliases: z3.array(z3.string()).nullable().optional(),
3687
+ isMalicious: z3.boolean().nullable().optional(),
3688
+ affectsInspectedVersion: z3.boolean(),
3689
+ matchedAffectedVersionRanges: z3.array(z3.string()),
3690
+ duplicateIds: z3.array(z3.string())
3691
+ });
3692
+ var pageInfoSchema = z3.object({
3693
+ hasNextPage: z3.boolean(),
3694
+ endCursor: z3.string().nullable().optional(),
3695
+ totalCount: z3.number().int()
3696
+ });
3697
+ var vulnerabilityAdvisoryPageSchema = z3.object({
3698
+ entries: z3.array(vulnerabilityDetailSchema),
3699
+ pageInfo: pageInfoSchema
3098
3700
  });
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()
3701
+ var vulnerabilitySecurityDetailsSchema = z3.object({
3702
+ affectedVulnerabilityCount: z3.number().int(),
3703
+ nonAffectingVulnerabilityCount: z3.number().int(),
3704
+ allVulnerabilityCount: z3.number().int(),
3705
+ currentVersionAffected: z3.boolean().nullable().optional(),
3706
+ advisories: vulnerabilityAdvisoryPageSchema,
3707
+ upgradePaths: z3.array(z3.string()).nullable().optional()
3104
3708
  }).nullable().optional();
3105
- var vulnerabilityReportResponseSchema = z2.object({
3709
+ var vulnerabilityReportResponseSchema = z3.object({
3106
3710
  package: packageVersionIdentitySchema.nullable().optional(),
3107
3711
  security: vulnerabilitySecurityDetailsSchema
3108
3712
  });
3109
- var vulnerabilitiesGraphQLResponseSchema = z2.object({
3110
- data: z2.object({
3713
+ var vulnerabilitiesGraphQLResponseSchema = z3.object({
3714
+ data: z3.object({
3111
3715
  packageVulnerabilities: vulnerabilityReportResponseSchema.nullable().optional()
3112
3716
  }).nullable().optional(),
3113
- errors: z2.array(graphQLErrorSchema2).optional()
3717
+ errors: z3.array(graphQLErrorSchema2).optional()
3114
3718
  });
3115
3719
  var PACKAGE_VULNERABILITIES_QUERY = `
3116
3720
  query PackageVulnerabilities(
@@ -3119,6 +3723,7 @@ query PackageVulnerabilities(
3119
3723
  $version: String
3120
3724
  $minSeverity: Float
3121
3725
  $includeWithdrawn: Boolean
3726
+ $after: String
3122
3727
  ) {
3123
3728
  packageVulnerabilities(
3124
3729
  registry: $registry
@@ -3133,110 +3738,124 @@ query PackageVulnerabilities(
3133
3738
  version
3134
3739
  }
3135
3740
  security {
3136
- vulnerabilityCount
3741
+ affectedVulnerabilityCount
3742
+ nonAffectingVulnerabilityCount
3743
+ allVulnerabilityCount
3137
3744
  currentVersionAffected
3138
3745
  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
3746
+ advisories(scope: AFFECTED, first: 100, after: $after) {
3747
+ entries {
3748
+ osvId
3749
+ summary
3750
+ severityScore
3751
+ severityType
3752
+ affectedVersionRanges
3753
+ affectedVersionRangesCount
3754
+ affectedVersionRangesTruncated
3755
+ fixedInVersions
3756
+ publishedAt
3757
+ modifiedAt
3758
+ withdrawnAt
3759
+ aliases
3760
+ isMalicious
3761
+ affectsInspectedVersion
3762
+ matchedAffectedVersionRanges
3763
+ duplicateIds
3764
+ }
3765
+ pageInfo {
3766
+ hasNextPage
3767
+ endCursor
3768
+ totalCount
3769
+ }
3151
3770
  }
3152
3771
  }
3153
3772
  }
3154
3773
  }`;
3155
- var directDependencySchema = z2.object({
3156
- name: z2.string().nullable().optional(),
3157
- versionConstraint: z2.string().nullable().optional(),
3158
- type: z2.string().nullable().optional()
3774
+ var directDependencySchema = z3.object({
3775
+ name: z3.string().nullable().optional(),
3776
+ versionConstraint: z3.string().nullable().optional(),
3777
+ type: z3.string().nullable().optional()
3159
3778
  });
3160
- var dependencyGraphNodeSchema = z2.object({
3161
- registry: z2.string(),
3162
- name: z2.string(),
3163
- version: z2.string().nullable().optional()
3779
+ var dependencyGraphNodeSchema = z3.object({
3780
+ registry: z3.string(),
3781
+ name: z3.string(),
3782
+ version: z3.string().nullable().optional()
3164
3783
  });
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()
3784
+ var dependencyGraphEdgeSchema = z3.object({
3785
+ fromIndex: z3.number().int().nullable().optional(),
3786
+ toIndex: z3.number().int(),
3787
+ constraint: z3.string().nullable().optional(),
3788
+ dependencyType: z3.string().nullable().optional()
3170
3789
  });
3171
- var dependencyGraphSchema = z2.object({
3172
- formatVersion: z2.number().int(),
3173
- nodes: z2.array(dependencyGraphNodeSchema),
3174
- edges: z2.array(dependencyGraphEdgeSchema)
3790
+ var dependencyGraphSchema = z3.object({
3791
+ formatVersion: z3.number().int(),
3792
+ nodes: z3.array(dependencyGraphNodeSchema),
3793
+ edges: z3.array(dependencyGraphEdgeSchema)
3175
3794
  });
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()
3795
+ var dependencyConflictEdgeSchema = z3.object({
3796
+ fromIndex: z3.number().int().nullable().optional(),
3797
+ toIndex: z3.number().int(),
3798
+ versionConstraint: z3.string(),
3799
+ dependencyType: z3.string()
3181
3800
  });
3182
- var dependencyConflictSchema = z2.object({
3183
- packageName: z2.string(),
3184
- requiredVersions: z2.array(z2.string()),
3185
- conflictingEdges: z2.array(dependencyConflictEdgeSchema)
3801
+ var dependencyConflictSchema = z3.object({
3802
+ packageName: z3.string(),
3803
+ requiredVersions: z3.array(z3.string()),
3804
+ conflictingEdges: z3.array(dependencyConflictEdgeSchema)
3186
3805
  });
3187
- var circularDependencyCycleSchema = z2.object({
3188
- cycleStart: z2.string(),
3189
- circularPath: z2.array(z2.string()),
3190
- displayChain: z2.string()
3806
+ var circularDependencyCycleSchema = z3.object({
3807
+ cycleStart: z3.string(),
3808
+ circularPath: z3.array(z3.string()),
3809
+ displayChain: z3.string()
3191
3810
  });
3192
- var environmentMarkerSchema = z2.object({
3193
- type: z2.string().nullable().optional(),
3194
- value: z2.string().nullable().optional(),
3195
- raw: z2.string().nullable().optional()
3811
+ var environmentMarkerSchema = z3.object({
3812
+ type: z3.string().nullable().optional(),
3813
+ value: z3.string().nullable().optional(),
3814
+ raw: z3.string().nullable().optional()
3196
3815
  });
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(),
3816
+ var transitiveDependencySchema = z3.object({
3817
+ totalEdges: z3.number().int().nullable().optional(),
3818
+ uniquePackagesCount: z3.number().int().nullable().optional(),
3819
+ uniqueDependencies: z3.array(z3.string()).nullable().optional(),
3820
+ dependencyConflicts: z3.array(dependencyConflictSchema).nullable().optional(),
3821
+ circularDependencyCycles: z3.array(circularDependencyCycleSchema).nullable().optional(),
3203
3822
  dependencyGraph: dependencyGraphSchema.nullable().optional()
3204
3823
  }).nullable().optional();
3205
- var dependencyBundleSchema = z2.object({
3206
- direct: z2.array(directDependencySchema).nullable().optional(),
3824
+ var dependencyBundleSchema = z3.object({
3825
+ direct: z3.array(directDependencySchema).nullable().optional(),
3207
3826
  transitive: transitiveDependencySchema
3208
3827
  }).nullable().optional();
3209
- var groupDependencySchema = z2.object({
3210
- name: z2.string(),
3211
- constraint: z2.string().nullable().optional()
3828
+ var groupDependencySchema = z3.object({
3829
+ name: z3.string(),
3830
+ constraint: z3.string().nullable().optional()
3212
3831
  });
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)
3832
+ var dependencyGroupSchema = z3.object({
3833
+ name: z3.string(),
3834
+ lifecycle: z3.string(),
3835
+ conditionType: z3.string(),
3836
+ conditionValue: z3.string().nullable().optional(),
3837
+ selectionMode: z3.string(),
3838
+ exclusiveGroup: z3.string().nullable().optional(),
3839
+ fallbackPriority: z3.number().int().nullable().optional(),
3840
+ compatibleWith: z3.array(z3.string()).nullable().optional(),
3841
+ defaultEnabled: z3.boolean().nullable().optional(),
3842
+ dependencies: z3.array(groupDependencySchema)
3224
3843
  });
3225
- var dependencyGroupsInfoSchema = z2.object({
3226
- primaryGroup: z2.string().nullable().optional(),
3227
- environmentMarkers: z2.array(environmentMarkerSchema).nullable().optional(),
3228
- groups: z2.array(dependencyGroupSchema)
3844
+ var dependencyGroupsInfoSchema = z3.object({
3845
+ primaryGroup: z3.string().nullable().optional(),
3846
+ environmentMarkers: z3.array(environmentMarkerSchema).nullable().optional(),
3847
+ groups: z3.array(dependencyGroupSchema)
3229
3848
  }).nullable().optional();
3230
- var dependencyReportResponseSchema = z2.object({
3849
+ var dependencyReportResponseSchema = z3.object({
3231
3850
  package: packageVersionIdentitySchema.nullable().optional(),
3232
3851
  dependencies: dependencyBundleSchema,
3233
3852
  dependencyGroups: dependencyGroupsInfoSchema
3234
3853
  });
3235
- var dependenciesGraphQLResponseSchema = z2.object({
3236
- data: z2.object({
3854
+ var dependenciesGraphQLResponseSchema = z3.object({
3855
+ data: z3.object({
3237
3856
  packageDependencies: dependencyReportResponseSchema.nullable().optional()
3238
3857
  }).nullable().optional(),
3239
- errors: z2.array(graphQLErrorSchema2).optional()
3858
+ errors: z3.array(graphQLErrorSchema2).optional()
3240
3859
  });
3241
3860
  var PACKAGE_DEPENDENCIES_QUERY = `
3242
3861
  query PackageDependencies(
@@ -3330,32 +3949,32 @@ query PackageDependencies(
3330
3949
  }
3331
3950
  }
3332
3951
  }`;
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()
3952
+ var changelogPackageInfoSchema = z3.object({
3953
+ name: z3.string().nullable().optional(),
3954
+ registry: z3.string().nullable().optional(),
3955
+ repoUrl: z3.string().nullable().optional(),
3956
+ fromVersion: z3.string().nullable().optional(),
3957
+ toVersion: z3.string().nullable().optional(),
3958
+ limit: z3.number().int().nullable().optional()
3340
3959
  }).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()
3960
+ var changelogEntryDetailSchema = z3.object({
3961
+ version: z3.string().nullable().optional(),
3962
+ normalizedVersion: z3.string().nullable().optional(),
3963
+ body: z3.string().nullable().optional(),
3964
+ htmlUrl: z3.string().nullable().optional(),
3965
+ publishedAt: z3.string().nullable().optional(),
3966
+ metadata: z3.unknown().nullable().optional()
3348
3967
  });
3349
- var changelogReportResponseSchema = z2.object({
3968
+ var changelogReportResponseSchema = z3.object({
3350
3969
  package: changelogPackageInfoSchema,
3351
- source: z2.string().nullable().optional(),
3352
- entries: z2.array(changelogEntryDetailSchema).nullable().optional()
3970
+ source: z3.string().nullable().optional(),
3971
+ entries: z3.array(changelogEntryDetailSchema).nullable().optional()
3353
3972
  });
3354
- var changelogGraphQLResponseSchema = z2.object({
3355
- data: z2.object({
3973
+ var changelogGraphQLResponseSchema = z3.object({
3974
+ data: z3.object({
3356
3975
  packageChangelog: changelogReportResponseSchema.nullable().optional()
3357
3976
  }).nullable().optional(),
3358
- errors: z2.array(graphQLErrorSchema2).optional()
3977
+ errors: z3.array(graphQLErrorSchema2).optional()
3359
3978
  });
3360
3979
  var PACKAGE_CHANGELOG_QUERY = `
3361
3980
  query PackageChangelog(
@@ -3395,72 +4014,72 @@ query PackageChangelog(
3395
4014
  }
3396
4015
  }
3397
4016
  }`;
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(),
4017
+ var packageDocSourceKindSchema = z3.enum(["CRAWLED", "REPOSITORY"]);
4018
+ var packageDocPageSummarySchema = z3.object({
4019
+ id: z3.string().nullable().optional(),
4020
+ title: z3.string().nullable().optional(),
4021
+ slug: z3.string().nullable().optional(),
4022
+ order: z3.number().int().nullable().optional(),
4023
+ linkName: z3.string().nullable().optional(),
4024
+ lastUpdatedAt: z3.string().nullable().optional(),
3406
4025
  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()
4026
+ sourceUrl: z3.string().nullable().optional(),
4027
+ repoUrl: z3.string().nullable().optional(),
4028
+ gitRef: z3.string().nullable().optional(),
4029
+ requestedRef: z3.string().nullable().optional(),
4030
+ filePath: z3.string().nullable().optional()
3412
4031
  });
3413
- var packageDocsPageInfoSchema = z2.object({
3414
- hasNextPage: z2.boolean(),
3415
- endCursor: z2.string().nullable().optional(),
3416
- totalCount: z2.number().int().nullable().optional()
4032
+ var packageDocsPageInfoSchema = z3.object({
4033
+ hasNextPage: z3.boolean(),
4034
+ endCursor: z3.string().nullable().optional(),
4035
+ totalCount: z3.number().int().nullable().optional()
3417
4036
  }).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(),
4037
+ var packageDocsListResponseSchema = z3.object({
4038
+ registry: z3.string().nullable().optional(),
4039
+ packageName: z3.string().nullable().optional(),
4040
+ version: z3.string().nullable().optional(),
4041
+ stale: z3.boolean().nullable().optional(),
4042
+ pages: z3.array(packageDocPageSummarySchema).nullable().optional(),
3424
4043
  pageInfo: packageDocsPageInfoSchema
3425
4044
  });
3426
- var packageDocSourceSchema = z2.object({
3427
- url: z2.string().nullable().optional(),
3428
- label: z2.string().nullable().optional()
4045
+ var packageDocSourceSchema = z3.object({
4046
+ url: z3.string().nullable().optional(),
4047
+ label: z3.string().nullable().optional()
3429
4048
  }).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(),
4049
+ var packageDocPageSchema = z3.object({
4050
+ id: z3.string().nullable().optional(),
4051
+ title: z3.string().nullable().optional(),
4052
+ content: z3.string().nullable().optional(),
4053
+ contentFormat: z3.string().nullable().optional(),
4054
+ breadcrumbs: z3.array(z3.string()).nullable().optional(),
4055
+ linkName: z3.string().nullable().optional(),
4056
+ lastUpdatedAt: z3.string().nullable().optional(),
3438
4057
  sourceKind: packageDocSourceKindSchema.nullable().optional(),
3439
4058
  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()
4059
+ repoUrl: z3.string().nullable().optional(),
4060
+ gitRef: z3.string().nullable().optional(),
4061
+ requestedRef: z3.string().nullable().optional(),
4062
+ filePath: z3.string().nullable().optional(),
4063
+ baseUrl: z3.string().nullable().optional()
3445
4064
  }).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(),
4065
+ var packageDocResultResponseSchema = z3.object({
4066
+ registry: z3.string().nullable().optional(),
4067
+ packageName: z3.string().nullable().optional(),
4068
+ version: z3.string().nullable().optional(),
3450
4069
  sourceKind: packageDocSourceKindSchema.nullable().optional(),
3451
4070
  page: packageDocPageSchema
3452
4071
  });
3453
- var packageDocsListGraphQLResponseSchema = z2.object({
3454
- data: z2.object({
4072
+ var packageDocsListGraphQLResponseSchema = z3.object({
4073
+ data: z3.object({
3455
4074
  listPackageDocs: packageDocsListResponseSchema.nullable().optional()
3456
4075
  }).nullable().optional(),
3457
- errors: z2.array(graphQLErrorSchema2).optional()
4076
+ errors: z3.array(graphQLErrorSchema2).optional()
3458
4077
  });
3459
- var packageDocReadGraphQLResponseSchema = z2.object({
3460
- data: z2.object({
4078
+ var packageDocReadGraphQLResponseSchema = z3.object({
4079
+ data: z3.object({
3461
4080
  getDocPage: packageDocResultResponseSchema.nullable().optional()
3462
4081
  }).nullable().optional(),
3463
- errors: z2.array(graphQLErrorSchema2).optional()
4082
+ errors: z3.array(graphQLErrorSchema2).optional()
3464
4083
  });
3465
4084
  var LIST_PACKAGE_DOCS_QUERY = `
3466
4085
  query ListPackageDocs(
@@ -3602,6 +4221,9 @@ class PackageIntelligenceServiceImpl {
3602
4221
  const extensions = getPrimaryExtensions2(errors);
3603
4222
  const code = typeof extensions?.code === "string" ? extensions.code : undefined;
3604
4223
  const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
4224
+ if (isClientUpdateRequiredGraphQLError({ message, code })) {
4225
+ return new ClientUpdateRequiredError;
4226
+ }
3605
4227
  switch (code) {
3606
4228
  case "NOT_FOUND":
3607
4229
  case "PACKAGE_NOT_FOUND":
@@ -3692,6 +4314,56 @@ class PackageIntelligenceServiceImpl {
3692
4314
  }));
3693
4315
  }
3694
4316
  async executePackageVulnerabilities(token, params) {
4317
+ let after = null;
4318
+ let firstPage;
4319
+ const entries = [];
4320
+ const seenCursors = new Set;
4321
+ do {
4322
+ const page = await this.fetchPackageVulnerabilitiesPage(token, params, after);
4323
+ if (!firstPage)
4324
+ firstPage = page;
4325
+ const advisoryPage = page.security?.advisories;
4326
+ if (!advisoryPage) {
4327
+ after = null;
4328
+ break;
4329
+ }
4330
+ entries.push(...advisoryPage.entries);
4331
+ if (advisoryPage.pageInfo.hasNextPage) {
4332
+ const nextCursor = advisoryPage.pageInfo.endCursor;
4333
+ if (!nextCursor) {
4334
+ throw new MalformedPackageIntelligenceResponseError("Vulnerability response pagination omitted next cursor.");
4335
+ }
4336
+ if (seenCursors.has(nextCursor)) {
4337
+ throw new MalformedPackageIntelligenceResponseError("Vulnerability response pagination repeated a cursor.");
4338
+ }
4339
+ seenCursors.add(nextCursor);
4340
+ after = nextCursor;
4341
+ } else {
4342
+ after = null;
4343
+ }
4344
+ } while (after !== null);
4345
+ if (!firstPage) {
4346
+ throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
4347
+ }
4348
+ if (firstPage.security) {
4349
+ const expectedCount = firstPage.security.advisories.pageInfo.totalCount;
4350
+ if (entries.length !== expectedCount) {
4351
+ throw new MalformedPackageIntelligenceResponseError("Vulnerability response pagination returned an incomplete advisory set.");
4352
+ }
4353
+ }
4354
+ const data = firstPage.security ? {
4355
+ ...firstPage,
4356
+ security: {
4357
+ ...firstPage.security,
4358
+ advisories: {
4359
+ ...firstPage.security.advisories,
4360
+ entries
4361
+ }
4362
+ }
4363
+ } : firstPage;
4364
+ return this.normaliseVulnerabilityReport(data);
4365
+ }
4366
+ async fetchPackageVulnerabilitiesPage(token, params, after) {
3695
4367
  let response;
3696
4368
  try {
3697
4369
  response = await postPkgseerGraphql({
@@ -3703,7 +4375,8 @@ class PackageIntelligenceServiceImpl {
3703
4375
  name: params.packageName,
3704
4376
  version: params.version,
3705
4377
  minSeverity: params.minSeverity,
3706
- includeWithdrawn: params.includeWithdrawn
4378
+ includeWithdrawn: params.includeWithdrawn,
4379
+ after
3707
4380
  },
3708
4381
  fetchFn: this.fetchFn
3709
4382
  });
@@ -3727,7 +4400,7 @@ class PackageIntelligenceServiceImpl {
3727
4400
  if (!data) {
3728
4401
  throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
3729
4402
  }
3730
- return this.normaliseVulnerabilityReport(data);
4403
+ return data;
3731
4404
  }
3732
4405
  normaliseVulnerabilityReport(data) {
3733
4406
  const name = data.package?.name ?? undefined;
@@ -3741,21 +4414,28 @@ class PackageIntelligenceServiceImpl {
3741
4414
  registry: data.package?.registry ?? undefined
3742
4415
  };
3743
4416
  const security = data.security ? {
3744
- vulnerabilityCount: data.security.vulnerabilityCount ?? undefined,
4417
+ affectedVulnerabilityCount: data.security.affectedVulnerabilityCount,
4418
+ nonAffectingVulnerabilityCount: data.security.nonAffectingVulnerabilityCount,
4419
+ allVulnerabilityCount: data.security.allVulnerabilityCount,
3745
4420
  currentVersionAffected: data.security.currentVersionAffected ?? undefined,
3746
- vulnerabilities: data.security.vulnerabilities?.map((vuln) => ({
4421
+ vulnerabilities: data.security.advisories.entries.map((vuln) => ({
3747
4422
  osvId: vuln.osvId ?? undefined,
3748
4423
  summary: vuln.summary ?? undefined,
3749
4424
  severityScore: vuln.severityScore ?? undefined,
3750
4425
  severityType: vuln.severityType ?? undefined,
3751
4426
  affectedVersionRanges: vuln.affectedVersionRanges ?? undefined,
4427
+ affectedVersionRangesCount: vuln.affectedVersionRangesCount,
4428
+ affectedVersionRangesTruncated: vuln.affectedVersionRangesTruncated,
3752
4429
  fixedInVersions: vuln.fixedInVersions ?? undefined,
3753
4430
  publishedAt: vuln.publishedAt ?? undefined,
3754
4431
  modifiedAt: vuln.modifiedAt ?? undefined,
3755
4432
  withdrawnAt: vuln.withdrawnAt ?? undefined,
3756
4433
  aliases: vuln.aliases ?? undefined,
3757
- isMalicious: vuln.isMalicious ?? undefined
3758
- })) ?? undefined,
4434
+ isMalicious: vuln.isMalicious ?? undefined,
4435
+ affectsInspectedVersion: vuln.affectsInspectedVersion,
4436
+ matchedAffectedVersionRanges: vuln.matchedAffectedVersionRanges,
4437
+ duplicateIds: vuln.duplicateIds
4438
+ })),
3759
4439
  upgradePaths: data.security.upgradePaths ?? undefined
3760
4440
  } : undefined;
3761
4441
  return {
@@ -3946,12 +4626,12 @@ class PackageIntelligenceServiceImpl {
3946
4626
  return this.normaliseChangelogReport(data, params);
3947
4627
  }
3948
4628
  normaliseChangelogReport(data, params) {
3949
- const source = data.source ?? undefined;
3950
- if (!source) {
4629
+ const source = data.source && data.source.trim() ? data.source : undefined;
4630
+ const rawEntries = data.entries ?? [];
4631
+ if (!source && rawEntries.length === 0) {
3951
4632
  const target = params.repoUrl ?? (params.registry && params.packageName ? `${params.registry.toLowerCase()}:${params.packageName}` : "package");
3952
4633
  throw new PackageIntelligenceChangelogSourceNotFoundError(`No changelog source available for ${target} (tried GitHub Releases, CHANGELOG.md, and HexDocs).`);
3953
4634
  }
3954
- const rawEntries = data.entries ?? [];
3955
4635
  const entries = rawEntries.map((entry) => ({
3956
4636
  version: entry.version ?? undefined,
3957
4637
  normalizedVersion: entry.normalizedVersion ?? undefined,
@@ -4292,37 +4972,428 @@ class TokenManager {
4292
4972
  refreshToken: tokens.refreshToken
4293
4973
  }));
4294
4974
  } catch {
4975
+ const reloadedToken = await this.loadExternallyUpdatedToken(tokens);
4976
+ if (reloadedToken)
4977
+ return reloadedToken.accessToken;
4295
4978
  const isExpired = tokens.expiresAt ? new Date >= new Date(tokens.expiresAt) : false;
4296
4979
  if (isExpired) {
4980
+ const currentStoredTokens = await this.loadExternallyUpdatedToken(tokens);
4981
+ if (currentStoredTokens)
4982
+ return currentStoredTokens.accessToken;
4983
+ const cleared = await withTelemetrySpan("token-manager.clear-tokens-if-unchanged", () => this.authStorage.clearTokensIfUnchanged(this.mcpUrl, tokens));
4984
+ if (!cleared) {
4985
+ const currentToken = await this.authStorage.loadTokens(this.mcpUrl);
4986
+ this.cachedToken = currentToken;
4987
+ return currentToken?.accessToken;
4988
+ }
4297
4989
  this.cachedToken = null;
4298
- await withTelemetrySpan("token-manager.clear-tokens", () => this.authStorage.clearTokens(this.mcpUrl));
4299
4990
  }
4300
4991
  return;
4301
4992
  }
4302
4993
  const newTokenData = {
4303
4994
  accessToken: response.accessToken,
4304
- refreshToken: response.refreshToken,
4995
+ refreshToken: response.refreshToken ?? tokens.refreshToken,
4305
4996
  expiresAt: new Date(Date.now() + response.expiresIn * 1000).toISOString(),
4306
4997
  createdAt: new Date().toISOString()
4307
4998
  };
4308
- await withTelemetrySpan("token-manager.save-tokens", () => this.authStorage.saveTokens(this.mcpUrl, newTokenData));
4999
+ const externallyUpdatedToken = await this.loadExternallyUpdatedToken(tokens, {
5000
+ treatMissingAsExternalUpdate: true
5001
+ });
5002
+ if (externallyUpdatedToken === null)
5003
+ return;
5004
+ if (externallyUpdatedToken)
5005
+ return externallyUpdatedToken.accessToken;
5006
+ const saved = await withTelemetrySpan("token-manager.save-tokens", () => this.authStorage.saveTokensIfUnchanged(this.mcpUrl, tokens, newTokenData));
5007
+ if (!saved) {
5008
+ const currentToken = await this.authStorage.loadTokens(this.mcpUrl);
5009
+ this.cachedToken = currentToken;
5010
+ return currentToken?.accessToken;
5011
+ }
4309
5012
  this.cachedToken = newTokenData;
4310
5013
  return response.accessToken;
4311
5014
  });
4312
5015
  }
5016
+ async loadExternallyUpdatedToken(failedTokens, options = {}) {
5017
+ const storedTokens = await withTelemetrySpan("token-manager.reload-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
5018
+ if (!storedTokens) {
5019
+ if (options.treatMissingAsExternalUpdate)
5020
+ this.cachedToken = null;
5021
+ return options.treatMissingAsExternalUpdate ? null : undefined;
5022
+ }
5023
+ if (areSameTokenData(storedTokens, failedTokens))
5024
+ return;
5025
+ this.cachedToken = storedTokens;
5026
+ return storedTokens;
5027
+ }
4313
5028
  }
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.");
5029
+ function areSameTokenData(a, b) {
5030
+ return a.accessToken === b.accessToken && a.refreshToken === b.refreshToken && a.expiresAt === b.expiresAt && a.createdAt === b.createdAt;
5031
+ }
5032
+ // src/services/update-check-service.ts
5033
+ import semver from "semver";
5034
+ var NPM_DIST_TAGS_URL = "https://registry.npmjs.org/-/package/githits/dist-tags";
5035
+ var NPM_PACKAGE_VERSION_URL = "https://registry.npmjs.org/githits";
5036
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
5037
+ var FETCH_TIMEOUT_MS = 1000;
5038
+ var DIR_MODE2 = 448;
5039
+ var UPDATE_COMMAND = "npm i -g githits@latest";
5040
+ var MAX_DEPRECATION_REASON_LENGTH = 200;
5041
+
5042
+ class NpmRegistryUpdateCheckService {
5043
+ currentVersion;
5044
+ fs;
5045
+ fetcher;
5046
+ env;
5047
+ now;
5048
+ checkIntervalMs;
5049
+ fetchTimeoutMs;
5050
+ configDir;
5051
+ cachePath;
5052
+ constructor(options) {
5053
+ this.currentVersion = options.currentVersion;
5054
+ this.fs = options.fileSystemService;
5055
+ this.fetcher = options.fetcher ?? fetch;
5056
+ this.env = options.env ?? process.env;
5057
+ this.now = options.now ?? (() => new Date);
5058
+ this.checkIntervalMs = options.checkIntervalMs ?? CHECK_INTERVAL_MS;
5059
+ this.fetchTimeoutMs = options.fetchTimeoutMs ?? FETCH_TIMEOUT_MS;
5060
+ this.configDir = this.fs.joinPath(resolveConfigHome(this.env, this.fs), "githits");
5061
+ this.cachePath = this.fs.joinPath(this.configDir, "update-check.json");
5062
+ }
5063
+ async checkForUpdate(signal) {
5064
+ const cache = await this.loadCache();
5065
+ const latestDue = !cache || this.isCheckDue(cache);
5066
+ const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
5067
+ const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
5068
+ const currentVersionStatusChanged = currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus);
5069
+ if (cache && !latestDue) {
5070
+ if (currentVersionStatusChanged) {
5071
+ await this.saveCache({
5072
+ ...cache,
5073
+ currentVersionStatus
5074
+ });
5075
+ }
5076
+ return this.noticeFromLatest(cache.latestVersion);
5077
+ }
5078
+ const latestVersion = await this.fetchLatestVersion(signal);
5079
+ if (!latestVersion) {
5080
+ if (currentVersionStatusChanged) {
5081
+ await this.saveCache({
5082
+ ...cache,
5083
+ currentVersionStatus
5084
+ });
5085
+ }
5086
+ return this.noticeFromLatest(cache?.latestVersion);
5087
+ }
5088
+ await this.saveCache({
5089
+ checkedAt: this.now().toISOString(),
5090
+ latestVersion,
5091
+ currentVersionStatus
4325
5092
  });
5093
+ return this.noticeFromLatest(latestVersion);
5094
+ }
5095
+ async refreshRequiredUpdateStatus(signal) {
5096
+ const cache = await this.loadCache();
5097
+ const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
5098
+ const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
5099
+ if (currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus)) {
5100
+ await this.saveCache({
5101
+ ...cache,
5102
+ currentVersionStatus
5103
+ });
5104
+ }
5105
+ }
5106
+ async getRequiredUpdateNotice() {
5107
+ const cache = await this.loadCache();
5108
+ const status = cache?.currentVersionStatus;
5109
+ if (!status || status.version !== this.currentVersion || !status.deprecatedReason) {
5110
+ return;
5111
+ }
5112
+ return {
5113
+ currentVersion: this.currentVersion,
5114
+ ...cache.latestVersion ? { latestKnownVersion: cache.latestVersion } : {},
5115
+ reason: status.deprecatedReason,
5116
+ updateCommand: formatUpdateCommand(this.env)
5117
+ };
5118
+ }
5119
+ noticeFromLatest(latestVersion) {
5120
+ if (!latestVersion || !semver.valid(latestVersion) || !semver.valid(this.currentVersion) || !semver.gt(latestVersion, this.currentVersion)) {
5121
+ return;
5122
+ }
5123
+ return {
5124
+ currentVersion: this.currentVersion,
5125
+ latestVersion,
5126
+ updateCommand: UPDATE_COMMAND
5127
+ };
5128
+ }
5129
+ isCheckDue(cache) {
5130
+ if (!cache.checkedAt || !cache.latestVersion) {
5131
+ return true;
5132
+ }
5133
+ const checkedAtMs = Date.parse(cache.checkedAt);
5134
+ if (Number.isNaN(checkedAtMs)) {
5135
+ return true;
5136
+ }
5137
+ return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
5138
+ }
5139
+ isCurrentVersionStatusDue(status) {
5140
+ if (!status || status.version !== this.currentVersion) {
5141
+ return true;
5142
+ }
5143
+ const checkedAtMs = Date.parse(status.checkedAt);
5144
+ if (Number.isNaN(checkedAtMs)) {
5145
+ return true;
5146
+ }
5147
+ return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
5148
+ }
5149
+ async refreshCurrentVersionStatusIfDue(cached, signal) {
5150
+ const reusableCached = cached?.version === this.currentVersion ? cached : undefined;
5151
+ if (!this.isCurrentVersionStatusDue(reusableCached)) {
5152
+ return reusableCached;
5153
+ }
5154
+ const remote = await this.fetchCurrentVersionStatus(signal);
5155
+ if (!remote) {
5156
+ return reusableCached;
5157
+ }
5158
+ return remote;
5159
+ }
5160
+ async fetchLatestVersion(signal) {
5161
+ try {
5162
+ const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
5163
+ const response = await this.fetcher(NPM_DIST_TAGS_URL, {
5164
+ signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
5165
+ });
5166
+ if (!response.ok) {
5167
+ return;
5168
+ }
5169
+ const body = await response.json();
5170
+ if (!body || typeof body !== "object" || typeof body.latest !== "string") {
5171
+ return;
5172
+ }
5173
+ const latest = body.latest;
5174
+ return semver.valid(latest) ? latest : undefined;
5175
+ } catch {
5176
+ return;
5177
+ }
5178
+ }
5179
+ async fetchCurrentVersionStatus(signal) {
5180
+ try {
5181
+ const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
5182
+ const response = await this.fetcher(`${NPM_PACKAGE_VERSION_URL}/${this.currentVersion}`, {
5183
+ signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
5184
+ });
5185
+ if (!response.ok) {
5186
+ return;
5187
+ }
5188
+ const body = await response.json();
5189
+ if (!body || typeof body !== "object") {
5190
+ return;
5191
+ }
5192
+ const rawDeprecated = body.deprecated;
5193
+ const deprecatedReason = typeof rawDeprecated === "string" ? sanitizeDeprecationReason(rawDeprecated) : undefined;
5194
+ return {
5195
+ version: this.currentVersion,
5196
+ checkedAt: this.now().toISOString(),
5197
+ ...deprecatedReason ? { deprecatedReason } : {}
5198
+ };
5199
+ } catch {
5200
+ return;
5201
+ }
5202
+ }
5203
+ async loadCache() {
5204
+ try {
5205
+ if (!await this.fs.exists(this.cachePath)) {
5206
+ return;
5207
+ }
5208
+ const raw = await this.fs.readFile(this.cachePath);
5209
+ const parsed = JSON.parse(raw);
5210
+ const currentVersionStatus = parseCurrentVersionStatus(parsed.currentVersionStatus);
5211
+ const hasLatest = typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string";
5212
+ if (!hasLatest && !currentVersionStatus) {
5213
+ return;
5214
+ }
5215
+ return {
5216
+ ...hasLatest ? {
5217
+ checkedAt: parsed.checkedAt,
5218
+ latestVersion: parsed.latestVersion
5219
+ } : {},
5220
+ currentVersionStatus
5221
+ };
5222
+ } catch {
5223
+ return;
5224
+ }
5225
+ }
5226
+ async saveCache(cache) {
5227
+ try {
5228
+ await this.fs.ensureDir(this.configDir, DIR_MODE2);
5229
+ if (typeof this.fs.atomicWriteFile === "function") {
5230
+ await this.fs.atomicWriteFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
5231
+ `);
5232
+ return;
5233
+ }
5234
+ await this.fs.writeFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
5235
+ `, 384);
5236
+ } catch {}
5237
+ }
5238
+ }
5239
+ function resolveConfigHome(env, fs) {
5240
+ const xdgConfigHome = env.XDG_CONFIG_HOME?.trim();
5241
+ if (xdgConfigHome) {
5242
+ return xdgConfigHome;
5243
+ }
5244
+ return fs.joinPath(fs.getHomeDir(), ".config");
5245
+ }
5246
+ function shouldRunUpdateCheck(input) {
5247
+ if (input.stderrIsTTY !== true) {
5248
+ return false;
5249
+ }
5250
+ const env = input.env ?? process.env;
5251
+ if (env.CI || env.GITHITS_DISABLE_UPDATE_CHECK) {
5252
+ return false;
5253
+ }
5254
+ if (isHelpOrVersionInvocation(input.args)) {
5255
+ return false;
5256
+ }
5257
+ if (isLikelyEphemeralPackageRunner(env)) {
5258
+ return false;
5259
+ }
5260
+ if (isMcpStdioInvocation(input.args, input.stdinIsTTY === true, input.stdoutIsTTY === true)) {
5261
+ return false;
5262
+ }
5263
+ return true;
5264
+ }
5265
+ function shouldRunRequiredUpdateEnforcement(input) {
5266
+ if (isHelpOrVersionInvocation(input.args)) {
5267
+ return false;
5268
+ }
5269
+ const env = input.env ?? process.env;
5270
+ if (isLikelyEphemeralPackageRunner(env)) {
5271
+ return false;
5272
+ }
5273
+ return true;
5274
+ }
5275
+ function formatUpdateNotice(notice) {
5276
+ return `Update available: githits ${notice.currentVersion} -> ${notice.latestVersion}
5277
+ Run: ${notice.updateCommand}`;
5278
+ }
5279
+ function formatRequiredUpdateNotice(notice) {
5280
+ const lines = [
5281
+ `Update required: ${notice.reason}`,
5282
+ "",
5283
+ `Installed githits ${notice.currentVersion} is no longer supported.`
5284
+ ];
5285
+ if (notice.latestKnownVersion) {
5286
+ lines.push(`Latest known version: ${notice.latestKnownVersion}`);
5287
+ }
5288
+ lines.push("Update with:", ` ${notice.updateCommand}`);
5289
+ return [...lines].join(`
5290
+ `);
5291
+ }
5292
+ function formatUpdateCommand(env = process.env) {
5293
+ const userAgent = env.npm_config_user_agent ?? "";
5294
+ const execPath = env.npm_execpath ?? "";
5295
+ const signal = `${userAgent} ${execPath}`.toLowerCase();
5296
+ if (signal.includes("pnpm")) {
5297
+ return "pnpm add -g githits@latest";
5298
+ }
5299
+ if (signal.includes("yarn")) {
5300
+ return "yarn global add githits@latest";
5301
+ }
5302
+ if (signal.includes("bun")) {
5303
+ return "bun add -g githits@latest";
5304
+ }
5305
+ return UPDATE_COMMAND;
5306
+ }
5307
+ function parseCurrentVersionStatus(value) {
5308
+ if (!value || typeof value !== "object") {
5309
+ return;
5310
+ }
5311
+ const status = value;
5312
+ if (typeof status.version !== "string") {
5313
+ return;
5314
+ }
5315
+ if (typeof status.checkedAt !== "string") {
5316
+ return;
5317
+ }
5318
+ const deprecatedReason = typeof status.deprecatedReason === "string" ? sanitizeDeprecationReason(status.deprecatedReason) : undefined;
5319
+ return {
5320
+ version: status.version,
5321
+ checkedAt: status.checkedAt,
5322
+ ...deprecatedReason ? { deprecatedReason } : {}
5323
+ };
5324
+ }
5325
+ function sameCurrentVersionStatus(left, right) {
5326
+ return left?.version === right?.version && left?.checkedAt === right?.checkedAt && left?.deprecatedReason === right?.deprecatedReason;
5327
+ }
5328
+ function sanitizeDeprecationReason(value) {
5329
+ const sanitized = Array.from(value).map((character) => isControlCharacter(character) ? " " : character).join("").replace(/\s+/g, " ").trim().slice(0, MAX_DEPRECATION_REASON_LENGTH).trim();
5330
+ return sanitized.length > 0 ? sanitized : undefined;
5331
+ }
5332
+ function isControlCharacter(value) {
5333
+ const code = value.charCodeAt(0);
5334
+ return code <= 31 || code >= 127 && code <= 159;
5335
+ }
5336
+ function isHelpOrVersionInvocation(args) {
5337
+ return args.length === 0 || args[0] === "help" || args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-V");
5338
+ }
5339
+ function isLikelyEphemeralPackageRunner(env) {
5340
+ const lifecycleEvent = env.npm_lifecycle_event;
5341
+ if (lifecycleEvent === "npx" || lifecycleEvent === "bunx") {
5342
+ return true;
5343
+ }
5344
+ const userAgent = env.npm_config_user_agent ?? "";
5345
+ return env.npm_command === "exec" && (userAgent.startsWith("npm/") || userAgent.startsWith("bun/"));
5346
+ }
5347
+ function isMcpStdioInvocation(args, stdinIsTTY, stdoutIsTTY) {
5348
+ const firstTokenIndex = args.findIndex((arg) => !arg.startsWith("-"));
5349
+ if (firstTokenIndex === -1 || args[firstTokenIndex] !== "mcp") {
5350
+ return false;
5351
+ }
5352
+ const remainingArgs = args.slice(firstTokenIndex + 1);
5353
+ return remainingArgs.includes("start") || !stdinIsTTY || !stdoutIsTTY;
5354
+ }
5355
+ // src/container.ts
5356
+ async function createAuthStorage(fileSystemService) {
5357
+ return withTelemetrySpan("container.create-auth-storage", async () => {
5358
+ const authConfig = await loadAuthConfig(fileSystemService);
5359
+ return createAuthStorageForMode(fileSystemService, authConfig.storage, authConfig.configPath);
5360
+ });
5361
+ }
5362
+ function createAuthStorageForMode(fileSystemService, mode, configPath = "your GitHits config.toml") {
5363
+ const fileStorage = new ModeAwareFileAuthStorage(new AuthStorageImpl(fileSystemService, getAuthFileStorageDir(fileSystemService)), mode, configPath);
5364
+ const legacyStorage = new AuthStorageImpl(fileSystemService, getLegacyAuthStorageDir(fileSystemService));
5365
+ const rawKeyring = new KeyringServiceImpl;
5366
+ const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
5367
+ const keychainStorage = new KeychainAuthStorage(keyring);
5368
+ return new LockedAuthStorage(new MigratingAuthStorage(keychainStorage, fileStorage, legacyStorage, mode, configPath, (message) => console.error(message)), fileSystemService);
5369
+ }
5370
+ async function createAuthCommandDependencies() {
5371
+ return withTelemetrySpan("container.create-auth-command", async () => {
5372
+ const fileSystemService = new FileSystemServiceImpl;
5373
+ return {
5374
+ authStorage: await createAuthStorage(fileSystemService),
5375
+ authService: new AuthServiceImpl,
5376
+ browserService: new BrowserServiceImpl,
5377
+ fileSystemService,
5378
+ mcpUrl: getMcpUrl(),
5379
+ apiUrl: getApiUrl(),
5380
+ envApiToken: getEnvApiToken()
5381
+ };
5382
+ });
5383
+ }
5384
+ async function createAuthStatusDependencies() {
5385
+ return withTelemetrySpan("container.create-auth-status", async () => {
5386
+ const fileSystemService = new FileSystemServiceImpl;
5387
+ const envApiToken = getEnvApiToken();
5388
+ return {
5389
+ authStorage: envApiToken ? createAuthStorageForMode(fileSystemService, "keychain") : await createAuthStorage(fileSystemService),
5390
+ authService: new AuthServiceImpl,
5391
+ browserService: new BrowserServiceImpl,
5392
+ fileSystemService,
5393
+ mcpUrl: getMcpUrl(),
5394
+ apiUrl: getApiUrl(),
5395
+ envApiToken
5396
+ };
4326
5397
  });
4327
5398
  }
4328
5399
  function createStaticTokenProvider(token) {
@@ -4338,18 +5409,17 @@ async function createContainer() {
4338
5409
  const mcpUrl = getMcpUrl();
4339
5410
  const apiUrl = getApiUrl();
4340
5411
  const codeNavigationUrl = getCodeNavigationUrl();
4341
- const codeNavigationCliOverrideEnabled = isCodeNavigationCliOverrideEnabled();
4342
5412
  const fileSystemService = new FileSystemServiceImpl;
4343
- const authStorage = createAuthStorage(fileSystemService);
4344
5413
  const authService = new AuthServiceImpl;
4345
5414
  const browserService = new BrowserServiceImpl;
4346
5415
  const envToken = getEnvApiToken();
4347
5416
  if (envToken) {
5417
+ const authStorage2 = createAuthStorageForMode(fileSystemService, "keychain");
4348
5418
  const tokenProvider = createStaticTokenProvider(envToken);
4349
- const codeNavigationService2 = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
4350
- const packageIntelligenceService2 = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
5419
+ const codeNavigationService2 = new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider);
5420
+ const packageIntelligenceService2 = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider);
4351
5421
  return {
4352
- authStorage,
5422
+ authStorage: authStorage2,
4353
5423
  authService,
4354
5424
  browserService,
4355
5425
  fileSystemService,
@@ -4358,18 +5428,17 @@ async function createContainer() {
4358
5428
  apiToken: envToken,
4359
5429
  hasValidToken: true,
4360
5430
  envApiToken: envToken,
4361
- codeNavigationCapability: getCodeNavigationCapability(envToken),
4362
- codeNavigationCliOverrideEnabled,
4363
5431
  codeNavigationUrl,
4364
5432
  codeNavigationService: codeNavigationService2,
4365
5433
  packageIntelligenceService: packageIntelligenceService2,
4366
5434
  githitsService: new GitHitsServiceImpl(apiUrl, envToken)
4367
5435
  };
4368
5436
  }
5437
+ const authStorage = await createAuthStorage(fileSystemService);
4369
5438
  const tokenManager = new TokenManager({ authService, authStorage, mcpUrl });
4370
5439
  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;
5440
+ const codeNavigationService = new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager);
5441
+ const packageIntelligenceService = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenManager);
4373
5442
  return {
4374
5443
  authStorage,
4375
5444
  authService,
@@ -4380,8 +5449,6 @@ async function createContainer() {
4380
5449
  apiToken,
4381
5450
  hasValidToken: apiToken !== undefined,
4382
5451
  envApiToken: undefined,
4383
- codeNavigationCapability: getCodeNavigationCapability(apiToken),
4384
- codeNavigationCliOverrideEnabled,
4385
5452
  codeNavigationUrl,
4386
5453
  codeNavigationService,
4387
5454
  packageIntelligenceService,
@@ -4389,48 +5456,5 @@ async function createContainer() {
4389
5456
  };
4390
5457
  });
4391
5458
  }
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
5459
 
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 };
5460
+ export { AuthConfigError, CLIENT_UPDATE_REQUIRED_REASON, ClientUpdateRequiredError, debugLog, setMcpClientVersionProvider, setClientMode, isTelemetryEnabled, withTelemetrySpan, startTelemetrySpan, endTelemetrySpan, flushTelemetry, AuthenticationError, CodeNavigationAccessError, CodeNavigationGraphQLError, CodeNavigationIndexingError, CodeNavigationUnresolvableError, MalformedCodeNavigationResponseError, CodeNavigationTargetNotFoundError, CodeNavigationFileNotFoundError, CodeNavigationVersionNotFoundError, CodeNavigationValidationError, CodeNavigationFeatureFlagRequiredError, CodeNavigationNetworkError, CodeNavigationBackendError, getCodeNavigationUrl, ExecServiceImpl, FileSystemServiceImpl, AuthStorageLockTimeoutError, AuthStoragePolicyError, PackageIntelligenceAccessError, PackageIntelligenceFeatureFlagRequiredError, PackageIntelligenceNetworkError, PackageIntelligenceBackendError, PackageIntelligenceGraphQLError, PackageIntelligenceTargetNotFoundError, PackageIntelligenceValidationError, PackageIntelligenceVersionNotFoundError, MalformedPackageIntelligenceResponseError, PackageIntelligenceChangelogSourceNotFoundError, PromptServiceImpl, refreshExpiredToken, NpmRegistryUpdateCheckService, shouldRunUpdateCheck, shouldRunRequiredUpdateEnforcement, formatUpdateNotice, formatRequiredUpdateNotice, createAuthCommandDependencies, createAuthStatusDependencies, createContainer };