githits 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,93 @@
1
1
  import {
2
2
  version
3
- } from "./chunk-g6ay6x9v.js";
3
+ } from "./chunk-gxya097b.js";
4
4
 
5
+ // src/services/app-config-paths.ts
6
+ var APP_DIR = "githits";
7
+ function getAppConfigDir(fs) {
8
+ const home = fs.getHomeDir();
9
+ switch (process.platform) {
10
+ case "win32":
11
+ return fs.joinPath(process.env.APPDATA ?? fs.joinPath(home, "AppData", "Roaming"), APP_DIR);
12
+ case "darwin":
13
+ return fs.joinPath(home, "Library", "Application Support", APP_DIR);
14
+ default:
15
+ return fs.joinPath(process.env.XDG_CONFIG_HOME ?? fs.joinPath(home, ".config"), APP_DIR);
16
+ }
17
+ }
18
+ function getAuthConfigPath(fs) {
19
+ return fs.joinPath(getAppConfigDir(fs), "config.toml");
20
+ }
21
+ function getAuthFileStorageDir(fs) {
22
+ return fs.joinPath(getAppConfigDir(fs), "auth");
23
+ }
24
+ function getLegacyAuthStorageDir(fs) {
25
+ return fs.joinPath(fs.getHomeDir(), ".githits");
26
+ }
27
+ // src/services/auth-config.ts
28
+ import { parse as parseToml } from "smol-toml";
29
+ import { z } from "zod";
30
+ var AUTH_STORAGE_MODES = ["keychain", "file"];
31
+ var AUTH_STORAGE_MODE_VALUES = new Set(AUTH_STORAGE_MODES);
32
+ var CONFIG_SCHEMA = z.object({
33
+ auth: z.object({
34
+ storage: z.string().optional()
35
+ }).optional()
36
+ }).passthrough();
37
+
38
+ class AuthConfigError extends Error {
39
+ constructor(message) {
40
+ super(message);
41
+ this.name = "AuthConfigError";
42
+ }
43
+ }
44
+ function parseAuthStorageMode(value) {
45
+ const normalized = value.trim().toLowerCase();
46
+ if (AUTH_STORAGE_MODE_VALUES.has(normalized)) {
47
+ return normalized;
48
+ }
49
+ throw new AuthConfigError(`Invalid auth storage mode "${value}". Use "keychain" or "file". File mode stores OAuth credentials unencrypted on disk.`);
50
+ }
51
+ async function loadAuthConfig(fs) {
52
+ const configPath = getAuthConfigPath(fs);
53
+ const envMode = process.env.GITHITS_AUTH_STORAGE;
54
+ if (envMode !== undefined && envMode.trim() !== "") {
55
+ try {
56
+ return { storage: parseAuthStorageMode(envMode), configPath };
57
+ } catch (error) {
58
+ if (error instanceof AuthConfigError) {
59
+ throw new AuthConfigError(`Invalid GITHITS_AUTH_STORAGE: ${error.message}`);
60
+ }
61
+ throw error;
62
+ }
63
+ }
64
+ if (!await fs.exists(configPath)) {
65
+ return { storage: "keychain", configPath };
66
+ }
67
+ let rawConfig;
68
+ try {
69
+ rawConfig = parseToml(await fs.readFile(configPath));
70
+ } catch (error) {
71
+ const message = error instanceof Error ? error.message : String(error);
72
+ throw new AuthConfigError(`Cannot parse GitHits config at ${configPath}: ${message}`);
73
+ }
74
+ const parsed = CONFIG_SCHEMA.safeParse(rawConfig);
75
+ if (!parsed.success) {
76
+ throw new AuthConfigError(`Invalid GitHits config at ${configPath}: ${z.prettifyError(parsed.error)}`);
77
+ }
78
+ const configuredMode = parsed.data.auth?.storage;
79
+ if (configuredMode === undefined || configuredMode.trim() === "") {
80
+ return { storage: "keychain", configPath };
81
+ }
82
+ try {
83
+ return { storage: parseAuthStorageMode(configuredMode), configPath };
84
+ } catch (error) {
85
+ if (error instanceof AuthConfigError) {
86
+ throw new AuthConfigError(`Invalid GitHits config at ${configPath}: ${error.message}`);
87
+ }
88
+ throw error;
89
+ }
90
+ }
5
91
  // src/services/auth-service.ts
6
92
  import { createServer } from "node:http";
7
93
 
@@ -310,11 +396,9 @@ function escapeHtml(text) {
310
396
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
311
397
  }
312
398
  // src/services/auth-storage.ts
313
- var CONFIG_DIR = ".githits";
314
399
  var AUTH_FILE = "auth.json";
315
400
  var CLIENT_FILE = "client.json";
316
401
  var DIR_MODE = 448;
317
- var FILE_MODE = 384;
318
402
 
319
403
  class AuthStorageImpl {
320
404
  fs;
@@ -323,7 +407,7 @@ class AuthStorageImpl {
323
407
  clientPath;
324
408
  constructor(fs, configDir) {
325
409
  this.fs = fs;
326
- this.configDir = configDir ?? fs.joinPath(fs.getHomeDir(), CONFIG_DIR);
410
+ this.configDir = configDir ?? getAuthFileStorageDir(fs);
327
411
  this.authPath = fs.joinPath(this.configDir, AUTH_FILE);
328
412
  this.clientPath = fs.joinPath(this.configDir, CLIENT_FILE);
329
413
  }
@@ -343,7 +427,7 @@ class AuthStorageImpl {
343
427
  };
344
428
  stored.tokens[normalizeBaseUrl(baseUrl)] = data;
345
429
  await this.fs.ensureDir(this.configDir, DIR_MODE);
346
- await this.fs.writeFile(this.authPath, JSON.stringify(stored, null, 2), FILE_MODE);
430
+ await this.fs.atomicWriteFile(this.authPath, JSON.stringify(stored, null, 2));
347
431
  }
348
432
  async clearTokens(baseUrl) {
349
433
  const stored = await this.loadAuthFile();
@@ -353,7 +437,7 @@ class AuthStorageImpl {
353
437
  if (Object.keys(stored.tokens).length === 0) {
354
438
  await this.fs.deleteFile(this.authPath);
355
439
  } else {
356
- await this.fs.writeFile(this.authPath, JSON.stringify(stored, null, 2), FILE_MODE);
440
+ await this.fs.atomicWriteFile(this.authPath, JSON.stringify(stored, null, 2));
357
441
  }
358
442
  }
359
443
  async loadClient(baseUrl) {
@@ -370,7 +454,7 @@ class AuthStorageImpl {
370
454
  if (Object.keys(stored.clients).length === 0) {
371
455
  await this.fs.deleteFile(this.clientPath);
372
456
  } else {
373
- await this.fs.writeFile(this.clientPath, JSON.stringify(stored, null, 2), FILE_MODE);
457
+ await this.fs.atomicWriteFile(this.clientPath, JSON.stringify(stored, null, 2));
374
458
  }
375
459
  }
376
460
  async saveClient(baseUrl, data) {
@@ -380,7 +464,7 @@ class AuthStorageImpl {
380
464
  };
381
465
  stored.clients[normalizeBaseUrl(baseUrl)] = data;
382
466
  await this.fs.ensureDir(this.configDir, DIR_MODE);
383
- await this.fs.writeFile(this.clientPath, JSON.stringify(stored, null, 2), FILE_MODE);
467
+ await this.fs.atomicWriteFile(this.clientPath, JSON.stringify(stored, null, 2));
384
468
  }
385
469
  async loadAuthFile() {
386
470
  if (!await this.fs.exists(this.authPath))
@@ -531,54 +615,34 @@ class ChunkingKeyringService {
531
615
  }
532
616
  }
533
617
  }
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;
618
+ // src/services/client-update-required-error.ts
619
+ var CLIENT_UPDATE_REQUIRED_REASON = "Backend protocol changed";
620
+
621
+ class ClientUpdateRequiredError extends Error {
622
+ reason;
623
+ currentVersion;
624
+ constructor(message = `Update required: ${CLIENT_UPDATE_REQUIRED_REASON}`, reason = CLIENT_UPDATE_REQUIRED_REASON, currentVersion = version) {
625
+ super(message);
626
+ this.reason = reason;
627
+ this.currentVersion = currentVersion;
628
+ this.name = "ClientUpdateRequiredError";
552
629
  }
553
- return null;
554
630
  }
555
- function isStringArray(value) {
556
- return Array.isArray(value) && value.every((entry) => typeof entry === "string");
557
- }
558
- function parseJwtPayload(token) {
559
- const parts = token.split(".");
560
- if (parts.length < 2)
561
- return null;
562
- try {
563
- const decoded = Buffer.from(toBase64(parts[1] ?? ""), "base64").toString("utf8");
564
- const parsed = JSON.parse(decoded);
565
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
566
- return null;
567
- }
568
- return parsed;
569
- } catch {
570
- return null;
631
+ function isClientUpdateRequiredGraphQLError(input) {
632
+ if (input.code === "CLIENT_UPDATE_REQUIRED") {
633
+ return true;
634
+ }
635
+ const message = input.message;
636
+ if (!isGraphQLSchemaMismatchMessage(message)) {
637
+ return false;
571
638
  }
639
+ return !input.code || input.code === "GRAPHQL_VALIDATION_FAILED" || input.code === "BAD_USER_INPUT";
572
640
  }
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), "=");
641
+ function isGraphQLSchemaMismatchMessage(message) {
642
+ return /Cannot query field|Field .* does not exist|Unknown argument|Unknown type|Unknown field/i.test(message);
579
643
  }
580
644
  // src/services/code-navigation-service.ts
581
- import { z } from "zod";
645
+ import { z as z2 } from "zod";
582
646
 
583
647
  // src/shared/debug-log.ts
584
648
  function debugLog(area, payload) {
@@ -903,17 +967,6 @@ async function withTelemetrySpan(name, operation, attributes) {
903
967
  throw error;
904
968
  }
905
969
  }
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
970
  function startTelemetrySpan(name, attributes) {
918
971
  return telemetryCollector.startSpan(name, attributes);
919
972
  }
@@ -1420,80 +1473,80 @@ function asRecord(value) {
1420
1473
  }
1421
1474
  return;
1422
1475
  }
