@customize-agent/knowledge 3.0.10 → 3.0.12

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.
@@ -494,7 +494,13 @@ export class IndexStateStore {
494
494
  );
495
495
  `);
496
496
  this.initFts();
497
- this.setMetadata('schema_version', '2');
497
+ try {
498
+ if (this.getMetadata('schema_version') !== '2')
499
+ this.setMetadata('schema_version', '2');
500
+ }
501
+ catch {
502
+ // 受限环境中已有索引库可能以只读方式挂载;运行时元数据不是必需项。
503
+ }
498
504
  }
499
505
  initFts() {
500
506
  try {
@@ -32,7 +32,7 @@ export class KnowledgeBaseManager {
32
32
  relationshipDetector = new RelationshipDetector();
33
33
  embeddingProvider;
34
34
  vectorStores;
35
- configManager = new ProjectConfigManager();
35
+ configManager;
36
36
  projectConfig;
37
37
  lastSkippedFiles = [];
38
38
  llmProvider;
@@ -47,6 +47,7 @@ export class KnowledgeBaseManager {
47
47
  this.llmProvider = options.llmProvider;
48
48
  this.onProgress = options.onProgress;
49
49
  const storageRoot = options.storageRoot ?? path.join(os.homedir(), USER_DATA_DIR);
50
+ this.configManager = new ProjectConfigManager(storageRoot);
50
51
  let dbPath;
51
52
  if (this.scope === 'global') {
52
53
  this.kbPath = options.kbPath ?? path.join(storageRoot, GLOBAL_KNOWLEDGE_DIR);
@@ -366,18 +367,26 @@ export class KnowledgeBaseManager {
366
367
  return this.incrementalIndex();
367
368
  }
368
369
  getUploadRelativePath(fileName, targetRelativePath) {
369
- return targetRelativePath ?? this.defaultUploadRelativePath(fileName);
370
+ return targetRelativePath ? this.normalizeRelativePath(targetRelativePath) : this.defaultUploadRelativePath(fileName);
370
371
  }
371
372
  async uploadFile(fileName, content, targetRelativePath, onProgress, options = {}) {
372
373
  return this.uploadFiles([{ fileName, content, targetRelativePath }], onProgress, options);
373
374
  }
374
375
  async uploadFiles(files, onProgress, options = {}) {
375
376
  this.initialize();
377
+ const uploadedPaths = [];
376
378
  for (const file of files) {
377
379
  const relativePath = this.getUploadRelativePath(file.fileName, file.targetRelativePath);
378
380
  const targetPath = this.resolveKbRelativePath(relativePath);
379
381
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
380
382
  fs.writeFileSync(targetPath, file.content);
383
+ uploadedPaths.push(relativePath);
384
+ }
385
+ for (const relativePath of uploadedPaths) {
386
+ const record = this.store.listRecords().find(item => item.relativePath === relativePath);
387
+ if (record)
388
+ await this.deleteVectorFile(record.collectionName, relativePath);
389
+ this.store.deleteRecord(relativePath);
381
390
  }
382
391
  return this.incrementalIndex({ onProgress, vectorMode: options.vectorMode });
383
392
  }
@@ -10,13 +10,14 @@ export class MultiProjectManager {
10
10
  storageRoot;
11
11
  llmProvider;
12
12
  registry;
13
- configManager = new ProjectConfigManager();
13
+ configManager;
14
14
  projects = new Map();
15
15
  globalKB;
16
16
  constructor(storageRoot = path.join(os.homedir(), USER_DATA_DIR), llmProvider) {
17
17
  this.storageRoot = storageRoot;
18
18
  this.llmProvider = llmProvider;
19
19
  this.registry = new ProjectRegistry(path.join(storageRoot, 'projects', 'registry.db'));
20
+ this.configManager = new ProjectConfigManager(storageRoot);
20
21
  }
21
22
  async getProject(projectRoot) {
22
23
  const resolvedRoot = path.resolve(projectRoot);
@@ -1,8 +1,10 @@
1
1
  import type { ProjectConfig } from '../types.js';
2
- export declare function getProjectConfigPath(projectRoot: string): string;
2
+ export declare function getProjectConfigPath(projectRoot: string, storageRoot?: string): string;
3
3
  export declare function getProjectKbPath(projectRoot: string): string;
4
4
  export declare function ensureProjectCustomizeFile(projectRoot: string): void;
5
5
  export declare class ProjectConfigManager {
6
+ private readonly storageRoot;
7
+ constructor(storageRoot?: string);
6
8
  loadOrCreate(projectRoot: string): ProjectConfig;
7
9
  save(projectRoot: string, config: ProjectConfig): void;
8
10
  private withDefaults;
@@ -1,11 +1,11 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import * as os from 'node:os';
4
- import { DEFAULT_CATEGORY_DIRS, KNOWLEDGE_BASE_DIR, PROJECT_CONFIG_PATH, USER_DATA_DIR } from '../constants.js';
4
+ import { DEFAULT_CATEGORY_DIRS, KNOWLEDGE_BASE_DIR, USER_DATA_DIR } from '../constants.js';
5
5
  import { computeProjectId } from './project-id.js';
6
- export function getProjectConfigPath(projectRoot) {
6
+ export function getProjectConfigPath(projectRoot, storageRoot = path.join(os.homedir(), USER_DATA_DIR)) {
7
7
  const projectId = computeProjectId(projectRoot);
8
- return path.join(os.homedir(), USER_DATA_DIR, 'projects', projectId, ...PROJECT_CONFIG_PATH.slice(1));
8
+ return path.join(storageRoot, 'projects', projectId, 'project.json');
9
9
  }
10
10
  export function getProjectKbPath(projectRoot) {
11
11
  return path.join(projectRoot, KNOWLEDGE_BASE_DIR);
@@ -35,22 +35,24 @@ export function ensureProjectCustomizeFile(projectRoot) {
35
35
  }
36
36
  }
37
37
  export class ProjectConfigManager {
38
+ storageRoot;
39
+ constructor(storageRoot = path.join(os.homedir(), USER_DATA_DIR)) {
40
+ this.storageRoot = storageRoot;
41
+ }
38
42
  loadOrCreate(projectRoot) {
39
43
  ensureProjectCustomizeFile(projectRoot);
40
- const configPath = getProjectConfigPath(projectRoot);
44
+ const configPath = getProjectConfigPath(projectRoot, this.storageRoot);
41
45
  const now = Date.now();
42
46
  if (fs.existsSync(configPath)) {
43
47
  const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
44
- const config = this.withDefaults(projectRoot, raw, now);
45
- this.save(projectRoot, { ...config, lastOpenedAt: now });
46
- return { ...config, lastOpenedAt: now };
48
+ return this.withDefaults(projectRoot, raw, now);
47
49
  }
48
50
  const config = this.withDefaults(projectRoot, {}, now);
49
51
  this.save(projectRoot, config);
50
52
  return config;
51
53
  }
52
54
  save(projectRoot, config) {
53
- const configPath = getProjectConfigPath(projectRoot);
55
+ const configPath = getProjectConfigPath(projectRoot, this.storageRoot);
54
56
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
55
57
  fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
56
58
  }
@@ -17,6 +17,7 @@ export declare class ContentExtractor {
17
17
  private textScore;
18
18
  private extractCad;
19
19
  private extractDxf;
20
+ private tryConvertDwgWithBundledWasm;
20
21
  private tryConvertDwgToDxf;
21
22
  private extractCadMesh;
22
23
  private getTempRoot;
@@ -2,6 +2,7 @@ import { spawnSync } from 'node:child_process';
2
2
  import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
+ import { pathToFileURL } from 'node:url';
5
6
  import { ExternalExtractorRegistry } from './external-extractor.js';
6
7
  import { resolveAndImport, resolvePackage, getNodeModulesRoot } from './module-resolver.js';
7
8
  export class ContentExtractor {
@@ -333,10 +334,31 @@ export class ContentExtractor {
333
334
  warnings,
334
335
  };
335
336
  }
337
+ async tryConvertDwgWithBundledWasm(filePath) {
338
+ try {
339
+ const mod = await resolveAndImport('dwgdxf');
340
+ if (!mod.convertDwgToDxf)
341
+ return { tool: 'dwgdxf_wasm', warnings: ['内置 dwgdxf WASM 转换器未导出 convertDwgToDxf'] };
342
+ const wasmBase = pathToFileURL(path.join(path.dirname(resolvePackage('dwgdxf')), 'wasm')).href;
343
+ const dxfBytes = await mod.convertDwgToDxf(fs.readFileSync(filePath), { wasmBase });
344
+ const dxfText = Buffer.from(dxfBytes).toString('utf8');
345
+ return dxfText.trim()
346
+ ? { dxfText, tool: 'dwgdxf_wasm', warnings: [] }
347
+ : { tool: 'dwgdxf_wasm', warnings: ['内置 dwgdxf WASM 转换器未输出 DXF 文本'] };
348
+ }
349
+ catch (error) {
350
+ return { tool: 'dwgdxf_wasm', warnings: [`内置 dwgdxf WASM 转换失败: ${error instanceof Error ? error.message : String(error)}`] };
351
+ }
352
+ }
336
353
  async tryConvertDwgToDxf(filePath) {
337
354
  const tmpDir = fs.mkdtempSync(path.join(this.getTempRoot(), 'customize-dwg-'));
338
355
  const outputPath = path.join(tmpDir, `${path.basename(filePath, path.extname(filePath))}.dxf`);
339
356
  try {
357
+ const failures = [];
358
+ const bundled = await this.tryConvertDwgWithBundledWasm(filePath);
359
+ if (bundled.dxfText)
360
+ return bundled;
361
+ failures.push(...bundled.warnings);
340
362
  const customCmd = process.env.CUSTOMIZE_DWG_TO_DXF_CMD;
341
363
  if (customCmd) {
342
364
  if (/\s/u.test(customCmd))
@@ -357,7 +379,6 @@ export class ContentExtractor {
357
379
  return { dxfText: fs.readFileSync(outputPath, 'utf8'), tool: 'external_dwg_to_dxf', warnings: [] };
358
380
  return { tool: 'external_dwg_to_dxf', warnings: [`CUSTOMIZE_DWG_TO_DXF_CMD 转换失败: ${result.stderr || result.stdout || result.error?.message || 'unknown error'}`] };
359
381
  }
360
- const failures = [];
361
382
  for (const bin of ['dwgread', 'dwg2dxf']) {
362
383
  const result = spawnSync(bin, bin === 'dwgread' ? ['-O', 'DXF', '-o', outputPath, filePath] : [filePath, outputPath], { encoding: 'utf8', timeout: 120_000 });
363
384
  if (result.status === 0 && fs.existsSync(outputPath))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@customize-agent/knowledge",
3
- "version": "3.0.10",
3
+ "version": "3.0.12",
4
4
  "description": "Local knowledge base infrastructure for customize-agent",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,6 +37,7 @@
37
37
  "dependencies": {
38
38
  "@napi-rs/canvas": "^0.1.82",
39
39
  "better-sqlite3": "^12.10.0",
40
+ "dwgdxf": "^2.0.1",
40
41
  "dxf-parser": "^1.1.2",
41
42
  "fast-glob": "^3.3.3",
42
43
  "jszip": "^3.10.1",