@customize-agent/knowledge 1.0.1 → 2.0.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.
@@ -29,6 +29,7 @@ export interface MinHashRecord {
29
29
  filePath: string;
30
30
  signature: number[];
31
31
  shingleCount: number;
32
+ buckets: string[];
32
33
  createdAt: number;
33
34
  }
34
35
  export interface FileRelationship {
@@ -64,6 +65,7 @@ export declare class IndexStateStore {
64
65
  upsertFileHash(record: Omit<FileHashRecord, 'createdAt' | 'updatedAt'>): void;
65
66
  upsertMinHash(record: Omit<MinHashRecord, 'createdAt'>): void;
66
67
  listMinHashes(excludePath?: string): MinHashRecord[];
68
+ listMinHashesByBuckets(buckets: string[], excludePath?: string): MinHashRecord[];
67
69
  addRelationship(relationship: Omit<FileRelationship, 'id' | 'createdAt'>): void;
68
70
  listRelationships(filePath?: string): FileRelationship[];
69
71
  setTags(relativePath: string, tags: string[]): void;
@@ -50,7 +50,7 @@ export class IndexStateStore {
50
50
  listRecords() {
51
51
  const rows = this.db.prepare(`
52
52
  SELECT * FROM kb_index_state
53
- WHERE status = 'active'
53
+ WHERE status != 'deleted'
54
54
  ORDER BY category, relative_path
55
55
  `).all();
56
56
  return rows.map(row => this.rowToRecord(row));
@@ -140,14 +140,22 @@ export class IndexStateStore {
140
140
  `).run(record.contentHash, record.filePath, record.fileSize, record.category, record.normalizedHash ?? null, now, now);
141
141
  }
142
142
  upsertMinHash(record) {
143
- this.db.prepare(`
144
- INSERT INTO kb_minhash (file_path, signature, shingle_count, created_at)
145
- VALUES (?, ?, ?, ?)
146
- ON CONFLICT(file_path) DO UPDATE SET
147
- signature = excluded.signature,
148
- shingle_count = excluded.shingle_count,
149
- created_at = excluded.created_at
150
- `).run(record.filePath, Buffer.from(JSON.stringify(record.signature), 'utf8'), record.shingleCount, Date.now());
143
+ const now = Date.now();
144
+ const tx = this.db.transaction(() => {
145
+ this.db.prepare(`
146
+ INSERT INTO kb_minhash (file_path, signature, shingle_count, created_at)
147
+ VALUES (?, ?, ?, ?)
148
+ ON CONFLICT(file_path) DO UPDATE SET
149
+ signature = excluded.signature,
150
+ shingle_count = excluded.shingle_count,
151
+ created_at = excluded.created_at
152
+ `).run(record.filePath, Buffer.from(JSON.stringify(record.signature), 'utf8'), record.shingleCount, now);
153
+ this.db.prepare('DELETE FROM kb_lsh_buckets WHERE file_path = ?').run(record.filePath);
154
+ const insertBucket = this.db.prepare('INSERT OR IGNORE INTO kb_lsh_buckets (bucket_key, file_path, created_at) VALUES (?, ?, ?)');
155
+ for (const bucket of record.buckets)
156
+ insertBucket.run(bucket, record.filePath, now);
157
+ });
158
+ tx();
151
159
  }
152
160
  listMinHashes(excludePath) {
153
161
  const rows = excludePath
@@ -155,6 +163,24 @@ export class IndexStateStore {
155
163
  : this.db.prepare('SELECT * FROM kb_minhash').all();
156
164
  return rows.map(row => this.rowToMinHash(row));
157
165
  }
166
+ listMinHashesByBuckets(buckets, excludePath) {
167
+ if (buckets.length === 0)
168
+ return [];
169
+ const placeholders = buckets.map(() => '?').join(',');
170
+ const params = [...buckets];
171
+ let sql = `
172
+ SELECT DISTINCT m.*
173
+ FROM kb_minhash m
174
+ INNER JOIN kb_lsh_buckets b ON b.file_path = m.file_path
175
+ WHERE b.bucket_key IN (${placeholders})
176
+ `;
177
+ if (excludePath) {
178
+ sql += ' AND m.file_path != ?';
179
+ params.push(excludePath);
180
+ }
181
+ const rows = this.db.prepare(sql).all(...params);
182
+ return rows.map(row => this.rowToMinHash(row));
183
+ }
158
184
  addRelationship(relationship) {
159
185
  this.db.prepare(`
160
186
  INSERT INTO kb_relationships (
@@ -209,6 +235,7 @@ export class IndexStateStore {
209
235
  this.db.prepare('DELETE FROM kb_index_state WHERE relative_path = ?').run(relativePath);
210
236
  this.db.prepare('DELETE FROM kb_file_hashes WHERE file_path = ?').run(relativePath);
211
237
  this.db.prepare('DELETE FROM kb_minhash WHERE file_path = ?').run(relativePath);
238
+ this.db.prepare('DELETE FROM kb_lsh_buckets WHERE file_path = ?').run(relativePath);
212
239
  this.db.prepare('DELETE FROM kb_tags WHERE file_path = ?').run(relativePath);
213
240
  this.db.prepare('DELETE FROM kb_relationships WHERE source_file = ? OR target_file = ?').run(relativePath, relativePath);
214
241
  }
@@ -222,11 +249,11 @@ export class IndexStateStore {
222
249
  const stats = this.db.prepare(`
223
250
  SELECT
224
251
  COUNT(*) as file_count,
225
- COALESCE(SUM(chunk_count), 0) as chunk_count,
252
+ (SELECT COUNT(*) FROM kb_chunks) as chunk_count,
226
253
  COALESCE(SUM(file_size), 0) as total_size_bytes,
227
254
  COALESCE(MAX(indexed_at), 0) as last_indexed_at
228
255
  FROM kb_index_state
229
- WHERE status = 'active'
256
+ WHERE status != 'deleted'
230
257
  `).get();
231
258
  return {
232
259
  fileCount: Number(stats.file_count ?? 0),
@@ -301,6 +328,15 @@ export class IndexStateStore {
301
328
  created_at INTEGER NOT NULL
302
329
  );
303
330
 
331
+ CREATE TABLE IF NOT EXISTS kb_lsh_buckets (
332
+ bucket_key TEXT NOT NULL,
333
+ file_path TEXT NOT NULL,
334
+ created_at INTEGER NOT NULL,
335
+ PRIMARY KEY (bucket_key, file_path)
336
+ );
337
+ CREATE INDEX IF NOT EXISTS idx_lsh_bucket ON kb_lsh_buckets(bucket_key);
338
+ CREATE INDEX IF NOT EXISTS idx_lsh_file ON kb_lsh_buckets(file_path);
339
+
304
340
  CREATE TABLE IF NOT EXISTS kb_relationships (
305
341
  id INTEGER PRIMARY KEY AUTOINCREMENT,
306
342
  source_file TEXT NOT NULL,
@@ -341,10 +377,13 @@ export class IndexStateStore {
341
377
  rowToMinHash(row) {
342
378
  const raw = row.signature;
343
379
  const json = Buffer.isBuffer(raw) ? raw.toString('utf8') : String(raw);
380
+ const filePath = String(row.file_path);
381
+ const bucketRows = this.db.prepare('SELECT bucket_key FROM kb_lsh_buckets WHERE file_path = ?').all(filePath);
344
382
  return {
345
- filePath: String(row.file_path),
383
+ filePath,
346
384
  signature: JSON.parse(json),
347
385
  shingleCount: Number(row.shingle_count),
386
+ buckets: bucketRows.map(bucket => bucket.bucket_key),
348
387
  createdAt: Number(row.created_at),
349
388
  };
350
389
  }
@@ -45,6 +45,7 @@ export declare class KnowledgeBaseManager {
45
45
  listRelationships(filePath?: string): FileRelationship[];
46
46
  listFiles(): IndexStateRecord[];
47
47
  addFile(sourcePath: string, targetRelativePath?: string): Promise<DiffResult>;
48
+ getUploadRelativePath(fileName: string, targetRelativePath?: string): string;
48
49
  uploadFile(fileName: string, content: Buffer, targetRelativePath?: string): Promise<DiffResult>;
49
50
  listFailedFiles(): DiffResult['skippedFiles'];
50
51
  removeFile(relativePath: string): Promise<void>;
@@ -81,10 +81,28 @@ export class KnowledgeBaseManager {
81
81
  for (const file of [...diff.newFiles, ...diff.modifiedFiles]) {
82
82
  const hash = tracker.hashFile(file.absolutePath);
83
83
  const duplicate = this.store.findExactDuplicate(hash, file.relativePath);
84
+ const collectionName = this.scope === 'global'
85
+ ? this.collections.getCollectionName('global', file.category)
86
+ : this.collections.getCollectionName('project', file.category, this.projectId);
84
87
  const extraction = await this.extractor.extract(file);
85
88
  if (!this.hasUsableContent(extraction.text, extraction.metadata)) {
86
- diff.skippedFiles.push({ file, reason: extraction.warnings[0] ?? '未解析出可用于模型的正文内容,已跳过向量化' });
87
- this.store.deleteRecord(file.relativePath);
89
+ const reason = extraction.warnings[0] ?? '未解析出可用于模型的正文内容,已跳过向量化';
90
+ diff.skippedFiles.push({ file, reason });
91
+ this.store.upsertRecord({
92
+ relativePath: file.relativePath,
93
+ category: file.category,
94
+ format: file.format,
95
+ contentHash: hash,
96
+ fileSize: file.fileSize,
97
+ mtime: file.mtime,
98
+ chunkCount: 0,
99
+ collectionName,
100
+ indexedAt: now,
101
+ lastVerifiedAt: now,
102
+ status: 'error',
103
+ errorMessage: reason,
104
+ metadataJson: JSON.stringify({ mimeType: file.mimeType, warnings: extraction.warnings }),
105
+ });
88
106
  continue;
89
107
  }
90
108
  const normalizedHash = this.dedup.normalizedHash(extraction.text);
@@ -92,9 +110,6 @@ export class KnowledgeBaseManager {
92
110
  ? this.store.findNormalizedDuplicate(normalizedHash, file.relativePath)
93
111
  : undefined;
94
112
  const chunks = duplicate ? [] : this.chunker.chunk(extraction.text, file, extraction.metadata);
95
- const collectionName = this.scope === 'global'
96
- ? this.collections.getCollectionName('global', file.category)
97
- : this.collections.getCollectionName('project', file.category, this.projectId);
98
113
  this.store.upsertFileHash({
99
114
  contentHash: hash,
100
115
  filePath: file.relativePath,
@@ -125,7 +140,7 @@ export class KnowledgeBaseManager {
125
140
  if (!duplicate && extraction.text.length > 1000) {
126
141
  const minHash = this.dedup.computeMinHash(extraction.text);
127
142
  if (minHash) {
128
- for (const existing of this.store.listMinHashes(file.relativePath)) {
143
+ for (const existing of this.store.listMinHashesByBuckets(minHash.buckets, file.relativePath)) {
129
144
  const similarity = this.dedup.estimateSimilarity(minHash.signature, existing.signature);
130
145
  const relationshipType = this.dedup.relationshipForSimilarity(similarity);
131
146
  if (relationshipType) {
@@ -143,6 +158,7 @@ export class KnowledgeBaseManager {
143
158
  filePath: file.relativePath,
144
159
  signature: minHash.signature,
145
160
  shingleCount: minHash.shingleCount,
161
+ buckets: minHash.buckets,
146
162
  });
147
163
  }
148
164
  }
@@ -212,9 +228,12 @@ export class KnowledgeBaseManager {
212
228
  fs.copyFileSync(resolvedSource, targetPath);
213
229
  return this.incrementalIndex();
214
230
  }
231
+ getUploadRelativePath(fileName, targetRelativePath) {
232
+ return targetRelativePath ?? this.defaultUploadRelativePath(fileName);
233
+ }
215
234
  async uploadFile(fileName, content, targetRelativePath) {
216
235
  this.initialize();
217
- const relativePath = targetRelativePath ?? this.defaultUploadRelativePath(fileName);
236
+ const relativePath = this.getUploadRelativePath(fileName, targetRelativePath);
218
237
  const targetPath = this.resolveKbRelativePath(relativePath);
219
238
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
220
239
  fs.writeFileSync(targetPath, content);
@@ -1,6 +1,7 @@
1
1
  export interface MinHashSignature {
2
2
  signature: number[];
3
3
  shingleCount: number;
4
+ buckets: string[];
4
5
  }
5
6
  export interface SimilarityMatch {
6
7
  filePath: string;
@@ -8,10 +9,12 @@ export interface SimilarityMatch {
8
9
  }
9
10
  export declare class DedupEngine {
10
11
  private readonly hashCount;
12
+ private readonly bandSize;
11
13
  normalizeText(text: string): string;
12
14
  normalizedHash(text: string): string | undefined;
13
15
  relationshipForFormats(sourceFormat: string, targetFormat: string): 'format_variant' | 'translation';
14
16
  computeMinHash(text: string, shingleSize?: number): MinHashSignature | undefined;
17
+ computeLshBuckets(signature: number[]): string[];
15
18
  estimateSimilarity(a: number[], b: number[]): number;
16
19
  relationshipForSimilarity(similarity: number): 'near_duplicate' | 'revision' | undefined;
17
20
  private hashToUint32;
@@ -1,6 +1,7 @@
1
1
  import * as crypto from 'node:crypto';
2
2
  export class DedupEngine {
3
- hashCount = 128;
3
+ hashCount = 192;
4
+ bandSize = 6;
4
5
  normalizeText(text) {
5
6
  return text
6
7
  .toLowerCase()
@@ -42,7 +43,16 @@ export class DedupEngine {
42
43
  signature[seed] = value;
43
44
  }
44
45
  }
45
- return { signature, shingleCount: shingles.size };
46
+ return { signature, shingleCount: shingles.size, buckets: this.computeLshBuckets(signature) };
47
+ }
48
+ computeLshBuckets(signature) {
49
+ const buckets = [];
50
+ for (let i = 0; i < signature.length; i += this.bandSize) {
51
+ const band = signature.slice(i, i + this.bandSize);
52
+ if (band.length === this.bandSize)
53
+ buckets.push(`${i / this.bandSize}:${this.hashToUint32(band.join('|'))}`);
54
+ }
55
+ return buckets;
46
56
  }
47
57
  estimateSimilarity(a, b) {
48
58
  const length = Math.min(a.length, b.length);
package/dist/index.d.ts CHANGED
@@ -20,4 +20,3 @@ export { CollectionManager, globalCollectionName, projectCollectionName } from '
20
20
  export type { CollectionClient, VectorCollectionInfo, VectorDocument, VectorSearchQuery, VectorSearchResult, VectorStoreInterface } from './vector/types.js';
21
21
  export { VectorIndexer, type VectorIndexResult } from './vector/vector-indexer.js';
22
22
  export { FederationSearch, type FederatedQuery, type FederatedResult, type FederatedSearchItem, type SearchFilters, type SearchScope } from './search/federation-search.js';
23
- export { startKnowledgeDashboard, type DashboardServerHandle, type DashboardServerOptions } from './server/dashboard-server.js';
package/dist/index.js CHANGED
@@ -19,4 +19,3 @@ export { ChromaHttpClient, ChromaVectorStore } from './vector/chroma-store.js';
19
19
  export { CollectionManager, globalCollectionName, projectCollectionName } from './vector/collection-manager.js';
20
20
  export { VectorIndexer } from './vector/vector-indexer.js';
21
21
  export { FederationSearch } from './search/federation-search.js';
22
- export { startKnowledgeDashboard } from './server/dashboard-server.js';
@@ -1 +1 @@
1
- export declare const dashboardStyles = "\n:root {\n color-scheme: light;\n --bg:#f5f5f7;\n --bg-glass:rgba(255,255,255,.72);\n --panel:rgba(255,255,255,.86);\n --panel-solid:#ffffff;\n --line:rgba(60,60,67,.16);\n --line-strong:rgba(60,60,67,.26);\n --text:#1d1d1f;\n --muted:#6e6e73;\n --accent:#007aff;\n --accent-2:#5856d6;\n --danger:#ff3b30;\n --ok:#34c759;\n --warn:#ff9500;\n --shadow:0 18px 45px rgba(0,0,0,.08);\n --shadow-soft:0 8px 24px rgba(0,0,0,.06);\n}\n* { box-sizing: border-box; }\nhtml { background:var(--bg); }\nbody {\n margin:0; min-height:100vh; font-family: -apple-system, BlinkMacSystemFont, \"SF Pro Display\", \"SF Pro Text\", \"Segoe UI\", sans-serif;\n background:\n radial-gradient(circle at 15% 0%, rgba(0,122,255,.12), transparent 28rem),\n radial-gradient(circle at 85% 8%, rgba(88,86,214,.10), transparent 26rem),\n linear-gradient(180deg, #fbfbfd 0%, var(--bg) 100%);\n color:var(--text);\n}\nheader {\n position: sticky; top:0; z-index:10; backdrop-filter: saturate(180%) blur(22px); -webkit-backdrop-filter: saturate(180%) blur(22px);\n padding:20px max(32px, calc((100vw - 1480px) / 2 + 32px)); border-bottom:1px solid var(--line); display:flex; align-items:center; justify-content:space-between; gap:18px;\n background:var(--bg-glass);\n}\nmain { padding:28px 32px 40px; max-width:1480px; margin:0 auto; }\nmain, aside, main > div { min-width:0; }\n.page { display:none; }\n.page.active { display:block; }\n.topbar { flex-wrap:nowrap; justify-content:flex-start; overflow-x:auto; padding-bottom:2px; scrollbar-width:none; }\n.topbar::-webkit-scrollbar { display:none; }\n.topbar > * { flex:0 0 auto; }\n.tab { background:rgba(255,255,255,.72); border-radius:999px; padding:9px 15px; color:var(--muted); }\n.tab.active { background:#1d1d1f; color:white; border-color:#1d1d1f; }\nsection {\n position:relative; overflow:visible;\n background:var(--panel); border:1px solid rgba(255,255,255,.7); border-radius:26px; padding:20px; box-shadow:var(--shadow-soft);\n backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);\n}\nsection:has(.custom-select.open), section.select-host-open { z-index:10000; }\nsection:hover { box-shadow:var(--shadow); transform:translateY(-1px); transition:.2s ease; }\nh1 { font-size:24px; margin:0; letter-spacing:-.02em; font-weight:700; }\nh2 { font-size:12px; margin:0 0 14px; color:var(--muted); text-transform:uppercase; letter-spacing:.12em; font-weight:700; }\nh3 { font-size:15px; margin:14px 0 8px; }\n.subtitle { margin-top:6px; color:var(--muted); font-size:14px; }\nbutton, input, select {\n min-width:0; background:rgba(255,255,255,.88); color:var(--text); border:1px solid var(--line); border-radius:14px; padding:10px 13px; outline:none;\n font: inherit; box-shadow: inset 0 0 0 1px rgba(255,255,255,.25);\n}\nselect {\n min-width:132px; appearance:none; -webkit-appearance:none; padding-right:34px;\n background-image:linear-gradient(45deg, transparent 50%, #6e6e73 50%), linear-gradient(135deg, #6e6e73 50%, transparent 50%);\n background-position:calc(100% - 18px) 50%, calc(100% - 12px) 50%; background-size:6px 6px, 6px 6px; background-repeat:no-repeat;\n}\nbutton { cursor:pointer; white-space:nowrap; transition:.16s ease; font-weight:600; }\nbutton:hover { border-color:rgba(0,122,255,.35); transform:translateY(-1px); box-shadow:0 8px 18px rgba(0,122,255,.12); }\nbutton.primary { background:linear-gradient(180deg, #0a84ff, #007aff); border-color:transparent; color:white; box-shadow:0 10px 22px rgba(0,122,255,.24); }\ninput { width:100%; }\ninput::placeholder { color:#a1a1a6; }\ninput:focus, select:focus { border-color:rgba(0,122,255,.55); box-shadow:0 0 0 4px rgba(0,122,255,.12); }\n.row { display:flex; gap:10px; margin-bottom:10px; align-items:center; }\n.row input { flex:1; }\n.stack { display:grid; gap:10px; }\n.stack.compact { gap:8px; }\n.field-row { display:grid; grid-template-columns:minmax(0, 1fr) auto; gap:8px; align-items:center; }\n.field-row:has(input + input + button) { grid-template-columns:minmax(0, 1fr) minmax(0, 1fr) auto; }\n.helper-title { font-size:15px; font-weight:700; color:var(--text); margin:2px 0 6px; }\n.helper-text { color:var(--muted); font-size:13px; line-height:1.6; margin-bottom:12px; overflow-wrap:anywhere; }\n.upload-card { display:flex; gap:14px; align-items:center; border:1.5px dashed rgba(0,122,255,.32); background:rgba(0,122,255,.06); border-radius:20px; padding:16px; cursor:pointer; margin-bottom:10px; }\n.upload-card:hover { background:rgba(0,122,255,.10); border-color:rgba(0,122,255,.55); }\nbutton.loading, .upload-card.loading { position:relative; pointer-events:none; opacity:.82; cursor:progress; }\nbutton.loading::after, .upload-card.loading::after { content:''; width:14px; height:14px; border:2px solid currentColor; border-top-color:transparent; border-radius:50%; display:inline-block; margin-left:8px; vertical-align:-2px; animation:spin .8s linear infinite; }\n.upload-card.loading::after { margin-left:auto; color:#007aff; width:18px; height:18px; }\nbutton:disabled { cursor:progress; }\n@keyframes spin { to { transform:rotate(360deg); } }\n.upload-card input { display:none; }\n.upload-card b { display:block; font-size:15px; }\n.upload-card span { display:block; color:var(--muted); font-size:12px; line-height:1.45; margin-top:3px; overflow-wrap:anywhere; }\n.upload-icon { width:38px; height:38px; flex:0 0 38px; border-radius:50%; display:grid; place-items:center; background:#007aff; color:white; font-weight:800; font-size:20px; }\n.advanced-block { margin-top:12px; border:1px solid var(--line); border-radius:16px; padding:10px 12px; background:rgba(255,255,255,.55); }\n.advanced-block summary { cursor:pointer; color:var(--muted); font-size:13px; font-weight:600; margin-bottom:8px; }\n.divider { height:1px; background:var(--line); margin:16px 0; }\n.search-bar { display:grid; grid-template-columns: 170px minmax(0, 1fr) auto; gap:10px; align-items:end; }\n.filter-bar { display:grid; grid-template-columns: repeat(2, minmax(180px, 260px)); gap:10px; align-items:end; margin:12px 0 14px; }\n.select-wrap { position:relative; display:grid; gap:5px; color:var(--muted); font-size:12px; font-weight:600; min-width:0; overflow:visible; }\n.select-wrap.compact { min-width:130px; }\n.language-select { width:96px; min-width:96px; }\n.language-select select { min-width:96px; width:96px; cursor:pointer; }\n.select-wrap select { width:100%; }\n.select-native-hidden { position:absolute !important; opacity:0 !important; pointer-events:none !important; width:1px !important; height:1px !important; }\n.custom-select { position:relative; min-width:0; z-index:1; }\n.custom-select.open { z-index:10001; }\n.custom-select-trigger { width:100%; justify-content:space-between; text-align:left; display:flex; align-items:center; gap:10px; background:rgba(255,255,255,.88); color:var(--text); border:1px solid var(--line); border-radius:14px; padding:10px 13px; box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); }\n.custom-select-trigger::after { content:'\u2304'; color:var(--muted); font-size:13px; line-height:1; display:inline-flex; align-items:center; justify-content:center; transform:translateY(-1px); flex:0 0 auto; }\n.custom-select.open .custom-select-trigger { border-color:rgba(0,122,255,.55); box-shadow:0 0 0 4px rgba(0,122,255,.12); }\n.custom-select-menu { display:none; position:absolute; top:calc(100% + 6px); left:0; right:0; z-index:10002; max-height:260px; overflow:auto; padding:6px; border:1px solid var(--line); border-radius:16px; background:rgba(255,255,255,.98); box-shadow:0 18px 40px rgba(0,0,0,.16); backdrop-filter:blur(16px); }\n.custom-select.open .custom-select-menu { display:block; }\n.custom-select-option { display:block; width:100%; border:0; box-shadow:none; background:transparent; text-align:left; color:var(--text); border-radius:10px; padding:9px 10px; white-space:normal; }\n.custom-select-option:hover, .custom-select-option.active { background:rgba(0,122,255,.10); color:var(--accent); transform:none; box-shadow:none; }\n.config-row { display:grid; grid-template-columns:90px minmax(0, 1fr); gap:10px; align-items:start; padding:8px 0; border-top:1px solid var(--line); }\n.config-row:first-child { border-top:0; }\n.config-row b, .config-row span { min-width:0; overflow-wrap:anywhere; line-height:1.5; }\n.file-group { border:1px solid var(--line); border-radius:18px; background:rgba(255,255,255,.58); margin-top:10px; overflow:hidden; }\n.file-group summary { cursor:pointer; padding:12px 14px; font-weight:700; display:flex; justify-content:space-between; align-items:center; }\n.file-group summary span { color:var(--muted); font-size:12px; }\n.file-item { display:grid; grid-template-columns:minmax(0, 1fr) auto; gap:12px; padding:12px 14px; border-top:1px solid var(--line); align-items:center; }\n.file-main { min-width:0; }\n.file-actions { display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; }\n.file-actions button { padding:7px 9px; font-size:12px; }\n.grid { display:grid; gap:20px; grid-template-columns: repeat(2, minmax(0, 1fr)); }\n.panel-gap { margin-top:20px; }\n.stat-grid { display:grid; grid-template-columns: repeat(3, 1fr); gap:10px; margin:10px 0 12px; }\n.metric { background:linear-gradient(180deg, #fff, #f7f7fa); border:1px solid var(--line); border-radius:18px; padding:14px; }\n.metric b { display:block; font-size:24px; margin-top:4px; letter-spacing:-.03em; }\n.metric span { color:var(--muted); font-size:12px; }\n.stat { display:flex; justify-content:space-between; align-items:flex-start; color:var(--muted); margin:8px 0; gap:12px; }\n.stat b { color:var(--text); }\n.card { border-top:1px solid var(--line); padding:14px 0; }\n.card:first-child { border-top:0; padding-top:0; }\n.path { color:var(--accent); font-size:13px; overflow-wrap:anywhere; word-break:normal; font-weight:600; line-height:1.45; }\n.meta { color:var(--muted); font-size:12px; overflow-wrap:anywhere; word-break:normal; line-height:1.55; min-width:0; }\n.badge { display:inline-flex; align-items:center; border:1px solid var(--line); border-radius:999px; padding:3px 9px; font-size:12px; color:var(--muted); margin:3px 5px 3px 0; background:rgba(255,255,255,.7); }\n.badge.ok { color:#1d7f3a; border-color:rgba(52,199,89,.28); background:rgba(52,199,89,.10); }\n.badge.warn { color:#9a5a00; border-color:rgba(255,149,0,.30); background:rgba(255,149,0,.10); }\n#capabilities { max-height: 460px; overflow:auto; padding-right:4px; }\n#results .card { background:var(--panel-solid); border-radius:20px; border:1px solid var(--line); margin-top:12px; padding:16px; box-shadow:var(--shadow-soft); }\n.status { color:var(--muted); min-height:20px; font-size:12px; margin-top:8px; }\n.status.ok { color:var(--ok); }\n.status.error { color:var(--danger); }\n.toolbar { display:flex; gap:10px; align-items:center; justify-content:flex-end; }\npre { white-space:pre-wrap; word-break:break-word; color:#3a3a3c; margin:10px 0 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:12px; line-height:1.55; }\n.subsection { margin-top:18px; padding-top:16px; border-top:1px solid var(--line); }\n.modal-backdrop { position:fixed; inset:0; z-index:20000; display:grid; place-items:center; background:rgba(15,23,42,.35); backdrop-filter:blur(10px); }\n.modal-backdrop[hidden] { display:none; }\n.modal-card { width:min(520px, calc(100vw - 32px)); background:rgba(255,255,255,.98); border:1px solid var(--line); border-radius:24px; box-shadow:0 28px 70px rgba(0,0,0,.22); padding:22px; }\n.modal-card h3 { margin:0 0 8px; }\n.modal-card p { white-space:pre-wrap; color:var(--muted); margin:0 0 14px; }\n.modal-input { width:100%; box-sizing:border-box; margin:0 0 14px; border:1px solid var(--line); border-radius:14px; padding:11px 12px; font:inherit; }\n.modal-actions { display:flex; justify-content:flex-end; gap:10px; }\nbutton.danger { background:linear-gradient(135deg,#ff3b30,#ff6b5f); color:white; border-color:rgba(255,59,48,.25); }\n@media (max-width: 1080px) { .grid { grid-template-columns:1fr; } }\n@media (max-width: 680px) { main { padding:16px; } header { padding:16px; align-items:flex-start; flex-direction:column; } .toolbar { width:100%; justify-content:flex-start; flex-wrap:wrap; } .row { flex-direction:column; align-items:stretch; } .search-bar, .filter-bar { grid-template-columns:1fr; } select { width:100%; } .field-row, .field-row:has(input + input + button), .file-item, .config-row { grid-template-columns:1fr; } .file-actions { justify-content:flex-start; } .stat-grid { grid-template-columns:1fr; } .upload-card { align-items:flex-start; } }\n";
1
+ export declare const dashboardStyles = "\n:root {\n color-scheme: light;\n --bg:#f5f5f7;\n --bg-glass:rgba(255,255,255,.72);\n --panel:rgba(255,255,255,.86);\n --panel-solid:#ffffff;\n --line:rgba(60,60,67,.16);\n --line-strong:rgba(60,60,67,.26);\n --text:#1d1d1f;\n --muted:#6e6e73;\n --accent:#007aff;\n --accent-2:#5856d6;\n --danger:#ff3b30;\n --ok:#34c759;\n --warn:#ff9500;\n --shadow:0 18px 45px rgba(0,0,0,.08);\n --shadow-soft:0 8px 24px rgba(0,0,0,.06);\n}\n* { box-sizing: border-box; }\nhtml { background:var(--bg); }\nbody {\n margin:0; min-height:100vh; font-family: -apple-system, BlinkMacSystemFont, \"SF Pro Display\", \"SF Pro Text\", \"Segoe UI\", sans-serif;\n background:\n radial-gradient(circle at 15% 0%, rgba(0,122,255,.12), transparent 28rem),\n radial-gradient(circle at 85% 8%, rgba(88,86,214,.10), transparent 26rem),\n linear-gradient(180deg, #fbfbfd 0%, var(--bg) 100%);\n color:var(--text);\n}\nheader {\n position: sticky; top:0; z-index:10; backdrop-filter: saturate(180%) blur(22px); -webkit-backdrop-filter: saturate(180%) blur(22px);\n padding:20px max(32px, calc((100vw - 1480px) / 2 + 32px)); border-bottom:1px solid var(--line); display:flex; align-items:center; justify-content:space-between; gap:18px;\n background:var(--bg-glass);\n}\nmain { padding:28px 32px 40px; max-width:1480px; margin:0 auto; }\nmain, aside, main > div { min-width:0; }\n.page { display:none; }\n.page.active { display:block; }\n.topbar { flex-wrap:nowrap; justify-content:flex-start; overflow-x:auto; padding-bottom:2px; scrollbar-width:none; }\n.topbar::-webkit-scrollbar { display:none; }\n.topbar > * { flex:0 0 auto; }\n.tab { background:rgba(255,255,255,.72); border-radius:999px; padding:9px 15px; color:var(--muted); }\n.tab.active { background:#1d1d1f; color:white; border-color:#1d1d1f; }\nsection {\n position:relative; overflow:visible;\n background:var(--panel); border:1px solid rgba(255,255,255,.7); border-radius:26px; padding:20px; box-shadow:var(--shadow-soft);\n backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);\n}\nsection:has(.custom-select.open), section.select-host-open { z-index:10000; }\nsection:hover { box-shadow:var(--shadow); transform:translateY(-1px); transition:.2s ease; }\nh1 { font-size:24px; margin:0; letter-spacing:-.02em; font-weight:700; }\nh2 { font-size:12px; margin:0 0 14px; color:var(--muted); text-transform:uppercase; letter-spacing:.12em; font-weight:700; }\nh3 { font-size:15px; margin:14px 0 8px; }\n.subtitle { margin-top:6px; color:var(--muted); font-size:14px; }\nbutton, input, select {\n min-width:0; background:rgba(255,255,255,.88); color:var(--text); border:1px solid var(--line); border-radius:14px; padding:10px 13px; outline:none;\n font: inherit; box-shadow: inset 0 0 0 1px rgba(255,255,255,.25);\n}\nselect {\n min-width:132px; appearance:none; -webkit-appearance:none; padding-right:34px;\n background-image:linear-gradient(45deg, transparent 50%, #6e6e73 50%), linear-gradient(135deg, #6e6e73 50%, transparent 50%);\n background-position:calc(100% - 18px) 50%, calc(100% - 12px) 50%; background-size:6px 6px, 6px 6px; background-repeat:no-repeat;\n}\nbutton { cursor:pointer; white-space:nowrap; transition:.16s ease; font-weight:600; }\nbutton:hover { border-color:rgba(0,122,255,.35); transform:translateY(-1px); box-shadow:0 8px 18px rgba(0,122,255,.12); }\nbutton.primary { background:linear-gradient(180deg, #0a84ff, #007aff); border-color:transparent; color:white; box-shadow:0 10px 22px rgba(0,122,255,.24); }\ninput { width:100%; }\ninput::placeholder { color:#a1a1a6; }\ninput:focus, select:focus { border-color:rgba(0,122,255,.55); box-shadow:0 0 0 4px rgba(0,122,255,.12); }\n.row { display:flex; gap:10px; margin-bottom:10px; align-items:center; }\n.row input { flex:1; }\n.stack { display:grid; gap:10px; }\n.stack.compact { gap:8px; }\n.field-row { display:grid; grid-template-columns:minmax(0, 1fr) auto; gap:8px; align-items:center; }\n.field-row:has(input + input + button) { grid-template-columns:minmax(0, 1fr) minmax(0, 1fr) auto; }\n.helper-title { font-size:15px; font-weight:700; color:var(--text); margin:2px 0 6px; }\n.helper-text { color:var(--muted); font-size:13px; line-height:1.6; margin-bottom:12px; overflow-wrap:anywhere; }\n.upload-card { display:flex; gap:14px; align-items:center; border:1.5px dashed rgba(0,122,255,.32); background:rgba(0,122,255,.06); border-radius:20px; padding:16px; cursor:pointer; margin-bottom:10px; }\n.upload-card:hover { background:rgba(0,122,255,.10); border-color:rgba(0,122,255,.55); }\nbutton.loading, .upload-card.loading { position:relative; pointer-events:none; opacity:.82; cursor:progress; }\nbutton.loading::after, .upload-card.loading::after { content:''; width:14px; height:14px; border:2px solid currentColor; border-top-color:transparent; border-radius:50%; display:inline-block; margin-left:8px; vertical-align:-2px; animation:spin .8s linear infinite; }\n.upload-card.loading::after { margin-left:auto; color:#007aff; width:18px; height:18px; }\nbutton:disabled { cursor:progress; }\n@keyframes spin { to { transform:rotate(360deg); } }\n.upload-card input { display:none; }\n.upload-card b { display:block; font-size:15px; }\n.upload-card span { display:block; color:var(--muted); font-size:12px; line-height:1.45; margin-top:3px; overflow-wrap:anywhere; }\n.upload-icon { width:38px; height:38px; flex:0 0 38px; border-radius:50%; display:grid; place-items:center; background:#007aff; color:white; font-weight:800; font-size:20px; }\n.advanced-block { margin-top:12px; border:1px solid var(--line); border-radius:16px; padding:10px 12px; background:rgba(255,255,255,.55); }\n.advanced-block summary { cursor:pointer; color:var(--muted); font-size:13px; font-weight:600; margin-bottom:8px; }\n.divider { height:1px; background:var(--line); margin:16px 0; }\n.search-bar { display:grid; grid-template-columns: 170px minmax(0, 1fr) auto; gap:10px; align-items:end; }\n.filter-bar { display:grid; grid-template-columns: repeat(2, minmax(180px, 260px)); gap:10px; align-items:end; margin:12px 0 14px; }\n.select-wrap { position:relative; display:grid; gap:5px; color:var(--muted); font-size:12px; font-weight:600; min-width:0; overflow:visible; }\n.select-wrap.compact { min-width:130px; }\n.language-select { width:96px; min-width:96px; }\n.language-select select { min-width:96px; width:96px; cursor:pointer; }\n.select-wrap select { width:100%; }\n.select-native-hidden { position:absolute !important; opacity:0 !important; pointer-events:none !important; width:1px !important; height:1px !important; }\n.custom-select { position:relative; min-width:0; z-index:1; }\n.custom-select.open { z-index:10001; }\n.custom-select-trigger { width:100%; justify-content:space-between; text-align:left; display:flex; align-items:center; gap:10px; background:rgba(255,255,255,.88); color:var(--text); border:1px solid var(--line); border-radius:14px; padding:10px 13px; box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); }\n.custom-select-trigger::after { content:'\u2304'; color:var(--muted); font-size:13px; line-height:1; display:inline-flex; align-items:center; justify-content:center; transform:translateY(-1px); flex:0 0 auto; }\n.custom-select.open .custom-select-trigger { border-color:rgba(0,122,255,.55); box-shadow:0 0 0 4px rgba(0,122,255,.12); }\n.custom-select-menu { display:none; position:absolute; top:calc(100% + 6px); left:0; right:0; z-index:10002; max-height:260px; overflow:auto; padding:6px; border:1px solid var(--line); border-radius:16px; background:rgba(255,255,255,.98); box-shadow:0 18px 40px rgba(0,0,0,.16); backdrop-filter:blur(16px); }\n.custom-select.open .custom-select-menu { display:block; }\n.custom-select-option { display:block; width:100%; border:0; box-shadow:none; background:transparent; text-align:left; color:var(--text); border-radius:10px; padding:9px 10px; white-space:normal; }\n.custom-select-option:hover, .custom-select-option.active { background:rgba(0,122,255,.10); color:var(--accent); transform:none; box-shadow:none; }\n.config-row { display:grid; grid-template-columns:90px minmax(0, 1fr); gap:10px; align-items:start; padding:8px 0; border-top:1px solid var(--line); }\n.config-row:first-child { border-top:0; }\n.config-row b, .config-row span { min-width:0; overflow-wrap:anywhere; line-height:1.5; }\n.file-group { border:1px solid var(--line); border-radius:18px; background:rgba(255,255,255,.58); margin-top:10px; overflow:hidden; }\n.file-group summary { cursor:pointer; padding:12px 14px; font-weight:700; display:flex; justify-content:space-between; align-items:center; }\n.file-group summary span { color:var(--muted); font-size:12px; }\n.file-item { display:grid; grid-template-columns:minmax(0, 1fr) auto; gap:12px; padding:12px 14px; border-top:1px solid var(--line); align-items:center; }\n.file-main { min-width:0; }\n.file-actions { display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; }\n.file-actions button { padding:7px 9px; font-size:12px; }\n.grid { display:grid; gap:20px; grid-template-columns: repeat(2, minmax(0, 1fr)); }\n.panel-gap { margin-top:20px; }\n.stat-grid { display:grid; grid-template-columns: repeat(3, 1fr); gap:10px; margin:10px 0 12px; }\n.metric { background:linear-gradient(180deg, #fff, #f7f7fa); border:1px solid var(--line); border-radius:18px; padding:14px; }\n.metric b { display:block; font-size:24px; margin-top:4px; letter-spacing:-.03em; }\n.metric span { color:var(--muted); font-size:12px; }\n.stat { display:flex; justify-content:space-between; align-items:flex-start; color:var(--muted); margin:8px 0; gap:12px; }\n.stat b { color:var(--text); }\n.card { border-top:1px solid var(--line); padding:14px 0; }\n.card:first-child { border-top:0; padding-top:0; }\n.path { color:var(--accent); font-size:13px; overflow-wrap:anywhere; word-break:normal; font-weight:600; line-height:1.45; }\n.meta { color:var(--muted); font-size:12px; overflow-wrap:anywhere; word-break:normal; line-height:1.55; min-width:0; }\n.badge { display:inline-flex; align-items:center; border:1px solid var(--line); border-radius:999px; padding:3px 9px; font-size:12px; color:var(--muted); margin:3px 5px 3px 0; background:rgba(255,255,255,.7); }\n.badge.ok { color:#1d7f3a; border-color:rgba(52,199,89,.28); background:rgba(52,199,89,.10); }\n.badge.warn { color:#9a5a00; border-color:rgba(255,149,0,.30); background:rgba(255,149,0,.10); }\n#capabilities { max-height: 460px; overflow:auto; padding-right:4px; }\n#results .card { background:var(--panel-solid); border-radius:20px; border:1px solid var(--line); margin-top:12px; padding:16px; box-shadow:var(--shadow-soft); }\n.status { color:var(--muted); min-height:20px; font-size:12px; margin-top:8px; }\n.status.ok { color:var(--ok); }\n.status.error { color:var(--danger); }\n.toolbar { display:flex; gap:10px; align-items:center; justify-content:flex-end; }\npre { white-space:pre-wrap; word-break:break-word; color:#3a3a3c; margin:10px 0 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:12px; line-height:1.55; }\n.subsection { margin-top:18px; padding-top:16px;}\n.modal-backdrop { position:fixed; inset:0; z-index:20000; display:grid; place-items:center; background:rgba(15,23,42,.35); backdrop-filter:blur(10px); }\n.modal-backdrop[hidden] { display:none; }\n.modal-card { width:min(520px, calc(100vw - 32px)); background:rgba(255,255,255,.98); border:1px solid var(--line); border-radius:24px; box-shadow:0 28px 70px rgba(0,0,0,.22); padding:22px; }\n.modal-card h3 { margin:0 0 8px; }\n.modal-card p { white-space:pre-wrap; color:var(--muted); margin:0 0 14px; }\n.modal-input { width:100%; box-sizing:border-box; margin:0 0 14px; border:1px solid var(--line); border-radius:14px; padding:11px 12px; font:inherit; }\n.modal-actions { display:flex; justify-content:flex-end; gap:10px; }\nbutton.danger { background:linear-gradient(135deg,#ff3b30,#ff6b5f); color:white; border-color:rgba(255,59,48,.25); }\n@media (max-width: 1080px) { .grid { grid-template-columns:1fr; } }\n@media (max-width: 680px) { main { padding:16px; } header { padding:16px; align-items:flex-start; flex-direction:column; } .toolbar { width:100%; justify-content:flex-start; flex-wrap:wrap; } .row { flex-direction:column; align-items:stretch; } .search-bar, .filter-bar { grid-template-columns:1fr; } select { width:100%; } .field-row, .field-row:has(input + input + button), .file-item, .config-row { grid-template-columns:1fr; } .file-actions { justify-content:flex-start; } .stat-grid { grid-template-columns:1fr; } .upload-card { align-items:flex-start; } }\n";
@@ -138,7 +138,7 @@ button:disabled { cursor:progress; }
138
138
  .status.error { color:var(--danger); }
139
139
  .toolbar { display:flex; gap:10px; align-items:center; justify-content:flex-end; }
140
140
  pre { white-space:pre-wrap; word-break:break-word; color:#3a3a3c; margin:10px 0 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:12px; line-height:1.55; }
141
- .subsection { margin-top:18px; padding-top:16px; border-top:1px solid var(--line); }
141
+ .subsection { margin-top:18px; padding-top:16px;}
142
142
  .modal-backdrop { position:fixed; inset:0; z-index:20000; display:grid; place-items:center; background:rgba(15,23,42,.35); backdrop-filter:blur(10px); }
143
143
  .modal-backdrop[hidden] { display:none; }
144
144
  .modal-card { width:min(520px, calc(100vw - 32px)); background:rgba(255,255,255,.98); border:1px solid var(--line); border-radius:24px; box-shadow:0 28px 70px rgba(0,0,0,.22); padding:22px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@customize-agent/knowledge",
3
- "version": "1.0.1",
3
+ "version": "2.0.0",
4
4
  "description": "Local knowledge base infrastructure for customize-agent",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",