1423
- var availableVersionSchema = z.object({
1424
- version: z.string().nullable().optional(),
1425
- ref: z.string()
1476
+ var availableVersionSchema = z2.object({
1477
+ version: z2.string().nullable().optional(),
1478
+ ref: z2.string()
1426
1479
  });
1427
- var unifiedSearchSourceSchema = z.enum(["AUTO", "DOCS", "CODE", "SYMBOL"]);
1428
- var unifiedSearchResultTypeSchema = z.enum([
1480
+ var unifiedSearchSourceSchema = z2.enum(["AUTO", "DOCS", "CODE", "SYMBOL"]);
1481
+ var unifiedSearchResultTypeSchema = z2.enum([
1429
1482
  "DOCUMENTATION_PAGE",
1430
1483
  "REPOSITORY_SYMBOL",
1431
1484
  "REPOSITORY_CODE",
1432
1485
  "REPOSITORY_DOC"
1433
1486
  ]);
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()
1487
+ var unifiedSearchLocatorSchema = z2.object({
1488
+ registry: z2.string().nullable().optional(),
1489
+ packageName: z2.string().nullable().optional(),
1490
+ version: z2.string().nullable().optional(),
1491
+ pageId: z2.string().nullable().optional(),
1492
+ sourceKind: z2.string().nullable().optional(),
1493
+ sourceUrl: z2.string().nullable().optional(),
1494
+ repoUrl: z2.string().nullable().optional(),
1495
+ gitRef: z2.string().nullable().optional(),
1496
+ requestedRef: z2.string().nullable().optional(),
1497
+ filePath: z2.string().nullable().optional(),
1498
+ startLine: z2.number().int().nullable().optional(),
1499
+ endLine: z2.number().int().nullable().optional(),
1500
+ fileContentHash: z2.string().nullable().optional(),
1501
+ symbolRef: z2.string().nullable().optional(),
1502
+ qualifiedPath: z2.string().nullable().optional(),
1503
+ kind: z2.string().nullable().optional(),
1504
+ category: z2.string().nullable().optional(),
1505
+ language: z2.string().nullable().optional()
1453
1506
  });
1454
- var unifiedSearchHitSchema = z.object({
1455
- id: z.string(),
1507
+ var unifiedSearchHitSchema = z2.object({
1508
+ id: z2.string(),
1456
1509
  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()
1510
+ targetLabel: z2.string(),
1511
+ title: z2.string().nullable().optional(),
1512
+ summary: z2.string().nullable().optional(),
1513
+ score: z2.number().nullable().optional(),
1514
+ highlights: z2.object({
1515
+ title: z2.array(z2.tuple([z2.number().int(), z2.number().int()])).nullable().optional(),
1516
+ summary: z2.array(z2.tuple([z2.number().int(), z2.number().int()])).nullable().optional()
1464
1517
  }).nullable().optional(),
1465
1518
  locator: unifiedSearchLocatorSchema
1466
1519
  });
1467
- var unifiedSearchPageInfoSchema = z.object({
1468
- offset: z.number().int(),
1469
- limit: z.number().int(),
1470
- returned: z.number().int(),
1471
- hasMore: z.boolean()
1520
+ var unifiedSearchPageInfoSchema = z2.object({
1521
+ offset: z2.number().int(),
1522
+ limit: z2.number().int(),
1523
+ returned: z2.number().int(),
1524
+ hasMore: z2.boolean()
1472
1525
  });
1473
- var unifiedSearchSourceStatusSchema = z.object({
1526
+ var unifiedSearchSourceStatusSchema = z2.object({
1474
1527
  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()
1528
+ targetLabel: z2.string(),
1529
+ indexingStatus: z2.string().nullable().optional(),
1530
+ codeIndexState: z2.string().nullable().optional(),
1531
+ resultCount: z2.number().int().nullable().optional(),
1532
+ appliedFilters: z2.array(z2.string()),
1533
+ ignoredFilters: z2.array(z2.string()),
1534
+ incompatibleFilters: z2.array(z2.string()),
1535
+ appliedQueryFeatures: z2.array(z2.string()),
1536
+ ignoredQueryFeatures: z2.array(z2.string()),
1537
+ incompatibleQueryFeatures: z2.array(z2.string()),
1538
+ note: z2.string().nullable().optional()
1486
1539
  });
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),
1540
+ var unifiedSearchResultSchema = z2.object({
1541
+ query: z2.string(),
1542
+ queryWarnings: z2.array(z2.string()),
1543
+ sources: z2.array(unifiedSearchSourceSchema),
1544
+ results: z2.array(unifiedSearchHitSchema),
1492
1545
  page: unifiedSearchPageInfoSchema,
1493
- partialResults: z.boolean(),
1494
- sourceStatus: z.array(unifiedSearchSourceStatusSchema)
1546
+ partialResults: z2.boolean(),
1547
+ sourceStatus: z2.array(unifiedSearchSourceStatusSchema)
1495
1548
  });
1496
- var unifiedSearchSessionStatusSchema = z.enum([
1549
+ var unifiedSearchSessionStatusSchema = z2.enum([
1497
1550
  "PENDING",
1498
1551
  "INDEXING",
1499
1552
  "SEARCHING",
@@ -1501,60 +1554,60 @@ var unifiedSearchSessionStatusSchema = z.enum([
1501
1554
  "TIMEOUT",
1502
1555
  "FAILED"
1503
1556
  ]);
1504
- var unifiedSearchProgressSchema = z.object({
1505
- searchRef: z.string(),
1557
+ var unifiedSearchProgressSchema = z2.object({
1558
+ searchRef: z2.string(),
1506
1559
  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(),
1560
+ targetsTotal: z2.number().int(),
1561
+ targetsReady: z2.number().int(),
1562
+ elapsedMs: z2.number().int(),
1563
+ query: z2.string(),
1564
+ queryWarnings: z2.array(z2.string()),
1565
+ sources: z2.array(unifiedSearchSourceSchema),
1566
+ expiresAt: z2.string().nullable().optional(),
1514
1567
  results: unifiedSearchResultSchema.nullable().optional()
1515
1568
  });
1516
- var asyncUnifiedSearchResultSchema = z.object({
1517
- completed: z.boolean(),
1518
- searchRef: z.string().nullable().optional(),
1569
+ var asyncUnifiedSearchResultSchema = z2.object({
1570
+ completed: z2.boolean(),
1571
+ searchRef: z2.string().nullable().optional(),
1519
1572
  result: unifiedSearchResultSchema.nullable().optional(),
1520
1573
  progress: unifiedSearchProgressSchema.nullable().optional()
1521
1574
  });
1522
- var graphQLErrorSchema = z.object({
1523
- message: z.string(),
1524
- extensions: z.record(z.string(), z.unknown()).optional()
1575
+ var graphQLErrorSchema = z2.object({
1576
+ message: z2.string(),
1577
+ extensions: z2.record(z2.string(), z2.unknown()).optional()
1525
1578
  });
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()
1579
+ var navigationResolutionSchema = z2.object({
1580
+ requestedVersion: z2.string().nullable().optional(),
1581
+ requestedRef: z2.string().nullable().optional(),
1582
+ resolvedRef: z2.string().nullable().optional(),
1583
+ commitSha: z2.string().nullable().optional()
1531
1584
  }).nullable().optional();
1532
- var navigationDiagnosticsSchema = z.object({
1533
- hint: z.string().nullable().optional()
1585
+ var navigationDiagnosticsSchema = z2.object({
1586
+ hint: z2.string().nullable().optional()
1534
1587
  }).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()
1588
+ var repoFileEntrySchema = z2.object({
1589
+ path: z2.string(),
1590
+ name: z2.string().nullable().optional(),
1591
+ language: z2.string().nullable().optional(),
1592
+ fileType: z2.string().nullable().optional(),
1593
+ byteSize: z2.number().int().nullable().optional()
1541
1594
  });
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(),
1595
+ var listRepoFilesResponseSchema = z2.object({
1596
+ files: z2.array(repoFileEntrySchema),
1597
+ total: z2.number().int(),
1598
+ hasMore: z2.boolean(),
1599
+ indexedVersion: z2.string().nullable().optional(),
1547
1600
  resolution: navigationResolutionSchema,
1548
1601
  diagnostics: navigationDiagnosticsSchema,
1549
- codeIndexState: z.string(),
1550
- indexingRef: z.string().nullable().optional(),
1551
- availableVersions: z.array(availableVersionSchema).nullable().optional()
1602
+ codeIndexState: z2.string(),
1603
+ indexingRef: z2.string().nullable().optional(),
1604
+ availableVersions: z2.array(availableVersionSchema).nullable().optional()
1552
1605
  });
1553
- var listRepoFilesGraphQLResponseSchema = z.object({
1554
- data: z.object({
1606
+ var listRepoFilesGraphQLResponseSchema = z2.object({
1607
+ data: z2.object({
1555
1608
  listRepoFiles: listRepoFilesResponseSchema.nullable().optional()
1556
1609
  }).nullable().optional(),
1557
- errors: z.array(graphQLErrorSchema).optional()
1610
+ errors: z2.array(graphQLErrorSchema).optional()
1558
1611
  });
1559
1612
  var LIST_REPO_FILES_QUERY = `
1560
1613
  query ListRepoFiles(
@@ -1604,24 +1657,24 @@ query ListRepoFiles(
1604
1657
  }
1605
1658
  }
1606
1659
  }`;
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()
1660
+ var codeContextResponseSchema = z2.object({
1661
+ content: z2.string().nullable().optional(),
1662
+ filePath: z2.string().nullable().optional(),
1663
+ language: z2.string().nullable().optional(),
1664
+ totalLines: z2.number().int().nullable().optional(),
1665
+ startLine: z2.number().int().nullable().optional(),
1666
+ endLine: z2.number().int().nullable().optional(),
1667
+ repoUrl: z2.string().nullable().optional(),
1668
+ gitRef: z2.string().nullable().optional(),
1669
+ isBinary: z2.boolean().nullable().optional(),
1670
+ codeIndexState: z2.string(),
1671
+ indexingRef: z2.string().nullable().optional()
1619
1672
  });
1620
- var fetchCodeContextGraphQLResponseSchema = z.object({
1621
- data: z.object({
1673
+ var fetchCodeContextGraphQLResponseSchema = z2.object({
1674
+ data: z2.object({
1622
1675
  fetchCodeContext: codeContextResponseSchema.nullable().optional()
1623
1676
  }).nullable().optional(),
1624
- errors: z.array(graphQLErrorSchema).optional()
1677
+ errors: z2.array(graphQLErrorSchema).optional()
1625
1678
  });
1626
1679
  var FETCH_CODE_CONTEXT_QUERY = `
1627
1680
  query FetchCodeContext(
@@ -1659,63 +1712,63 @@ query FetchCodeContext(
1659
1712
  indexingRef
1660
1713
  }
1661
1714
  }`;
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()
1715
+ var grepRepoMatchSchema = z2.object({
1716
+ filePath: z2.string(),
1717
+ line: z2.number().int(),
1718
+ matchStartByte: z2.number().int(),
1719
+ matchEndByte: z2.number().int(),
1720
+ lineContent: z2.string(),
1721
+ contextBefore: z2.array(z2.string()).nullable().optional(),
1722
+ contextAfter: z2.array(z2.string()).nullable().optional(),
1723
+ fileContentHash: z2.string().nullable().optional(),
1724
+ fileIntent: z2.string().nullable().optional(),
1725
+ symbolRowId: z2.string().nullable().optional(),
1726
+ symbol: z2.object({
1727
+ symbolRef: z2.string().optional(),
1728
+ name: z2.string().optional(),
1729
+ qualifiedPath: z2.string().nullable().optional(),
1730
+ kind: z2.string().nullable().optional(),
1731
+ category: z2.string().nullable().optional(),
1732
+ arity: z2.number().int().nullable().optional(),
1733
+ isPublic: z2.boolean().nullable().optional(),
1734
+ filePath: z2.string().nullable().optional(),
1735
+ startLine: z2.number().int().nullable().optional(),
1736
+ endLine: z2.number().int().nullable().optional(),
1737
+ code: z2.string().nullable().optional(),
1738
+ callerCount: z2.number().int().nullable().optional(),
1739
+ contentHash: z2.string().nullable().optional(),
1740
+ parentSymbolRef: z2.string().nullable().optional(),
1741
+ parentPath: z2.string().nullable().optional()
1689
1742
  }).nullable().optional()
1690
1743
  });
1691
- var grepRepoResponseSchema = z.object({
1692
- matches: z.array(grepRepoMatchSchema),
1693
- nextCursor: z.string().nullable().optional(),
1694
- hasMore: z.boolean(),
1695
- truncatedReason: z.enum([
1744
+ var grepRepoResponseSchema = z2.object({
1745
+ matches: z2.array(grepRepoMatchSchema),
1746
+ nextCursor: z2.string().nullable().optional(),
1747
+ hasMore: z2.boolean(),
1748
+ truncatedReason: z2.enum([
1696
1749
  "NONE",
1697
1750
  "MAX_MATCHES",
1698
1751
  "MAX_MATCHES_PER_FILE",
1699
1752
  "DEADLINE"
1700
1753
  ]),
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(),
1754
+ routeTaken: z2.enum(["SINGLE_FILE", "CONTENT_INDEX"]).nullable().optional(),
1755
+ filesScanned: z2.number().int(),
1756
+ filesInScope: z2.number().int(),
1757
+ binaryFilesSkipped: z2.number().int(),
1758
+ filesTooLargeSkipped: z2.number().int(),
1759
+ totalMatches: z2.number().int(),
1760
+ uniqueFilesMatched: z2.number().int(),
1761
+ indexedVersion: z2.string().nullable().optional(),
1709
1762
  resolution: navigationResolutionSchema,
1710
- codeIndexState: z.string(),
1711
- indexingRef: z.string().nullable().optional(),
1712
- availableVersions: z.array(availableVersionSchema).nullable().optional()
1763
+ codeIndexState: z2.string(),
1764
+ indexingRef: z2.string().nullable().optional(),
1765
+ availableVersions: z2.array(availableVersionSchema).nullable().optional()
1713
1766
  });
1714
- var grepRepoGraphQLResponseSchema = z.object({
1715
- data: z.object({
1767
+ var grepRepoGraphQLResponseSchema = z2.object({
1768
+ data: z2.object({
1716
1769
  grepRepo: grepRepoResponseSchema.nullable().optional()
1717
1770
  }).nullable().optional(),
1718
- errors: z.array(graphQLErrorSchema).optional()
1771
+ errors: z2.array(graphQLErrorSchema).optional()
1719
1772
  });
1720
1773
  var GREP_REPO_SYMBOL_SELECTIONS = {
1721
1774
  symbol_ref: "symbolRef",
@@ -1824,17 +1877,17 @@ query GrepRepo(
1824
1877
  }
1825
1878
  }`;
1826
1879
  }
1827
- var unifiedSearchGraphQLResponseSchema = z.object({
1828
- data: z.object({
1880
+ var unifiedSearchGraphQLResponseSchema = z2.object({
1881
+ data: z2.object({
1829
1882
  search: asyncUnifiedSearchResultSchema.nullable().optional()
1830
1883
  }).nullable().optional(),
1831
- errors: z.array(graphQLErrorSchema).optional()
1884
+ errors: z2.array(graphQLErrorSchema).optional()
1832
1885
  });
1833
- var unifiedSearchStatusGraphQLResponseSchema = z.object({
1834
- data: z.object({
1886
+ var unifiedSearchStatusGraphQLResponseSchema = z2.object({
1887
+ data: z2.object({
1835
1888
  discoverySearchProgress: unifiedSearchProgressSchema.nullable().optional()
1836
1889
  }).nullable().optional(),
1837
- errors: z.array(graphQLErrorSchema).optional()
1890
+ errors: z2.array(graphQLErrorSchema).optional()
1838
1891
  });
1839
1892
 
1840
1893
  class CodeNavigationServiceImpl {
@@ -1987,6 +2040,9 @@ class CodeNavigationServiceImpl {
1987
2040
  const code = typeof extensions?.code === "string" ? extensions.code : undefined;
1988
2041
  const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
1989
2042
  const indexingRef = getGraphQLIndexingRef(errors);
2043
+ if (isClientUpdateRequiredGraphQLError({ message, code })) {
2044
+ return new ClientUpdateRequiredError;
2045
+ }
1990
2046
  switch (code) {
1991
2047
  case "PACKAGE_INDEXING":
1992
2048
  return new CodeNavigationIndexingError(this.createIndexingMessage(indexingRef), indexingRef, parseAvailableVersions(extensions));
@@ -2481,19 +2537,11 @@ function getCodeNavigationUrl() {
2481
2537
  if (explicitUrl) {
2482
2538
  return explicitUrl;
2483
2539
  }
2484
- const usingDefaultEnvironment = getMcpUrl() === DEFAULT_MCP_URL && getApiUrl() === DEFAULT_API_URL;
2485
- return usingDefaultEnvironment ? DEFAULT_CODE_NAV_URL : undefined;
2540
+ return DEFAULT_CODE_NAV_URL;
2486
2541
  }
2487
2542
  function getEnvApiToken() {
2488
2543
  return process.env.GITHITS_API_TOKEN;
2489
2544
  }
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
2545
  // src/services/exec-service.ts
2498
2546
  import { spawn } from "node:child_process";
2499
2547
 
@@ -2523,6 +2571,7 @@ class ExecServiceImpl {
2523
2571
  }
2524
2572
  }
2525
2573
  // src/services/filesystem-service.ts
2574
+ import { randomUUID as randomUUID2 } from "node:crypto";
2526
2575
  import {
2527
2576
  mkdir,
2528
2577
  readdir,
@@ -2586,7 +2635,7 @@ class FileSystemServiceImpl {
2586
2635
  }
2587
2636
  }
2588
2637
  async atomicWriteFile(path, contents) {
2589
- const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
2638
+ const tmpPath = `${path}.${process.pid}.${randomUUID2()}.tmp`;
2590
2639
  let mode = 384;
2591
2640
  try {
2592
2641
  const existing = await stat(path);
@@ -2718,149 +2767,345 @@ class KeyringServiceImpl {
2718
2767
  }
2719
2768
  }
2720
2769
  }
2770
+ // src/services/mode-aware-file-auth-storage.ts
2771
+ class AuthStoragePolicyError extends Error {
2772
+ constructor(message) {
2773
+ super(message);
2774
+ this.name = "AuthStoragePolicyError";
2775
+ }
2776
+ }
2777
+ function createFileAuthStorageGuidance(configPath) {
2778
+ return `OAuth credentials were not saved to plaintext file storage.
2779
+
2780
+ Options:
2781
+ 1. Unlock or fix your system keychain.
2782
+ 2. Use GITHITS_API_TOKEN for CI/automation.
2783
+ 3. If you accept storing OAuth credentials unencrypted on disk, set:
2784
+
2785
+ [auth]
2786
+ storage = "file"
2787
+
2788
+ in ${configPath}, or run with GITHITS_AUTH_STORAGE=file.
2789
+
2790
+ Warning: file storage is plaintext. Use it only on machines where local file access is trusted.`;
2791
+ }
2792
+
2793
+ class ModeAwareFileAuthStorage {
2794
+ storage;
2795
+ mode;
2796
+ configPath;
2797
+ constructor(storage, mode, configPath = "your GitHits config.toml") {
2798
+ this.storage = storage;
2799
+ this.mode = mode;
2800
+ this.configPath = configPath;
2801
+ }
2802
+ loadTokens(baseUrl2) {
2803
+ return this.storage.loadTokens(baseUrl2);
2804
+ }
2805
+ async saveTokens(baseUrl2, data) {
2806
+ this.assertFileMode();
2807
+ await this.storage.saveTokens(baseUrl2, data);
2808
+ }
2809
+ clearTokens(baseUrl2) {
2810
+ return this.storage.clearTokens(baseUrl2);
2811
+ }
2812
+ loadClient(baseUrl2) {
2813
+ return this.storage.loadClient(baseUrl2);
2814
+ }
2815
+ async saveClient(baseUrl2, data) {
2816
+ this.assertFileMode();
2817
+ await this.storage.saveClient(baseUrl2, data);
2818
+ }
2819
+ clearClient(baseUrl2) {
2820
+ return this.storage.clearClient(baseUrl2);
2821
+ }
2822
+ getStorageLocation() {
2823
+ return this.storage.getStorageLocation();
2824
+ }
2825
+ assertFileMode() {
2826
+ if (this.mode === "file")
2827
+ return;
2828
+ throw new AuthStoragePolicyError(createFileAuthStorageGuidance(this.configPath));
2829
+ }
2830
+ }
2831
+
2721
2832
  // src/services/migrating-auth-storage.ts
2722
2833
  class MigratingAuthStorage {
2723
2834
  primary;
2835
+ file;
2724
2836
  legacy;
2725
- onPrimaryUnavailable;
2726
- primaryAvailable = true;
2727
- warnedOnPrimaryFailure = false;
2728
- constructor(primary, legacy, onPrimaryUnavailable = () => {}) {
2837
+ mode;
2838
+ configPath;
2839
+ onWarning;
2840
+ warnedFileModeKeychainExport = false;
2841
+ warnedAmbiguousPlaintext = false;
2842
+ constructor(primary, file, legacy, mode, configPath = "your GitHits config.toml", onWarning = () => {}) {
2729
2843
  this.primary = primary;
2844
+ this.file = file;
2730
2845
  this.legacy = legacy;
2731
- this.onPrimaryUnavailable = onPrimaryUnavailable;
2846
+ this.mode = mode;
2847
+ this.configPath = configPath;
2848
+ this.onWarning = onWarning;
2732
2849
  }
2733
2850
  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
- }
2851
+ if (this.mode === "file") {
2852
+ return this.loadTokensFileMode(baseUrl2);
2853
+ }
2854
+ return this.loadTokensKeychainMode(baseUrl2);
2855
+ }
2856
+ async saveTokens(baseUrl2, data) {
2857
+ if (this.mode === "file") {
2858
+ await this.file.saveTokens(baseUrl2, data);
2859
+ return;
2743
2860
  }
2744
- const legacyTokens = await this.legacy.loadTokens(baseUrl2);
2745
- if (!legacyTokens)
2746
- return null;
2747
- if (!this.primaryAvailable)
2748
- return legacyTokens;
2749
2861
  try {
2750
- await this.primary.saveTokens(baseUrl2, legacyTokens);
2862
+ await this.primary.saveTokens(baseUrl2, data);
2751
2863
  } catch (error) {
2752
- if (!this.handlePrimaryFailure(error))
2753
- throw error;
2754
- return legacyTokens;
2864
+ throw this.toPolicyError(error);
2755
2865
  }
2756
- try {
2757
- await this.legacy.clearTokens(baseUrl2);
2758
- } catch {}
2759
- return legacyTokens;
2760
2866
  }
2761
- async saveTokens(baseUrl2, data) {
2762
- if (this.primaryAvailable) {
2763
- try {
2764
- await this.primary.saveTokens(baseUrl2, data);
2765
- return;
2766
- } catch (error) {
2767
- if (!this.handlePrimaryFailure(error))
2768
- throw error;
2769
- }
2867
+ async clearTokens(baseUrl2) {
2868
+ const primaryError = await this.clearBestEffort(() => this.primary.clearTokens(baseUrl2));
2869
+ await this.clearBestEffort(() => this.file.clearTokens(baseUrl2));
2870
+ await this.clearBestEffort(() => this.legacy.clearTokens(baseUrl2));
2871
+ if (primaryError && !(primaryError instanceof KeychainUnavailableError)) {
2872
+ throw primaryError;
2770
2873
  }
2771
- await this.legacy.saveTokens(baseUrl2, data);
2772
2874
  }
2773
- async clearTokens(baseUrl2) {
2774
- let primaryError;
2775
- if (this.primaryAvailable) {
2776
- try {
2777
- await this.primary.clearTokens(baseUrl2);
2778
- } catch (error) {
2779
- if (!this.handlePrimaryFailure(error)) {
2780
- primaryError = error;
2781
- }
2782
- }
2875
+ async loadClient(baseUrl2) {
2876
+ if (this.mode === "file") {
2877
+ return this.loadClientFileMode(baseUrl2);
2878
+ }
2879
+ return this.loadClientKeychainMode(baseUrl2);
2880
+ }
2881
+ async saveClient(baseUrl2, data) {
2882
+ if (this.mode === "file") {
2883
+ await this.file.saveClient(baseUrl2, data);
2884
+ return;
2783
2885
  }
2784
2886
  try {
2785
- await this.legacy.clearTokens(baseUrl2);
2786
- } catch {}
2787
- if (primaryError)
2887
+ await this.primary.saveClient(baseUrl2, data);
2888
+ } catch (error) {
2889
+ throw this.toPolicyError(error);
2890
+ }
2891
+ }
2892
+ async clearClient(baseUrl2) {
2893
+ const primaryError = await this.clearBestEffort(() => this.primary.clearClient(baseUrl2));
2894
+ await this.clearBestEffort(() => this.file.clearClient(baseUrl2));
2895
+ await this.clearBestEffort(() => this.legacy.clearClient(baseUrl2));
2896
+ if (primaryError && !(primaryError instanceof KeychainUnavailableError)) {
2788
2897
  throw primaryError;
2898
+ }
2789
2899
  }
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;
2900
+ getStorageLocation() {
2901
+ return this.mode === "file" ? this.file.getStorageLocation() : this.primary.getStorageLocation();
2902
+ }
2903
+ async loadTokensKeychainMode(baseUrl2) {
2904
+ try {
2905
+ const primaryTokens = await this.primary.loadTokens(baseUrl2);
2906
+ if (primaryTokens)
2907
+ return primaryTokens;
2908
+ } catch (error) {
2909
+ if (!(error instanceof KeychainUnavailableError))
2910
+ throw error;
2911
+ }
2912
+ const candidate = await this.selectPlaintextTokenCandidate(baseUrl2);
2913
+ if (!candidate)
2914
+ return null;
2915
+ try {
2916
+ await this.primary.saveTokens(baseUrl2, candidate.data);
2917
+ } catch (error) {
2918
+ if (error instanceof KeychainUnavailableError)
2919
+ return candidate.data;
2920
+ throw error;
2921
+ }
2922
+ await this.clearMigratedPlaintext(baseUrl2, "tokens", candidate.source, candidate.ambiguous);
2923
+ return candidate.data;
2924
+ }
2925
+ async loadTokensFileMode(baseUrl2) {
2926
+ const candidate = await this.selectPlaintextTokenCandidate(baseUrl2);
2927
+ if (candidate) {
2928
+ if (candidate.source === "legacy") {
2929
+ await this.file.saveTokens(baseUrl2, candidate.data);
2930
+ await this.clearBestEffort(() => this.legacy.clearTokens(baseUrl2));
2799
2931
  }
2932
+ return candidate.data;
2800
2933
  }
2801
- const legacyClient = await this.legacy.loadClient(baseUrl2);
2802
- if (!legacyClient)
2934
+ let primaryTokens = null;
2935
+ try {
2936
+ primaryTokens = await this.primary.loadTokens(baseUrl2);
2937
+ } catch (error) {
2938
+ if (!(error instanceof KeychainUnavailableError))
2939
+ throw error;
2940
+ }
2941
+ if (!primaryTokens)
2803
2942
  return null;
2804
- if (!this.primaryAvailable)
2805
- return legacyClient;
2943
+ this.warnKeychainExport();
2944
+ await this.file.saveTokens(baseUrl2, primaryTokens);
2945
+ return primaryTokens;
2946
+ }
2947
+ async loadClientKeychainMode(baseUrl2) {
2806
2948
  try {
2807
- await this.primary.saveClient(baseUrl2, legacyClient);
2949
+ const primaryClient = await this.primary.loadClient(baseUrl2);
2950
+ if (primaryClient)
2951
+ return primaryClient;
2808
2952
  } catch (error) {
2809
- if (!this.handlePrimaryFailure(error))
2953
+ if (!(error instanceof KeychainUnavailableError))
2810
2954
  throw error;
2811
- return legacyClient;
2812
2955
  }
2956
+ const candidate = await this.selectPlaintextClientCandidate(baseUrl2);
2957
+ if (!candidate)
2958
+ return null;
2813
2959
  try {
2814
- await this.legacy.clearClient(baseUrl2);
2815
- } catch {}
2816
- return legacyClient;
2817
- }
2818
- async saveClient(baseUrl2, data) {
2819
- if (this.primaryAvailable) {
2820
- try {
2821
- await this.primary.saveClient(baseUrl2, data);
2822
- return;
2823
- } catch (error) {
2824
- if (!this.handlePrimaryFailure(error))
2825
- throw error;
2826
- }
2960
+ await this.primary.saveClient(baseUrl2, candidate.data);
2961
+ } catch (error) {
2962
+ if (error instanceof KeychainUnavailableError)
2963
+ return candidate.data;
2964
+ throw error;
2827
2965
  }
2828
- await this.legacy.saveClient(baseUrl2, data);
2966
+ await this.clearMigratedPlaintext(baseUrl2, "client", candidate.source, candidate.ambiguous);
2967
+ return candidate.data;
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 loadClientFileMode(baseUrl2) {
2970
+ const candidate = await this.selectPlaintextClientCandidate(baseUrl2);
2971
+ if (candidate) {
2972
+ if (candidate.source === "legacy") {
2973
+ await this.file.saveClient(baseUrl2, candidate.data);
2974
+ await this.clearBestEffort(() => this.legacy.clearClient(baseUrl2));
2839
2975
  }
2976
+ return candidate.data;
2840
2977
  }
2978
+ let primaryClient = null;
2841
2979
  try {
2842
- await this.legacy.clearClient(baseUrl2);
2843
- } catch {}
2844
- if (primaryError)
2845
- throw primaryError;
2846
- }
2847
- getStorageLocation() {
2848
- return this.primaryAvailable ? this.primary.getStorageLocation() : this.legacy.getStorageLocation();
2980
+ primaryClient = await this.primary.loadClient(baseUrl2);
2981
+ } catch (error) {
2982
+ if (!(error instanceof KeychainUnavailableError))
2983
+ throw error;
2984
+ }
2985
+ if (!primaryClient)
2986
+ return null;
2987
+ this.warnKeychainExport();
2988
+ await this.file.saveClient(baseUrl2, primaryClient);
2989
+ return primaryClient;
2990
+ }
2991
+ async selectPlaintextTokenCandidate(baseUrl2) {
2992
+ const candidates = [];
2993
+ const fileTokens = await this.file.loadTokens(baseUrl2);
2994
+ if (fileTokens) {
2995
+ candidates.push({
2996
+ data: fileTokens,
2997
+ source: "file",
2998
+ timestamp: fileTokens.createdAt,
2999
+ ambiguous: false
3000
+ });
3001
+ }
3002
+ const legacyTokens = await this.legacy.loadTokens(baseUrl2);
3003
+ if (legacyTokens) {
3004
+ candidates.push({
3005
+ data: legacyTokens,
3006
+ source: "legacy",
3007
+ timestamp: legacyTokens.createdAt,
3008
+ ambiguous: false
3009
+ });
3010
+ }
3011
+ return this.selectNewestCandidate(candidates);
3012
+ }
3013
+ async selectPlaintextClientCandidate(baseUrl2) {
3014
+ const candidates = [];
3015
+ const fileClient = await this.file.loadClient(baseUrl2);
3016
+ if (fileClient) {
3017
+ candidates.push({
3018
+ data: fileClient,
3019
+ source: "file",
3020
+ timestamp: fileClient.registeredAt,
3021
+ ambiguous: false
3022
+ });
3023
+ }
3024
+ const legacyClient = await this.legacy.loadClient(baseUrl2);
3025
+ if (legacyClient) {
3026
+ candidates.push({
3027
+ data: legacyClient,
3028
+ source: "legacy",
3029
+ timestamp: legacyClient.registeredAt,
3030
+ ambiguous: false
3031
+ });
3032
+ }
3033
+ return this.selectNewestCandidate(candidates);
2849
3034
  }
2850
- handlePrimaryFailure(error) {
2851
- if (!(error instanceof KeychainUnavailableError)) {
2852
- return false;
3035
+ selectNewestCandidate(candidates) {
3036
+ if (candidates.length === 0)
3037
+ return null;
3038
+ if (candidates.length === 1)
3039
+ return candidates[0] ?? null;
3040
+ const parsed = candidates.map((candidate) => ({
3041
+ candidate,
3042
+ timestampMs: Date.parse(candidate.timestamp)
3043
+ }));
3044
+ if (parsed.some((entry) => Number.isNaN(entry.timestampMs))) {
3045
+ this.warnAmbiguousPlaintext();
3046
+ const selected = candidates.find((candidate) => candidate.source === "file") ?? candidates[0] ?? null;
3047
+ if (selected)
3048
+ selected.ambiguous = true;
3049
+ return selected;
3050
+ }
3051
+ const sorted = [...parsed].sort((a, b) => b.timestampMs - a.timestampMs);
3052
+ const first = sorted[0];
3053
+ const second = sorted[1];
3054
+ if (!first)
3055
+ return candidates[0] ?? null;
3056
+ if (second && first.timestampMs === second.timestampMs) {
3057
+ this.warnAmbiguousPlaintext();
3058
+ const selected = candidates.find((candidate) => candidate.source === "file") ?? first.candidate;
3059
+ selected.ambiguous = true;
3060
+ return selected;
3061
+ }
3062
+ return first.candidate;
3063
+ }
3064
+ async clearMigratedPlaintext(baseUrl2, kind, source, ambiguous = false) {
3065
+ if (!ambiguous) {
3066
+ await this.clearPlaintextSource(this.file, baseUrl2, kind);
3067
+ await this.clearPlaintextSource(this.legacy, baseUrl2, kind);
3068
+ return;
3069
+ }
3070
+ if (source === "file") {
3071
+ await this.clearPlaintextSource(this.file, baseUrl2, kind);
3072
+ return;
2853
3073
  }
2854
- this.primaryAvailable = false;
2855
- if (!this.warnedOnPrimaryFailure) {
2856
- this.warnedOnPrimaryFailure = true;
2857
- this.onPrimaryUnavailable(error);
3074
+ if (source === "legacy") {
3075
+ await this.clearPlaintextSource(this.legacy, baseUrl2, kind);
2858
3076
  }
2859
- return true;
3077
+ }
3078
+ async clearPlaintextSource(storage, baseUrl2, kind) {
3079
+ await this.clearBestEffort(() => kind === "tokens" ? storage.clearTokens(baseUrl2) : storage.clearClient(baseUrl2));
3080
+ }
3081
+ async clearBestEffort(fn) {
3082
+ try {
3083
+ await fn();
3084
+ return;
3085
+ } catch (error) {
3086
+ return error;
3087
+ }
3088
+ }
3089
+ toPolicyError(error) {
3090
+ if (!(error instanceof KeychainUnavailableError))
3091
+ return error;
3092
+ return new AuthStoragePolicyError(`System keychain is unavailable. ${createFileAuthStorageGuidance(this.configPath)}`);
3093
+ }
3094
+ warnKeychainExport() {
3095
+ if (this.warnedFileModeKeychainExport)
3096
+ return;
3097
+ this.warnedFileModeKeychainExport = true;
3098
+ this.onWarning("Warning: auth.storage=file is exporting existing keychain credentials to plaintext file storage.");
3099
+ }
3100
+ warnAmbiguousPlaintext() {
3101
+ if (this.warnedAmbiguousPlaintext)
3102
+ return;
3103
+ this.warnedAmbiguousPlaintext = true;
3104
+ this.onWarning("Warning: multiple plaintext auth entries exist with ambiguous timestamps; using the new config auth path and leaving the other entry intact.");
2860
3105
  }
2861
3106
  }
2862
3107
  // src/services/package-intelligence-service.ts
2863
- import { z as z2 } from "zod";
3108
+ import { z as z3 } from "zod";
2864
3109
 
2865
3110
  // src/services/promote-version-not-found.ts
2866
3111
  function promoteGenericVersionNotFound(error, params) {
@@ -2975,63 +3220,63 @@ class PackageIntelligenceChangelogSourceNotFoundError extends Error {
2975
3220
  this.name = "PackageIntelligenceChangelogSourceNotFoundError";
2976
3221
  }
2977
3222
  }
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()
3223
+ var githubRepositorySchema = z3.object({
3224
+ stargazersCount: z3.number().int().nullable().optional(),
3225
+ forksCount: z3.number().int().nullable().optional(),
3226
+ openIssuesCount: z3.number().int().nullable().optional(),
3227
+ archived: z3.boolean().nullable().optional(),
3228
+ language: z3.string().nullable().optional(),
3229
+ topics: z3.array(z3.string()).nullable().optional(),
3230
+ pushedAt: z3.string().nullable().optional()
2986
3231
  }).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(),
3232
+ var packageIdentitySchema = z3.object({
3233
+ name: z3.string().nullable().optional(),
3234
+ registry: z3.string().nullable().optional(),
3235
+ description: z3.string().nullable().optional(),
3236
+ latestVersion: z3.string().nullable().optional(),
3237
+ latestVersionPublishedAt: z3.string().nullable().optional(),
3238
+ homepage: z3.string().nullable().optional(),
3239
+ repositoryUrl: z3.string().nullable().optional(),
3240
+ license: z3.string().nullable().optional(),
3241
+ downloadsLastMonth: z3.number().int().nullable().optional(),
3242
+ downloadsTotal: z3.number().int().nullable().optional(),
2998
3243
  githubRepository: githubRepositorySchema
2999
3244
  });
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()
3245
+ var vulnerabilityOverviewSchema = z3.object({
3246
+ osvId: z3.string().nullable().optional(),
3247
+ summary: z3.string().nullable().optional(),
3248
+ severityScore: z3.number().nullable().optional(),
3249
+ publishedAt: z3.string().nullable().optional()
3005
3250
  });
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()
3251
+ var packageSecurityOverviewSchema = z3.object({
3252
+ vulnerabilityCount: z3.number().int().nullable().optional(),
3253
+ hasCurrentVulnerabilities: z3.boolean().nullable().optional(),
3254
+ recentVulnerabilities: z3.array(vulnerabilityOverviewSchema).nullable().optional()
3010
3255
  }).nullable().optional();
3011
- var quickstartInfoSchema = z2.object({
3012
- installCommand: z2.string().nullable().optional(),
3013
- usageExample: z2.string().nullable().optional()
3256
+ var quickstartInfoSchema = z3.object({
3257
+ installCommand: z3.string().nullable().optional(),
3258
+ usageExample: z3.string().nullable().optional()
3014
3259
  }).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()
3260
+ var changelogEntrySchema = z3.object({
3261
+ version: z3.string().nullable().optional(),
3262
+ publishedAt: z3.string().nullable().optional(),
3263
+ body: z3.string().nullable().optional()
3019
3264
  });
3020
- var packageSummaryResponseSchema = z2.object({
3265
+ var packageSummaryResponseSchema = z3.object({
3021
3266
  package: packageIdentitySchema.nullable().optional(),
3022
3267
  security: packageSecurityOverviewSchema,
3023
3268
  quickstart: quickstartInfoSchema,
3024
- latestChangelogs: z2.array(changelogEntrySchema).nullable().optional()
3269
+ latestChangelogs: z3.array(changelogEntrySchema).nullable().optional()
3025
3270
  });
3026
- var graphQLErrorSchema2 = z2.object({
3027
- message: z2.string(),
3028
- extensions: z2.record(z2.string(), z2.unknown()).optional()
3271
+ var graphQLErrorSchema2 = z3.object({
3272
+ message: z3.string(),
3273
+ extensions: z3.record(z3.string(), z3.unknown()).optional()
3029
3274
  });
3030
- var graphQLResponseSchema = z2.object({
3031
- data: z2.object({
3275
+ var graphQLResponseSchema = z3.object({
3276
+ data: z3.object({
3032
3277
  packageSummary: packageSummaryResponseSchema.nullable().optional()
3033
3278
  }).nullable().optional(),
3034
- errors: z2.array(graphQLErrorSchema2).optional()
3279
+ errors: z3.array(graphQLErrorSchema2).optional()
3035
3280
  });
3036
3281
  var PACKAGE_SUMMARY_QUERY = `
3037
3282
  query PackageSummary($registry: Registry!, $name: String!) {
@@ -3078,39 +3323,55 @@ query PackageSummary($registry: Registry!, $name: String!) {
3078
3323
  }
3079
3324
  }
3080
3325
  }`;
3081
- var packageVersionIdentitySchema = z2.object({
3082
- name: z2.string().nullable().optional(),
3083
- registry: z2.string().nullable().optional(),
3084
- version: z2.string().nullable().optional()
3326
+ var packageVersionIdentitySchema = z3.object({
3327
+ name: z3.string().nullable().optional(),
3328
+ registry: z3.string().nullable().optional(),
3329
+ version: z3.string().nullable().optional()
3085
3330
  });
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()
3331
+ var vulnerabilityDetailSchema = z3.object({
3332
+ osvId: z3.string().nullable().optional(),
3333
+ summary: z3.string().nullable().optional(),
3334
+ severityScore: z3.number().nullable().optional(),
3335
+ severityType: z3.string().nullable().optional(),
3336
+ affectedVersionRanges: z3.array(z3.string()).nullable().optional(),
3337
+ affectedVersionRangesCount: z3.number().int(),
3338
+ affectedVersionRangesTruncated: z3.boolean(),
3339
+ fixedInVersions: z3.array(z3.string()).nullable().optional(),
3340
+ publishedAt: z3.string().nullable().optional(),
3341
+ modifiedAt: z3.string().nullable().optional(),
3342
+ withdrawnAt: z3.string().nullable().optional(),
3343
+ aliases: z3.array(z3.string()).nullable().optional(),
3344
+ isMalicious: z3.boolean().nullable().optional(),
3345
+ affectsInspectedVersion: z3.boolean(),
3346
+ matchedAffectedVersionRanges: z3.array(z3.string()),
3347
+ duplicateIds: z3.array(z3.string())
3098
3348
  });
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()
3349
+ var pageInfoSchema = z3.object({
3350
+ hasNextPage: z3.boolean(),
3351
+ endCursor: z3.string().nullable().optional(),
3352
+ totalCount: z3.number().int()
3353
+ });
3354
+ var vulnerabilityAdvisoryPageSchema = z3.object({
3355
+ entries: z3.array(vulnerabilityDetailSchema),
3356
+ pageInfo: pageInfoSchema
3357
+ });
3358
+ var vulnerabilitySecurityDetailsSchema = z3.object({
3359
+ affectedVulnerabilityCount: z3.number().int(),
3360
+ nonAffectingVulnerabilityCount: z3.number().int(),
3361
+ allVulnerabilityCount: z3.number().int(),
3362
+ currentVersionAffected: z3.boolean().nullable().optional(),
3363
+ advisories: vulnerabilityAdvisoryPageSchema,
3364
+ upgradePaths: z3.array(z3.string()).nullable().optional()
3104
3365
  }).nullable().optional();
3105
- var vulnerabilityReportResponseSchema = z2.object({
3366
+ var vulnerabilityReportResponseSchema = z3.object({
3106
3367
  package: packageVersionIdentitySchema.nullable().optional(),
3107
3368
  security: vulnerabilitySecurityDetailsSchema
3108
3369
  });
3109
- var vulnerabilitiesGraphQLResponseSchema = z2.object({
3110
- data: z2.object({
3370
+ var vulnerabilitiesGraphQLResponseSchema = z3.object({
3371
+ data: z3.object({
3111
3372
  packageVulnerabilities: vulnerabilityReportResponseSchema.nullable().optional()
3112
3373
  }).nullable().optional(),
3113
- errors: z2.array(graphQLErrorSchema2).optional()
3374
+ errors: z3.array(graphQLErrorSchema2).optional()
3114
3375
  });
3115
3376
  var PACKAGE_VULNERABILITIES_QUERY = `
3116
3377
  query PackageVulnerabilities(
@@ -3119,6 +3380,7 @@ query PackageVulnerabilities(
3119
3380
  $version: String
3120
3381
  $minSeverity: Float
3121
3382
  $includeWithdrawn: Boolean
3383
+ $after: String
3122
3384
  ) {
3123
3385
  packageVulnerabilities(
3124
3386
  registry: $registry
@@ -3133,110 +3395,124 @@ query PackageVulnerabilities(
3133
3395
  version
3134
3396
  }
3135
3397
  security {
3136
- vulnerabilityCount
3398
+ affectedVulnerabilityCount
3399
+ nonAffectingVulnerabilityCount
3400
+ allVulnerabilityCount
3137
3401
  currentVersionAffected
3138
3402
  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
3403
+ advisories(scope: AFFECTED, first: 100, after: $after) {
3404
+ entries {
3405
+ osvId
3406
+ summary
3407
+ severityScore
3408
+ severityType
3409
+ affectedVersionRanges
3410
+ affectedVersionRangesCount
3411
+ affectedVersionRangesTruncated
3412
+ fixedInVersions
3413
+ publishedAt
3414
+ modifiedAt
3415
+ withdrawnAt
3416
+ aliases
3417
+ isMalicious
3418
+ affectsInspectedVersion
3419
+ matchedAffectedVersionRanges
3420
+ duplicateIds
3421
+ }
3422
+ pageInfo {
3423
+ hasNextPage
3424
+ endCursor
3425
+ totalCount
3426
+ }
3151
3427
  }
3152
3428
  }
3153
3429
  }
3154
3430
  }`;
3155
- var directDependencySchema = z2.object({
3156
- name: z2.string().nullable().optional(),
3157
- versionConstraint: z2.string().nullable().optional(),
3158
- type: z2.string().nullable().optional()
3431
+ var directDependencySchema = z3.object({
3432
+ name: z3.string().nullable().optional(),
3433
+ versionConstraint: z3.string().nullable().optional(),
3434
+ type: z3.string().nullable().optional()
3159
3435
  });
3160
- var dependencyGraphNodeSchema = z2.object({
3161
- registry: z2.string(),
3162
- name: z2.string(),
3163
- version: z2.string().nullable().optional()
3436
+ var dependencyGraphNodeSchema = z3.object({
3437
+ registry: z3.string(),
3438
+ name: z3.string(),
3439
+ version: z3.string().nullable().optional()
3164
3440
  });
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()
3441
+ var dependencyGraphEdgeSchema = z3.object({
3442
+ fromIndex: z3.number().int().nullable().optional(),
3443
+ toIndex: z3.number().int(),
3444
+ constraint: z3.string().nullable().optional(),
3445
+ dependencyType: z3.string().nullable().optional()
3170
3446
  });
3171
- var dependencyGraphSchema = z2.object({
3172
- formatVersion: z2.number().int(),
3173
- nodes: z2.array(dependencyGraphNodeSchema),
3174
- edges: z2.array(dependencyGraphEdgeSchema)
3447
+ var dependencyGraphSchema = z3.object({
3448
+ formatVersion: z3.number().int(),
3449
+ nodes: z3.array(dependencyGraphNodeSchema),
3450
+ edges: z3.array(dependencyGraphEdgeSchema)
3175
3451
  });
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()
3452
+ var dependencyConflictEdgeSchema = z3.object({
3453
+ fromIndex: z3.number().int().nullable().optional(),
3454
+ toIndex: z3.number().int(),
3455
+ versionConstraint: z3.string(),
3456
+ dependencyType: z3.string()
3181
3457
  });
3182
- var dependencyConflictSchema = z2.object({
3183
- packageName: z2.string(),
3184
- requiredVersions: z2.array(z2.string()),
3185
- conflictingEdges: z2.array(dependencyConflictEdgeSchema)
3458
+ var dependencyConflictSchema = z3.object({
3459
+ packageName: z3.string(),
3460
+ requiredVersions: z3.array(z3.string()),
3461
+ conflictingEdges: z3.array(dependencyConflictEdgeSchema)
3186
3462
  });
3187
- var circularDependencyCycleSchema = z2.object({
3188
- cycleStart: z2.string(),
3189
- circularPath: z2.array(z2.string()),
3190
- displayChain: z2.string()
3463
+ var circularDependencyCycleSchema = z3.object({
3464
+ cycleStart: z3.string(),
3465
+ circularPath: z3.array(z3.string()),
3466
+ displayChain: z3.string()
3191
3467
  });
3192
- var environmentMarkerSchema = z2.object({
3193
- type: z2.string().nullable().optional(),
3194
- value: z2.string().nullable().optional(),
3195
- raw: z2.string().nullable().optional()
3468
+ var environmentMarkerSchema = z3.object({
3469
+ type: z3.string().nullable().optional(),
3470
+ value: z3.string().nullable().optional(),
3471
+ raw: z3.string().nullable().optional()
3196
3472
  });
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(),
3473
+ var transitiveDependencySchema = z3.object({
3474
+ totalEdges: z3.number().int().nullable().optional(),
3475
+ uniquePackagesCount: z3.number().int().nullable().optional(),
3476
+ uniqueDependencies: z3.array(z3.string()).nullable().optional(),
3477
+ dependencyConflicts: z3.array(dependencyConflictSchema).nullable().optional(),
3478
+ circularDependencyCycles: z3.array(circularDependencyCycleSchema).nullable().optional(),
3203
3479
  dependencyGraph: dependencyGraphSchema.nullable().optional()
3204
3480
  }).nullable().optional();
3205
- var dependencyBundleSchema = z2.object({
3206
- direct: z2.array(directDependencySchema).nullable().optional(),
3481
+ var dependencyBundleSchema = z3.object({
3482
+ direct: z3.array(directDependencySchema).nullable().optional(),
3207
3483
  transitive: transitiveDependencySchema
3208
3484
  }).nullable().optional();
3209
- var groupDependencySchema = z2.object({
3210
- name: z2.string(),
3211
- constraint: z2.string().nullable().optional()
3485
+ var groupDependencySchema = z3.object({
3486
+ name: z3.string(),
3487
+ constraint: z3.string().nullable().optional()
3212
3488
  });
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)
3489
+ var dependencyGroupSchema = z3.object({
3490
+ name: z3.string(),
3491
+ lifecycle: z3.string(),
3492
+ conditionType: z3.string(),
3493
+ conditionValue: z3.string().nullable().optional(),
3494
+ selectionMode: z3.string(),
3495
+ exclusiveGroup: z3.string().nullable().optional(),
3496
+ fallbackPriority: z3.number().int().nullable().optional(),
3497
+ compatibleWith: z3.array(z3.string()).nullable().optional(),
3498
+ defaultEnabled: z3.boolean().nullable().optional(),
3499
+ dependencies: z3.array(groupDependencySchema)
3224
3500
  });
3225
- var dependencyGroupsInfoSchema = z2.object({
3226
- primaryGroup: z2.string().nullable().optional(),
3227
- environmentMarkers: z2.array(environmentMarkerSchema).nullable().optional(),
3228
- groups: z2.array(dependencyGroupSchema)
3501
+ var dependencyGroupsInfoSchema = z3.object({
3502
+ primaryGroup: z3.string().nullable().optional(),
3503
+ environmentMarkers: z3.array(environmentMarkerSchema).nullable().optional(),
3504
+ groups: z3.array(dependencyGroupSchema)
3229
3505
  }).nullable().optional();
3230
- var dependencyReportResponseSchema = z2.object({
3506
+ var dependencyReportResponseSchema = z3.object({
3231
3507
  package: packageVersionIdentitySchema.nullable().optional(),
3232
3508
  dependencies: dependencyBundleSchema,
3233
3509
  dependencyGroups: dependencyGroupsInfoSchema
3234
3510
  });
3235
- var dependenciesGraphQLResponseSchema = z2.object({
3236
- data: z2.object({
3511
+ var dependenciesGraphQLResponseSchema = z3.object({
3512
+ data: z3.object({
3237
3513
  packageDependencies: dependencyReportResponseSchema.nullable().optional()
3238
3514
  }).nullable().optional(),
3239
- errors: z2.array(graphQLErrorSchema2).optional()
3515
+ errors: z3.array(graphQLErrorSchema2).optional()
3240
3516
  });
3241
3517
  var PACKAGE_DEPENDENCIES_QUERY = `
3242
3518
  query PackageDependencies(
@@ -3330,32 +3606,32 @@ query PackageDependencies(
3330
3606
  }
3331
3607
  }
3332
3608
  }`;
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()
3609
+ var changelogPackageInfoSchema = z3.object({
3610
+ name: z3.string().nullable().optional(),
3611
+ registry: z3.string().nullable().optional(),
3612
+ repoUrl: z3.string().nullable().optional(),
3613
+ fromVersion: z3.string().nullable().optional(),
3614
+ toVersion: z3.string().nullable().optional(),
3615
+ limit: z3.number().int().nullable().optional()
3340
3616
  }).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()
3617
+ var changelogEntryDetailSchema = z3.object({
3618
+ version: z3.string().nullable().optional(),
3619
+ normalizedVersion: z3.string().nullable().optional(),
3620
+ body: z3.string().nullable().optional(),
3621
+ htmlUrl: z3.string().nullable().optional(),
3622
+ publishedAt: z3.string().nullable().optional(),
3623
+ metadata: z3.unknown().nullable().optional()
3348
3624
  });
3349
- var changelogReportResponseSchema = z2.object({
3625
+ var changelogReportResponseSchema = z3.object({
3350
3626
  package: changelogPackageInfoSchema,
3351
- source: z2.string().nullable().optional(),
3352
- entries: z2.array(changelogEntryDetailSchema).nullable().optional()
3627
+ source: z3.string().nullable().optional(),
3628
+ entries: z3.array(changelogEntryDetailSchema).nullable().optional()
3353
3629
  });
3354
- var changelogGraphQLResponseSchema = z2.object({
3355
- data: z2.object({
3630
+ var changelogGraphQLResponseSchema = z3.object({
3631
+ data: z3.object({
3356
3632
  packageChangelog: changelogReportResponseSchema.nullable().optional()
3357
3633
  }).nullable().optional(),
3358
- errors: z2.array(graphQLErrorSchema2).optional()
3634
+ errors: z3.array(graphQLErrorSchema2).optional()
3359
3635
  });
3360
3636
  var PACKAGE_CHANGELOG_QUERY = `
3361
3637
  query PackageChangelog(
@@ -3395,72 +3671,72 @@ query PackageChangelog(
3395
3671
  }
3396
3672
  }
3397
3673
  }`;
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(),
3674
+ var packageDocSourceKindSchema = z3.enum(["CRAWLED", "REPOSITORY"]);
3675
+ var packageDocPageSummarySchema = z3.object({
3676
+ id: z3.string().nullable().optional(),
3677
+ title: z3.string().nullable().optional(),
3678
+ slug: z3.string().nullable().optional(),
3679
+ order: z3.number().int().nullable().optional(),
3680
+ linkName: z3.string().nullable().optional(),
3681
+ lastUpdatedAt: z3.string().nullable().optional(),
3406
3682
  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()
3683
+ sourceUrl: z3.string().nullable().optional(),
3684
+ repoUrl: z3.string().nullable().optional(),
3685
+ gitRef: z3.string().nullable().optional(),
3686
+ requestedRef: z3.string().nullable().optional(),
3687
+ filePath: z3.string().nullable().optional()
3412
3688
  });
3413
- var packageDocsPageInfoSchema = z2.object({
3414
- hasNextPage: z2.boolean(),
3415
- endCursor: z2.string().nullable().optional(),
3416
- totalCount: z2.number().int().nullable().optional()
3689
+ var packageDocsPageInfoSchema = z3.object({
3690
+ hasNextPage: z3.boolean(),
3691
+ endCursor: z3.string().nullable().optional(),
3692
+ totalCount: z3.number().int().nullable().optional()
3417
3693
  }).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(),
3694
+ var packageDocsListResponseSchema = z3.object({
3695
+ registry: z3.string().nullable().optional(),
3696
+ packageName: z3.string().nullable().optional(),
3697
+ version: z3.string().nullable().optional(),
3698
+ stale: z3.boolean().nullable().optional(),
3699
+ pages: z3.array(packageDocPageSummarySchema).nullable().optional(),
3424
3700
  pageInfo: packageDocsPageInfoSchema
3425
3701
  });
3426
- var packageDocSourceSchema = z2.object({
3427
- url: z2.string().nullable().optional(),
3428
- label: z2.string().nullable().optional()
3702
+ var packageDocSourceSchema = z3.object({
3703
+ url: z3.string().nullable().optional(),
3704
+ label: z3.string().nullable().optional()
3429
3705
  }).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(),
3706
+ var packageDocPageSchema = z3.object({
3707
+ id: z3.string().nullable().optional(),
3708
+ title: z3.string().nullable().optional(),
3709
+ content: z3.string().nullable().optional(),
3710
+ contentFormat: z3.string().nullable().optional(),
3711
+ breadcrumbs: z3.array(z3.string()).nullable().optional(),
3712
+ linkName: z3.string().nullable().optional(),
3713
+ lastUpdatedAt: z3.string().nullable().optional(),
3438
3714
  sourceKind: packageDocSourceKindSchema.nullable().optional(),
3439
3715
  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()
3716
+ repoUrl: z3.string().nullable().optional(),
3717
+ gitRef: z3.string().nullable().optional(),
3718
+ requestedRef: z3.string().nullable().optional(),
3719
+ filePath: z3.string().nullable().optional(),
3720
+ baseUrl: z3.string().nullable().optional()
3445
3721
  }).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(),
3722
+ var packageDocResultResponseSchema = z3.object({
3723
+ registry: z3.string().nullable().optional(),
3724
+ packageName: z3.string().nullable().optional(),
3725
+ version: z3.string().nullable().optional(),
3450
3726
  sourceKind: packageDocSourceKindSchema.nullable().optional(),
3451
3727
  page: packageDocPageSchema
3452
3728
  });
3453
- var packageDocsListGraphQLResponseSchema = z2.object({
3454
- data: z2.object({
3729
+ var packageDocsListGraphQLResponseSchema = z3.object({
3730
+ data: z3.object({
3455
3731
  listPackageDocs: packageDocsListResponseSchema.nullable().optional()
3456
3732
  }).nullable().optional(),
3457
- errors: z2.array(graphQLErrorSchema2).optional()
3733
+ errors: z3.array(graphQLErrorSchema2).optional()
3458
3734
  });
3459
- var packageDocReadGraphQLResponseSchema = z2.object({
3460
- data: z2.object({
3735
+ var packageDocReadGraphQLResponseSchema = z3.object({
3736
+ data: z3.object({
3461
3737
  getDocPage: packageDocResultResponseSchema.nullable().optional()
3462
3738
  }).nullable().optional(),
3463
- errors: z2.array(graphQLErrorSchema2).optional()
3739
+ errors: z3.array(graphQLErrorSchema2).optional()
3464
3740
  });
3465
3741
  var LIST_PACKAGE_DOCS_QUERY = `
3466
3742
  query ListPackageDocs(
@@ -3602,6 +3878,9 @@ class PackageIntelligenceServiceImpl {
3602
3878
  const extensions = getPrimaryExtensions2(errors);
3603
3879
  const code = typeof extensions?.code === "string" ? extensions.code : undefined;
3604
3880
  const retryable = typeof extensions?.retryable === "boolean" ? extensions.retryable : undefined;
3881
+ if (isClientUpdateRequiredGraphQLError({ message, code })) {
3882
+ return new ClientUpdateRequiredError;
3883
+ }
3605
3884
  switch (code) {
3606
3885
  case "NOT_FOUND":
3607
3886
  case "PACKAGE_NOT_FOUND":
@@ -3692,6 +3971,56 @@ class PackageIntelligenceServiceImpl {
3692
3971
  }));
3693
3972
  }
3694
3973
  async executePackageVulnerabilities(token, params) {
3974
+ let after = null;
3975
+ let firstPage;
3976
+ const entries = [];
3977
+ const seenCursors = new Set;
3978
+ do {
3979
+ const page = await this.fetchPackageVulnerabilitiesPage(token, params, after);
3980
+ if (!firstPage)
3981
+ firstPage = page;
3982
+ const advisoryPage = page.security?.advisories;
3983
+ if (!advisoryPage) {
3984
+ after = null;
3985
+ break;
3986
+ }
3987
+ entries.push(...advisoryPage.entries);
3988
+ if (advisoryPage.pageInfo.hasNextPage) {
3989
+ const nextCursor = advisoryPage.pageInfo.endCursor;
3990
+ if (!nextCursor) {
3991
+ throw new MalformedPackageIntelligenceResponseError("Vulnerability response pagination omitted next cursor.");
3992
+ }
3993
+ if (seenCursors.has(nextCursor)) {
3994
+ throw new MalformedPackageIntelligenceResponseError("Vulnerability response pagination repeated a cursor.");
3995
+ }
3996
+ seenCursors.add(nextCursor);
3997
+ after = nextCursor;
3998
+ } else {
3999
+ after = null;
4000
+ }
4001
+ } while (after !== null);
4002
+ if (!firstPage) {
4003
+ throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
4004
+ }
4005
+ if (firstPage.security) {
4006
+ const expectedCount = firstPage.security.advisories.pageInfo.totalCount;
4007
+ if (entries.length !== expectedCount) {
4008
+ throw new MalformedPackageIntelligenceResponseError("Vulnerability response pagination returned an incomplete advisory set.");
4009
+ }
4010
+ }
4011
+ const data = firstPage.security ? {
4012
+ ...firstPage,
4013
+ security: {
4014
+ ...firstPage.security,
4015
+ advisories: {
4016
+ ...firstPage.security.advisories,
4017
+ entries
4018
+ }
4019
+ }
4020
+ } : firstPage;
4021
+ return this.normaliseVulnerabilityReport(data);
4022
+ }
4023
+ async fetchPackageVulnerabilitiesPage(token, params, after) {
3695
4024
  let response;
3696
4025
  try {
3697
4026
  response = await postPkgseerGraphql({
@@ -3703,7 +4032,8 @@ class PackageIntelligenceServiceImpl {
3703
4032
  name: params.packageName,
3704
4033
  version: params.version,
3705
4034
  minSeverity: params.minSeverity,
3706
- includeWithdrawn: params.includeWithdrawn
4035
+ includeWithdrawn: params.includeWithdrawn,
4036
+ after
3707
4037
  },
3708
4038
  fetchFn: this.fetchFn
3709
4039
  });
@@ -3727,7 +4057,7 @@ class PackageIntelligenceServiceImpl {
3727
4057
  if (!data) {
3728
4058
  throw new MalformedPackageIntelligenceResponseError("Empty response from the package-intelligence service.");
3729
4059
  }
3730
- return this.normaliseVulnerabilityReport(data);
4060
+ return data;
3731
4061
  }
3732
4062
  normaliseVulnerabilityReport(data) {
3733
4063
  const name = data.package?.name ?? undefined;
@@ -3741,21 +4071,28 @@ class PackageIntelligenceServiceImpl {
3741
4071
  registry: data.package?.registry ?? undefined
3742
4072
  };
3743
4073
  const security = data.security ? {
3744
- vulnerabilityCount: data.security.vulnerabilityCount ?? undefined,
4074
+ affectedVulnerabilityCount: data.security.affectedVulnerabilityCount,
4075
+ nonAffectingVulnerabilityCount: data.security.nonAffectingVulnerabilityCount,
4076
+ allVulnerabilityCount: data.security.allVulnerabilityCount,
3745
4077
  currentVersionAffected: data.security.currentVersionAffected ?? undefined,
3746
- vulnerabilities: data.security.vulnerabilities?.map((vuln) => ({
4078
+ vulnerabilities: data.security.advisories.entries.map((vuln) => ({
3747
4079
  osvId: vuln.osvId ?? undefined,
3748
4080
  summary: vuln.summary ?? undefined,
3749
4081
  severityScore: vuln.severityScore ?? undefined,
3750
4082
  severityType: vuln.severityType ?? undefined,
3751
4083
  affectedVersionRanges: vuln.affectedVersionRanges ?? undefined,
4084
+ affectedVersionRangesCount: vuln.affectedVersionRangesCount,
4085
+ affectedVersionRangesTruncated: vuln.affectedVersionRangesTruncated,
3752
4086
  fixedInVersions: vuln.fixedInVersions ?? undefined,
3753
4087
  publishedAt: vuln.publishedAt ?? undefined,
3754
4088
  modifiedAt: vuln.modifiedAt ?? undefined,
3755
4089
  withdrawnAt: vuln.withdrawnAt ?? undefined,
3756
4090
  aliases: vuln.aliases ?? undefined,
3757
- isMalicious: vuln.isMalicious ?? undefined
3758
- })) ?? undefined,
4091
+ isMalicious: vuln.isMalicious ?? undefined,
4092
+ affectsInspectedVersion: vuln.affectsInspectedVersion,
4093
+ matchedAffectedVersionRanges: vuln.matchedAffectedVersionRanges,
4094
+ duplicateIds: vuln.duplicateIds
4095
+ })),
3759
4096
  upgradePaths: data.security.upgradePaths ?? undefined
3760
4097
  } : undefined;
3761
4098
  return {
@@ -4311,18 +4648,371 @@ class TokenManager {
4311
4648
  });
4312
4649
  }
4313
4650
  }
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.");
4651
+ // src/services/update-check-service.ts
4652
+ import semver from "semver";
4653
+ var NPM_DIST_TAGS_URL = "https://registry.npmjs.org/-/package/githits/dist-tags";
4654
+ var NPM_PACKAGE_VERSION_URL = "https://registry.npmjs.org/githits";
4655
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
4656
+ var FETCH_TIMEOUT_MS = 1000;
4657
+ var DIR_MODE2 = 448;
4658
+ var UPDATE_COMMAND = "npm i -g githits@latest";
4659
+ var MAX_DEPRECATION_REASON_LENGTH = 200;
4660
+
4661
+ class NpmRegistryUpdateCheckService {
4662
+ currentVersion;
4663
+ fs;
4664
+ fetcher;
4665
+ env;
4666
+ now;
4667
+ checkIntervalMs;
4668
+ fetchTimeoutMs;
4669
+ configDir;
4670
+ cachePath;
4671
+ constructor(options) {
4672
+ this.currentVersion = options.currentVersion;
4673
+ this.fs = options.fileSystemService;
4674
+ this.fetcher = options.fetcher ?? fetch;
4675
+ this.env = options.env ?? process.env;
4676
+ this.now = options.now ?? (() => new Date);
4677
+ this.checkIntervalMs = options.checkIntervalMs ?? CHECK_INTERVAL_MS;
4678
+ this.fetchTimeoutMs = options.fetchTimeoutMs ?? FETCH_TIMEOUT_MS;
4679
+ this.configDir = this.fs.joinPath(resolveConfigHome(this.env, this.fs), "githits");
4680
+ this.cachePath = this.fs.joinPath(this.configDir, "update-check.json");
4681
+ }
4682
+ async checkForUpdate(signal) {
4683
+ const cache = await this.loadCache();
4684
+ const latestDue = !cache || this.isCheckDue(cache);
4685
+ const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
4686
+ const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
4687
+ const currentVersionStatusChanged = currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus);
4688
+ if (cache && !latestDue) {
4689
+ if (currentVersionStatusChanged) {
4690
+ await this.saveCache({
4691
+ ...cache,
4692
+ currentVersionStatus
4693
+ });
4694
+ }
4695
+ return this.noticeFromLatest(cache.latestVersion);
4696
+ }
4697
+ const latestVersion = await this.fetchLatestVersion(signal);
4698
+ if (!latestVersion) {
4699
+ if (currentVersionStatusChanged) {
4700
+ await this.saveCache({
4701
+ ...cache,
4702
+ currentVersionStatus
4703
+ });
4704
+ }
4705
+ return this.noticeFromLatest(cache?.latestVersion);
4706
+ }
4707
+ await this.saveCache({
4708
+ checkedAt: this.now().toISOString(),
4709
+ latestVersion,
4710
+ currentVersionStatus
4325
4711
  });
4712
+ return this.noticeFromLatest(latestVersion);
4713
+ }
4714
+ async refreshRequiredUpdateStatus(signal) {
4715
+ const cache = await this.loadCache();
4716
+ const currentVersionStatusDue = this.isCurrentVersionStatusDue(cache?.currentVersionStatus);
4717
+ const currentVersionStatus = await this.refreshCurrentVersionStatusIfDue(cache?.currentVersionStatus, signal);
4718
+ if (currentVersionStatusDue && !sameCurrentVersionStatus(currentVersionStatus, cache?.currentVersionStatus)) {
4719
+ await this.saveCache({
4720
+ ...cache,
4721
+ currentVersionStatus
4722
+ });
4723
+ }
4724
+ }
4725
+ async getRequiredUpdateNotice() {
4726
+ const cache = await this.loadCache();
4727
+ const status = cache?.currentVersionStatus;
4728
+ if (!status || status.version !== this.currentVersion || !status.deprecatedReason) {
4729
+ return;
4730
+ }
4731
+ return {
4732
+ currentVersion: this.currentVersion,
4733
+ ...cache.latestVersion ? { latestKnownVersion: cache.latestVersion } : {},
4734
+ reason: status.deprecatedReason,
4735
+ updateCommand: formatUpdateCommand(this.env)
4736
+ };
4737
+ }
4738
+ noticeFromLatest(latestVersion) {
4739
+ if (!latestVersion || !semver.valid(latestVersion) || !semver.valid(this.currentVersion) || !semver.gt(latestVersion, this.currentVersion)) {
4740
+ return;
4741
+ }
4742
+ return {
4743
+ currentVersion: this.currentVersion,
4744
+ latestVersion,
4745
+ updateCommand: UPDATE_COMMAND
4746
+ };
4747
+ }
4748
+ isCheckDue(cache) {
4749
+ if (!cache.checkedAt || !cache.latestVersion) {
4750
+ return true;
4751
+ }
4752
+ const checkedAtMs = Date.parse(cache.checkedAt);
4753
+ if (Number.isNaN(checkedAtMs)) {
4754
+ return true;
4755
+ }
4756
+ return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
4757
+ }
4758
+ isCurrentVersionStatusDue(status) {
4759
+ if (!status || status.version !== this.currentVersion) {
4760
+ return true;
4761
+ }
4762
+ const checkedAtMs = Date.parse(status.checkedAt);
4763
+ if (Number.isNaN(checkedAtMs)) {
4764
+ return true;
4765
+ }
4766
+ return this.now().getTime() - checkedAtMs >= this.checkIntervalMs;
4767
+ }
4768
+ async refreshCurrentVersionStatusIfDue(cached, signal) {
4769
+ const reusableCached = cached?.version === this.currentVersion ? cached : undefined;
4770
+ if (!this.isCurrentVersionStatusDue(reusableCached)) {
4771
+ return reusableCached;
4772
+ }
4773
+ const remote = await this.fetchCurrentVersionStatus(signal);
4774
+ if (!remote) {
4775
+ return reusableCached;
4776
+ }
4777
+ return remote;
4778
+ }
4779
+ async fetchLatestVersion(signal) {
4780
+ try {
4781
+ const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
4782
+ const response = await this.fetcher(NPM_DIST_TAGS_URL, {
4783
+ signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
4784
+ });
4785
+ if (!response.ok) {
4786
+ return;
4787
+ }
4788
+ const body = await response.json();
4789
+ if (!body || typeof body !== "object" || typeof body.latest !== "string") {
4790
+ return;
4791
+ }
4792
+ const latest = body.latest;
4793
+ return semver.valid(latest) ? latest : undefined;
4794
+ } catch {
4795
+ return;
4796
+ }
4797
+ }
4798
+ async fetchCurrentVersionStatus(signal) {
4799
+ try {
4800
+ const timeoutSignal = AbortSignal.timeout(this.fetchTimeoutMs);
4801
+ const response = await this.fetcher(`${NPM_PACKAGE_VERSION_URL}/${this.currentVersion}`, {
4802
+ signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal
4803
+ });
4804
+ if (!response.ok) {
4805
+ return;
4806
+ }
4807
+ const body = await response.json();
4808
+ if (!body || typeof body !== "object") {
4809
+ return;
4810
+ }
4811
+ const rawDeprecated = body.deprecated;
4812
+ const deprecatedReason = typeof rawDeprecated === "string" ? sanitizeDeprecationReason(rawDeprecated) : undefined;
4813
+ return {
4814
+ version: this.currentVersion,
4815
+ checkedAt: this.now().toISOString(),
4816
+ ...deprecatedReason ? { deprecatedReason } : {}
4817
+ };
4818
+ } catch {
4819
+ return;
4820
+ }
4821
+ }
4822
+ async loadCache() {
4823
+ try {
4824
+ if (!await this.fs.exists(this.cachePath)) {
4825
+ return;
4826
+ }
4827
+ const raw = await this.fs.readFile(this.cachePath);
4828
+ const parsed = JSON.parse(raw);
4829
+ const currentVersionStatus = parseCurrentVersionStatus(parsed.currentVersionStatus);
4830
+ const hasLatest = typeof parsed.checkedAt === "string" && typeof parsed.latestVersion === "string";
4831
+ if (!hasLatest && !currentVersionStatus) {
4832
+ return;
4833
+ }
4834
+ return {
4835
+ ...hasLatest ? {
4836
+ checkedAt: parsed.checkedAt,
4837
+ latestVersion: parsed.latestVersion
4838
+ } : {},
4839
+ currentVersionStatus
4840
+ };
4841
+ } catch {
4842
+ return;
4843
+ }
4844
+ }
4845
+ async saveCache(cache) {
4846
+ try {
4847
+ await this.fs.ensureDir(this.configDir, DIR_MODE2);
4848
+ if (typeof this.fs.atomicWriteFile === "function") {
4849
+ await this.fs.atomicWriteFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
4850
+ `);
4851
+ return;
4852
+ }
4853
+ await this.fs.writeFile(this.cachePath, `${JSON.stringify(cache, null, 2)}
4854
+ `, 384);
4855
+ } catch {}
4856
+ }
4857
+ }
4858
+ function resolveConfigHome(env, fs) {
4859
+ const xdgConfigHome = env.XDG_CONFIG_HOME?.trim();
4860
+ if (xdgConfigHome) {
4861
+ return xdgConfigHome;
4862
+ }
4863
+ return fs.joinPath(fs.getHomeDir(), ".config");
4864
+ }
4865
+ function shouldRunUpdateCheck(input) {
4866
+ if (input.stderrIsTTY !== true) {
4867
+ return false;
4868
+ }
4869
+ const env = input.env ?? process.env;
4870
+ if (env.CI || env.GITHITS_DISABLE_UPDATE_CHECK) {
4871
+ return false;
4872
+ }
4873
+ if (isHelpOrVersionInvocation(input.args)) {
4874
+ return false;
4875
+ }
4876
+ if (isLikelyEphemeralPackageRunner(env)) {
4877
+ return false;
4878
+ }
4879
+ if (isMcpStdioInvocation(input.args, input.stdinIsTTY === true, input.stdoutIsTTY === true)) {
4880
+ return false;
4881
+ }
4882
+ return true;
4883
+ }
4884
+ function shouldRunRequiredUpdateEnforcement(input) {
4885
+ if (isHelpOrVersionInvocation(input.args)) {
4886
+ return false;
4887
+ }
4888
+ const env = input.env ?? process.env;
4889
+ if (isLikelyEphemeralPackageRunner(env)) {
4890
+ return false;
4891
+ }
4892
+ return true;
4893
+ }
4894
+ function formatUpdateNotice(notice) {
4895
+ return `Update available: githits ${notice.currentVersion} -> ${notice.latestVersion}
4896
+ Run: ${notice.updateCommand}`;
4897
+ }
4898
+ function formatRequiredUpdateNotice(notice) {
4899
+ const lines = [
4900
+ `Update required: ${notice.reason}`,
4901
+ "",
4902
+ `Installed githits ${notice.currentVersion} is no longer supported.`
4903
+ ];
4904
+ if (notice.latestKnownVersion) {
4905
+ lines.push(`Latest known version: ${notice.latestKnownVersion}`);
4906
+ }
4907
+ lines.push("Update with:", ` ${notice.updateCommand}`);
4908
+ return [...lines].join(`
4909
+ `);
4910
+ }
4911
+ function formatUpdateCommand(env = process.env) {
4912
+ const userAgent = env.npm_config_user_agent ?? "";
4913
+ const execPath = env.npm_execpath ?? "";
4914
+ const signal = `${userAgent} ${execPath}`.toLowerCase();
4915
+ if (signal.includes("pnpm")) {
4916
+ return "pnpm add -g githits@latest";
4917
+ }
4918
+ if (signal.includes("yarn")) {
4919
+ return "yarn global add githits@latest";
4920
+ }
4921
+ if (signal.includes("bun")) {
4922
+ return "bun add -g githits@latest";
4923
+ }
4924
+ return UPDATE_COMMAND;
4925
+ }
4926
+ function parseCurrentVersionStatus(value) {
4927
+ if (!value || typeof value !== "object") {
4928
+ return;
4929
+ }
4930
+ const status = value;
4931
+ if (typeof status.version !== "string") {
4932
+ return;
4933
+ }
4934
+ if (typeof status.checkedAt !== "string") {
4935
+ return;
4936
+ }
4937
+ const deprecatedReason = typeof status.deprecatedReason === "string" ? sanitizeDeprecationReason(status.deprecatedReason) : undefined;
4938
+ return {
4939
+ version: status.version,
4940
+ checkedAt: status.checkedAt,
4941
+ ...deprecatedReason ? { deprecatedReason } : {}
4942
+ };
4943
+ }
4944
+ function sameCurrentVersionStatus(left, right) {
4945
+ return left?.version === right?.version && left?.checkedAt === right?.checkedAt && left?.deprecatedReason === right?.deprecatedReason;
4946
+ }
4947
+ function sanitizeDeprecationReason(value) {
4948
+ const sanitized = Array.from(value).map((character) => isControlCharacter(character) ? " " : character).join("").replace(/\s+/g, " ").trim().slice(0, MAX_DEPRECATION_REASON_LENGTH).trim();
4949
+ return sanitized.length > 0 ? sanitized : undefined;
4950
+ }
4951
+ function isControlCharacter(value) {
4952
+ const code = value.charCodeAt(0);
4953
+ return code <= 31 || code >= 127 && code <= 159;
4954
+ }
4955
+ function isHelpOrVersionInvocation(args) {
4956
+ return args.length === 0 || args[0] === "help" || args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-V");
4957
+ }
4958
+ function isLikelyEphemeralPackageRunner(env) {
4959
+ const lifecycleEvent = env.npm_lifecycle_event;
4960
+ if (lifecycleEvent === "npx" || lifecycleEvent === "bunx") {
4961
+ return true;
4962
+ }
4963
+ const userAgent = env.npm_config_user_agent ?? "";
4964
+ return env.npm_command === "exec" && (userAgent.startsWith("npm/") || userAgent.startsWith("bun/"));
4965
+ }
4966
+ function isMcpStdioInvocation(args, stdinIsTTY, stdoutIsTTY) {
4967
+ const firstTokenIndex = args.findIndex((arg) => !arg.startsWith("-"));
4968
+ if (firstTokenIndex === -1 || args[firstTokenIndex] !== "mcp") {
4969
+ return false;
4970
+ }
4971
+ const remainingArgs = args.slice(firstTokenIndex + 1);
4972
+ return remainingArgs.includes("start") || !stdinIsTTY || !stdoutIsTTY;
4973
+ }
4974
+ // src/container.ts
4975
+ async function createAuthStorage(fileSystemService) {
4976
+ return withTelemetrySpan("container.create-auth-storage", async () => {
4977
+ const authConfig = await loadAuthConfig(fileSystemService);
4978
+ return createAuthStorageForMode(fileSystemService, authConfig.storage, authConfig.configPath);
4979
+ });
4980
+ }
4981
+ function createAuthStorageForMode(fileSystemService, mode, configPath = "your GitHits config.toml") {
4982
+ const fileStorage = new ModeAwareFileAuthStorage(new AuthStorageImpl(fileSystemService, getAuthFileStorageDir(fileSystemService)), mode, configPath);
4983
+ const legacyStorage = new AuthStorageImpl(fileSystemService, getLegacyAuthStorageDir(fileSystemService));
4984
+ const rawKeyring = new KeyringServiceImpl;
4985
+ const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
4986
+ const keychainStorage = new KeychainAuthStorage(keyring);
4987
+ return new MigratingAuthStorage(keychainStorage, fileStorage, legacyStorage, mode, configPath, (message) => console.error(message));
4988
+ }
4989
+ async function createAuthCommandDependencies() {
4990
+ return withTelemetrySpan("container.create-auth-command", async () => {
4991
+ const fileSystemService = new FileSystemServiceImpl;
4992
+ return {
4993
+ authStorage: await createAuthStorage(fileSystemService),
4994
+ authService: new AuthServiceImpl,
4995
+ browserService: new BrowserServiceImpl,
4996
+ fileSystemService,
4997
+ mcpUrl: getMcpUrl(),
4998
+ apiUrl: getApiUrl(),
4999
+ envApiToken: getEnvApiToken()
5000
+ };
5001
+ });
5002
+ }
5003
+ async function createAuthStatusDependencies() {
5004
+ return withTelemetrySpan("container.create-auth-status", async () => {
5005
+ const fileSystemService = new FileSystemServiceImpl;
5006
+ const envApiToken = getEnvApiToken();
5007
+ return {
5008
+ authStorage: envApiToken ? createAuthStorageForMode(fileSystemService, "keychain") : await createAuthStorage(fileSystemService),
5009
+ authService: new AuthServiceImpl,
5010
+ browserService: new BrowserServiceImpl,
5011
+ fileSystemService,
5012
+ mcpUrl: getMcpUrl(),
5013
+ apiUrl: getApiUrl(),
5014
+ envApiToken
5015
+ };
4326
5016
  });
4327
5017
  }
4328
5018
  function createStaticTokenProvider(token) {
@@ -4338,18 +5028,17 @@ async function createContainer() {
4338
5028
  const mcpUrl = getMcpUrl();
4339
5029
  const apiUrl = getApiUrl();
4340
5030
  const codeNavigationUrl = getCodeNavigationUrl();
4341
- const codeNavigationCliOverrideEnabled = isCodeNavigationCliOverrideEnabled();
4342
5031
  const fileSystemService = new FileSystemServiceImpl;
4343
- const authStorage = createAuthStorage(fileSystemService);
4344
5032
  const authService = new AuthServiceImpl;
4345
5033
  const browserService = new BrowserServiceImpl;
4346
5034
  const envToken = getEnvApiToken();
4347
5035
  if (envToken) {
5036
+ const authStorage2 = createAuthStorageForMode(fileSystemService, "keychain");
4348
5037
  const tokenProvider = createStaticTokenProvider(envToken);
4349
- const codeNavigationService2 = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
4350
- const packageIntelligenceService2 = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
5038
+ const codeNavigationService2 = new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider);
5039
+ const packageIntelligenceService2 = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider);
4351
5040
  return {
4352
- authStorage,
5041
+ authStorage: authStorage2,
4353
5042
  authService,
4354
5043
  browserService,
4355
5044
  fileSystemService,
@@ -4358,18 +5047,17 @@ async function createContainer() {
4358
5047
  apiToken: envToken,
4359
5048
  hasValidToken: true,
4360
5049
  envApiToken: envToken,
4361
- codeNavigationCapability: getCodeNavigationCapability(envToken),
4362
- codeNavigationCliOverrideEnabled,
4363
5050
  codeNavigationUrl,
4364
5051
  codeNavigationService: codeNavigationService2,
4365
5052
  packageIntelligenceService: packageIntelligenceService2,
4366
5053
  githitsService: new GitHitsServiceImpl(apiUrl, envToken)
4367
5054
  };
4368
5055
  }
5056
+ const authStorage = await createAuthStorage(fileSystemService);
4369
5057
  const tokenManager = new TokenManager({ authService, authStorage, mcpUrl });
4370
5058
  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;
5059
+ const codeNavigationService = new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager);
5060
+ const packageIntelligenceService = new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenManager);
4373
5061
  return {
4374
5062
  authStorage,
4375
5063
  authService,
@@ -4380,8 +5068,6 @@ async function createContainer() {
4380
5068
  apiToken,
4381
5069
  hasValidToken: apiToken !== undefined,
4382
5070
  envApiToken: undefined,
4383
- codeNavigationCapability: getCodeNavigationCapability(apiToken),
4384
- codeNavigationCliOverrideEnabled,
4385
5071
  codeNavigationUrl,
4386
5072
  codeNavigationService,
4387
5073
  packageIntelligenceService,
@@ -4389,48 +5075,5 @@ async function createContainer() {
4389
5075
  };
4390
5076
  });
4391
5077
  }
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
5078
 
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 };
5079
+ 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, AuthStoragePolicyError, PackageIntelligenceAccessError, PackageIntelligenceFeatureFlagRequiredError, PackageIntelligenceNetworkError, PackageIntelligenceBackendError, PackageIntelligenceGraphQLError, PackageIntelligenceTargetNotFoundError, PackageIntelligenceValidationError, PackageIntelligenceVersionNotFoundError, MalformedPackageIntelligenceResponseError, PackageIntelligenceChangelogSourceNotFoundError, PromptServiceImpl, refreshExpiredToken, NpmRegistryUpdateCheckService, shouldRunUpdateCheck, shouldRunRequiredUpdateEnforcement, formatUpdateNotice, formatRequiredUpdateNotice, createAuthCommandDependencies, createAuthStatusDependencies, createContainer };