doc2vec 2.11.0 → 2.12.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.
package/dist/doc2vec.js CHANGED
@@ -53,6 +53,7 @@ const logger_1 = require("./logger");
53
53
  const utils_1 = require("./utils");
54
54
  const database_1 = require("./database");
55
55
  const content_processor_1 = require("./content-processor");
56
+ const types_1 = require("./types");
56
57
  const client_s3_1 = require("@aws-sdk/client-s3");
57
58
  const markdown_store_1 = require("./markdown-store");
58
59
  const GITHUB_TOKEN = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
@@ -61,6 +62,9 @@ dotenv.config();
61
62
  class Doc2Vec {
62
63
  constructor(configPath) {
63
64
  this.brokenLinksByWebsite = {};
65
+ // Change counters for the source currently being processed by run().
66
+ // Sources are processed sequentially, so a single slot is sufficient.
67
+ this.counters = (0, types_1.newSourceRunCounters)('items');
64
68
  this.logger = new logger_1.Logger('Doc2Vec', {
65
69
  level: logger_1.LogLevel.DEBUG,
66
70
  useTimestamp: true,
@@ -183,6 +187,15 @@ class Doc2Vec {
183
187
  const startTime = Date.now();
184
188
  let ok = true;
185
189
  let errorMessage;
190
+ const itemsKindByType = {
191
+ website: 'pages',
192
+ github: 'issues',
193
+ local_directory: 'files',
194
+ code: 'files',
195
+ s3: 'objects',
196
+ zendesk: 'items',
197
+ };
198
+ this.counters = (0, types_1.newSourceRunCounters)(itemsKindByType[sourceConfig.type] ?? 'items');
186
199
  try {
187
200
  if (sourceConfig.type === 'github') {
188
201
  await this.processGithubRepo(sourceConfig, sourceLogger);
@@ -219,7 +232,8 @@ class Doc2Vec {
219
232
  version: sourceConfig.version,
220
233
  duration_ms: Date.now() - startTime,
221
234
  ok,
222
- ...(errorMessage && { error: errorMessage })
235
+ ...(errorMessage && { error: errorMessage }),
236
+ counters: this.counters
223
237
  });
224
238
  }
225
239
  // Close the Postgres markdown store connection pool
@@ -380,11 +394,19 @@ class Doc2Vec {
380
394
  // ones would otherwise linger — leaving stale state ("open") and missing
381
395
  // the latest comments in search results. All chunks for an issue share
382
396
  // its unique url, so delete-by-url reconciles them exactly.
397
+ let removedChunks = 0;
383
398
  if (dbConnection.type === 'sqlite') {
384
- database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
399
+ removedChunks = database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
385
400
  }
386
401
  else if (dbConnection.type === 'qdrant') {
387
- await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
402
+ removedChunks = await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
403
+ }
404
+ if (removedChunks > 0) {
405
+ this.counters.items_updated++;
406
+ this.counters.chunks_deleted += removedChunks;
407
+ }
408
+ else {
409
+ this.counters.items_new++;
388
410
  }
389
411
  // Process and store each chunk immediately
390
412
  for (const chunk of chunks) {
@@ -400,6 +422,7 @@ class Doc2Vec {
400
422
  const embeddings = await this.createEmbeddings([chunk.content]);
401
423
  if (embeddings.length) {
402
424
  database_1.DatabaseManager.insertVectorsSQLite(dbConnection.db, chunk, embeddings[0], logger, chunkHash);
425
+ this.counters.chunks_added++;
403
426
  logger.debug(`Stored chunk ${chunkId} in SQLite`);
404
427
  }
405
428
  else {
@@ -430,6 +453,7 @@ class Doc2Vec {
430
453
  const embeddings = await this.createEmbeddings([chunk.content]);
431
454
  if (embeddings.length) {
432
455
  await database_1.DatabaseManager.storeChunkInQdrant(dbConnection, chunk, embeddings[0], chunkHash);
456
+ this.counters.chunks_added++;
433
457
  logger.debug(`Stored chunk ${chunkId} in Qdrant (${dbConnection.collectionName})`);
434
458
  }
435
459
  else {
@@ -579,11 +603,16 @@ class Doc2Vec {
579
603
  }
580
604
  // Vector DB chunks
581
605
  try {
606
+ let removedChunks = 0;
582
607
  if (dbConnection.type === 'sqlite') {
583
- database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
608
+ removedChunks = database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
584
609
  }
585
610
  else if (dbConnection.type === 'qdrant') {
586
- await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
611
+ removedChunks = await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
612
+ }
613
+ if (removedChunks > 0) {
614
+ this.counters.items_deleted++;
615
+ this.counters.chunks_deleted += removedChunks;
587
616
  }
588
617
  }
589
618
  catch (dbError) {
@@ -603,14 +632,17 @@ class Doc2Vec {
603
632
  await database_1.DatabaseManager.setMetadataValue(dbConnection, syncCompleteKey, 'true', logger, this.embeddingDimension);
604
633
  logger.info('Full sync completed successfully — subsequent runs will use normal caching');
605
634
  }
635
+ let removed = { items: 0, chunks: 0 };
606
636
  if (dbConnection.type === 'sqlite') {
607
637
  logger.info(`Running SQLite cleanup for ${urlPrefix}`);
608
- database_1.DatabaseManager.removeObsoleteChunksSQLite(dbConnection.db, visitedUrls, urlPrefix, logger);
638
+ removed = database_1.DatabaseManager.removeObsoleteChunksSQLite(dbConnection.db, visitedUrls, urlPrefix, logger);
609
639
  }
610
640
  else if (dbConnection.type === 'qdrant') {
611
641
  logger.info(`Running Qdrant cleanup for ${urlPrefix} in collection ${dbConnection.collectionName}`);
612
- await database_1.DatabaseManager.removeObsoleteChunksQdrant(dbConnection, visitedUrls, urlPrefix, logger);
642
+ removed = await database_1.DatabaseManager.removeObsoleteChunksQdrant(dbConnection, visitedUrls, urlPrefix, logger);
613
643
  }
644
+ this.counters.items_deleted += removed.items;
645
+ this.counters.chunks_deleted += removed.chunks;
614
646
  }
615
647
  logger.info(`Finished processing website: ${config.url}`);
616
648
  }
@@ -698,14 +730,17 @@ class Doc2Vec {
698
730
  }
699
731
  }, logger);
700
732
  logger.section('CLEANUP');
733
+ let removed = { items: 0, chunks: 0 };
701
734
  if (dbConnection.type === 'sqlite') {
702
735
  logger.info(`Running SQLite cleanup for local directory ${config.path}`);
703
- database_1.DatabaseManager.removeObsoleteFilesSQLite(dbConnection.db, processedFiles, config, logger);
736
+ removed = database_1.DatabaseManager.removeObsoleteFilesSQLite(dbConnection.db, processedFiles, config, logger);
704
737
  }
705
738
  else if (dbConnection.type === 'qdrant') {
706
739
  logger.info(`Running Qdrant cleanup for local directory ${config.path} in collection ${dbConnection.collectionName}`);
707
- await database_1.DatabaseManager.removeObsoleteFilesQdrant(dbConnection, processedFiles, config, logger);
740
+ removed = await database_1.DatabaseManager.removeObsoleteFilesQdrant(dbConnection, processedFiles, config, logger);
708
741
  }
742
+ this.counters.items_deleted += removed.items;
743
+ this.counters.chunks_deleted += removed.chunks;
709
744
  logger.info(`Finished processing local directory: ${config.path}`);
710
745
  }
711
746
  async processS3(config, parentLogger) {
@@ -886,11 +921,16 @@ class Doc2Vec {
886
921
  else {
887
922
  fileUrl = `s3://${config.bucket}/${deletedKey}`;
888
923
  }
924
+ let removedChunks = 0;
889
925
  if (dbConnection.type === 'sqlite') {
890
- database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, fileUrl, logger);
926
+ removedChunks = database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, fileUrl, logger);
891
927
  }
892
928
  else if (dbConnection.type === 'qdrant') {
893
- await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, fileUrl, logger);
929
+ removedChunks = await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, fileUrl, logger);
930
+ }
931
+ if (removedChunks > 0) {
932
+ this.counters.items_deleted++;
933
+ this.counters.chunks_deleted += removedChunks;
894
934
  }
895
935
  }
896
936
  }
@@ -1025,11 +1065,16 @@ class Doc2Vec {
1025
1065
  if (deleteUrls.length > 0) {
1026
1066
  logger.info(`Cleaning up ${deleteUrls.length} deleted/renamed files`);
1027
1067
  for (const url of deleteUrls) {
1068
+ let removedChunks = 0;
1028
1069
  if (dbConnection.type === 'sqlite') {
1029
- database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
1070
+ removedChunks = database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
1030
1071
  }
1031
1072
  else if (dbConnection.type === 'qdrant') {
1032
- await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
1073
+ removedChunks = await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
1074
+ }
1075
+ if (removedChunks > 0) {
1076
+ this.counters.items_deleted++;
1077
+ this.counters.chunks_deleted += removedChunks;
1033
1078
  }
1034
1079
  }
1035
1080
  }
@@ -1043,11 +1088,16 @@ class Doc2Vec {
1043
1088
  const deletedFiles = previousList.filter((filePath) => !trackedFiles?.has(filePath));
1044
1089
  for (const deletedFile of deletedFiles) {
1045
1090
  const url = this.buildCodeFileUrl(deletedFile, basePath, config, repoUrlPrefix);
1091
+ let removedChunks = 0;
1046
1092
  if (dbConnection.type === 'sqlite') {
1047
- database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
1093
+ removedChunks = database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
1048
1094
  }
1049
1095
  else if (dbConnection.type === 'qdrant') {
1050
- await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
1096
+ removedChunks = await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
1097
+ }
1098
+ if (removedChunks > 0) {
1099
+ this.counters.items_deleted++;
1100
+ this.counters.chunks_deleted += removedChunks;
1051
1101
  }
1052
1102
  }
1053
1103
  await database_1.DatabaseManager.setMetadataValue(dbConnection, fileListKey, JSON.stringify(currentList), logger, this.embeddingDimension);
@@ -1058,14 +1108,17 @@ class Doc2Vec {
1058
1108
  }
1059
1109
  }
1060
1110
  else {
1111
+ let removed = { items: 0, chunks: 0 };
1061
1112
  if (dbConnection.type === 'sqlite') {
1062
1113
  logger.info(`Running SQLite cleanup for code source ${basePath}`);
1063
- database_1.DatabaseManager.removeObsoleteFilesSQLite(dbConnection.db, processedFiles, cleanupPathConfig, logger);
1114
+ removed = database_1.DatabaseManager.removeObsoleteFilesSQLite(dbConnection.db, processedFiles, cleanupPathConfig, logger);
1064
1115
  }
1065
1116
  else if (dbConnection.type === 'qdrant') {
1066
1117
  logger.info(`Running Qdrant cleanup for code source ${basePath} in collection ${dbConnection.collectionName}`);
1067
- await database_1.DatabaseManager.removeObsoleteFilesQdrant(dbConnection, processedFiles, cleanupPathConfig, logger);
1118
+ removed = await database_1.DatabaseManager.removeObsoleteFilesQdrant(dbConnection, processedFiles, cleanupPathConfig, logger);
1068
1119
  }
1120
+ this.counters.items_deleted += removed.items;
1121
+ this.counters.chunks_deleted += removed.chunks;
1069
1122
  }
1070
1123
  if (config.source === 'github' && basePath && repoBranch) {
1071
1124
  const headSha = await this.getRepoHeadSha(basePath, logger);
@@ -1333,11 +1386,16 @@ class Doc2Vec {
1333
1386
  // Deleted tickets — remove their chunks and stop
1334
1387
  if (ticket.status === 'deleted') {
1335
1388
  logger.info(`Ticket #${ticketId} was deleted in Zendesk — removing its chunks`);
1389
+ let removedChunks = 0;
1336
1390
  if (dbConnection.type === 'sqlite') {
1337
- database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
1391
+ removedChunks = database_1.DatabaseManager.removeChunksByUrlSQLite(dbConnection.db, url, logger);
1338
1392
  }
1339
1393
  else {
1340
- await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
1394
+ removedChunks = await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
1395
+ }
1396
+ if (removedChunks > 0) {
1397
+ this.counters.items_deleted++;
1398
+ this.counters.chunks_deleted += removedChunks;
1341
1399
  }
1342
1400
  return;
1343
1401
  }
@@ -1599,6 +1657,7 @@ class Doc2Vec {
1599
1657
  newHashesSorted.every((h, i) => h === existingHashesSorted[i]);
1600
1658
  if (unchanged) {
1601
1659
  logger.info(`Skipping unchanged URL (${chunks.length} chunks): ${url}`);
1660
+ this.counters.items_unchanged++;
1602
1661
  return 0;
1603
1662
  }
1604
1663
  // 4. Content changed — delete all existing chunks for this URL
@@ -1610,6 +1669,11 @@ class Doc2Vec {
1610
1669
  else {
1611
1670
  await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
1612
1671
  }
1672
+ this.counters.items_updated++;
1673
+ this.counters.chunks_deleted += existingHashesSorted.length;
1674
+ }
1675
+ else {
1676
+ this.counters.items_new++;
1613
1677
  }
1614
1678
  // 5. Embed and insert all new chunks
1615
1679
  let embeddedCount = 0;
@@ -1637,6 +1701,7 @@ class Doc2Vec {
1637
1701
  }
1638
1702
  }
1639
1703
  chunkProgress.complete();
1704
+ this.counters.chunks_added += embeddedCount;
1640
1705
  return embeddedCount;
1641
1706
  }
1642
1707
  async createEmbeddings(texts) {
package/dist/types.js CHANGED
@@ -1,2 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.newSourceRunCounters = newSourceRunCounters;
4
+ function newSourceRunCounters(itemsKind) {
5
+ return {
6
+ items_kind: itemsKind,
7
+ items_new: 0,
8
+ items_updated: 0,
9
+ items_unchanged: 0,
10
+ items_deleted: 0,
11
+ chunks_added: 0,
12
+ chunks_deleted: 0,
13
+ };
14
+ }
@@ -0,0 +1 @@
1
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-md:28rem;--container-2xl:42rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:system-ui, -apple-system, "Segoe UI", sans-serif;--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-2{height:calc(var(--spacing) * 2)}.h-4{height:calc(var(--spacing) * 4)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[560px\]{max-height:560px}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing) * 2)}.w-4{width:calc(var(--spacing) * 4)}.w-10{width:calc(var(--spacing) * 10)}.w-44{width:calc(var(--spacing) * 44)}.w-72{width:calc(var(--spacing) * 72)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-md{max-width:var(--container-md)}.max-w-xs{max-width:var(--container-xs)}.min-w-48{min-width:calc(var(--spacing) * 48)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.cursor-pointer{cursor:pointer}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-x-6{column-gap:calc(var(--spacing) * 6)}.gap-x-8{column-gap:calc(var(--spacing) * 8)}.gap-y-1{row-gap:var(--spacing)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}.gap-y-3{row-gap:calc(var(--spacing) * 3)}.self-center{align-self:center}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent,.border-accent\/30{border-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.border-accent\/30{border-color:color-mix(in oklab,var(--accent) 30%,transparent)}}.border-accent\/40{border-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.border-accent\/40{border-color:color-mix(in oklab,var(--accent) 40%,transparent)}}.border-critical\/40{border-color:var(--status-critical)}@supports (color:color-mix(in lab,red,red)){.border-critical\/40{border-color:color-mix(in oklab,var(--status-critical) 40%,transparent)}}.border-critical\/50{border-color:var(--status-critical)}@supports (color:color-mix(in lab,red,red)){.border-critical\/50{border-color:color-mix(in oklab,var(--status-critical) 50%,transparent)}}.border-current{border-color:currentColor}.border-edge,.border-edge\/60{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-edge\/60{border-color:color-mix(in oklab,var(--border) 60%,transparent)}}.border-good\/40{border-color:var(--status-good)}@supports (color:color-mix(in lab,red,red)){.border-good\/40{border-color:color-mix(in oklab,var(--status-good) 40%,transparent)}}.border-transparent{border-color:#0000}.border-warning\/40{border-color:var(--status-warning)}@supports (color:color-mix(in lab,red,red)){.border-warning\/40{border-color:color-mix(in oklab,var(--status-warning) 40%,transparent)}}.border-warning\/50{border-color:var(--status-warning)}@supports (color:color-mix(in lab,red,red)){.border-warning\/50{border-color:color-mix(in oklab,var(--status-warning) 50%,transparent)}}.bg-accent{background-color:var(--accent)}.bg-edge\/10{background-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.bg-edge\/10{background-color:color-mix(in oklab,var(--border) 10%,transparent)}}.bg-page{background-color:var(--page)}.bg-surface{background-color:var(--surface-1)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--accent)}.text-critical{color:var(--status-critical)}.text-good-text{color:var(--good-text)}.text-ink{color:var(--text-primary)}.text-ink-muted{color:var(--text-muted)}.text-ink-secondary{color:var(--text-secondary)}.text-warning{color:var(--status-warning)}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline-offset-2{text-underline-offset:2px}.accent-\(--accent\){accent-color:var(--accent)}.opacity-30{opacity:.3}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.placeholder\:text-ink-muted\/60::placeholder{color:var(--text-muted)}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-ink-muted\/60::placeholder{color:color-mix(in oklab,var(--text-muted) 60%,transparent)}}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media(hover:hover){.hover\:border-accent:hover,.hover\:border-accent\/50:hover{border-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/50:hover{border-color:color-mix(in oklab,var(--accent) 50%,transparent)}}.hover\:bg-critical\/10:hover{background-color:var(--status-critical)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-critical\/10:hover{background-color:color-mix(in oklab,var(--status-critical) 10%,transparent)}}.hover\:bg-edge\/20:hover{background-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-edge\/20:hover{background-color:color-mix(in oklab,var(--border) 20%,transparent)}}.hover\:bg-ink\/\[0\.03\]:hover{background-color:var(--text-primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-ink\/\[0\.03\]:hover{background-color:color-mix(in oklab,var(--text-primary) 3%,transparent)}}.hover\:text-accent:hover{color:var(--accent)}.hover\:text-critical:hover{color:var(--status-critical)}.hover\:text-ink-secondary:hover{color:var(--text-secondary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-accent:focus{border-color:var(--accent)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}@media(min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:48rem){.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:64rem){.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}}:root{--page:#f9f9f7;--surface-1:#fcfcfb;--text-primary:#0b0b0b;--text-secondary:#52514e;--text-muted:#898781;--gridline:#e1e0d9;--baseline:#c3c2b7;--border:#0b0b0b1a;--series-1:#2a78d6;--status-good:#0ca30c;--status-warning:#fab219;--status-critical:#d03b3b;--good-text:#006300;--accent:#2a78d6}@media(prefers-color-scheme:dark){:root{--page:#0d0d0d;--surface-1:#1a1a19;--text-primary:#fff;--text-secondary:#c3c2b7;--text-muted:#898781;--gridline:#2c2c2a;--baseline:#383835;--border:#ffffff1a;--series-1:#3987e5;--status-good:#0ca30c;--status-warning:#fab219;--status-critical:#d03b3b;--good-text:#0ca30c;--accent:#3987e5}}html{background:var(--page);color:var(--text-primary);font-family:system-ui,-apple-system,Segoe UI,sans-serif}.log-scroll::-webkit-scrollbar{width:10px;height:10px}.log-scroll::-webkit-scrollbar-thumb{background:var(--baseline);border-radius:5px}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@keyframes pulse{50%{opacity:.5}}