memento-mcp-server 1.16.3-a → 1.16.3-b

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.
Files changed (68) hide show
  1. package/dist/domains/search/algorithms/vector-search-engine-migration.d.ts +13 -8
  2. package/dist/domains/search/algorithms/vector-search-engine-migration.d.ts.map +1 -1
  3. package/dist/domains/search/algorithms/vector-search-engine-migration.js +19 -41
  4. package/dist/domains/search/algorithms/vector-search-engine-migration.js.map +1 -1
  5. package/dist/domains/search/algorithms/vector-search-engine.d.ts +17 -36
  6. package/dist/domains/search/algorithms/vector-search-engine.d.ts.map +1 -1
  7. package/dist/domains/search/algorithms/vector-search-engine.js +94 -481
  8. package/dist/domains/search/algorithms/vector-search-engine.js.map +1 -1
  9. package/dist/domains/search/repositories/vector-search.repository.d.ts.map +1 -1
  10. package/dist/domains/search/repositories/vector-search.repository.js +28 -12
  11. package/dist/domains/search/repositories/vector-search.repository.js.map +1 -1
  12. package/dist/server/http-server.d.ts.map +1 -1
  13. package/dist/server/http-server.js +2 -7
  14. package/dist/server/http-server.js.map +1 -1
  15. package/dist/server/index.d.ts +3 -0
  16. package/dist/server/index.d.ts.map +1 -1
  17. package/dist/server/index.js +33 -7
  18. package/dist/server/index.js.map +1 -1
  19. package/dist/server/server-factory.d.ts +65 -0
  20. package/dist/server/server-factory.d.ts.map +1 -0
  21. package/dist/server/server-factory.js +40 -0
  22. package/dist/server/server-factory.js.map +1 -0
  23. package/dist/server/servers/sse-server.d.ts +33 -0
  24. package/dist/server/servers/sse-server.d.ts.map +1 -0
  25. package/dist/server/servers/sse-server.js +48 -0
  26. package/dist/server/servers/sse-server.js.map +1 -0
  27. package/dist/server/servers/stdio-server.d.ts +34 -0
  28. package/dist/server/servers/stdio-server.d.ts.map +1 -0
  29. package/dist/server/servers/stdio-server.js +58 -0
  30. package/dist/server/servers/stdio-server.js.map +1 -0
  31. package/dist/server/simple-mcp-server.d.ts +5 -0
  32. package/dist/server/simple-mcp-server.d.ts.map +1 -1
  33. package/dist/server/simple-mcp-server.js +17 -7
  34. package/dist/server/simple-mcp-server.js.map +1 -1
  35. package/dist/server/sse-server-impl.d.ts +22 -0
  36. package/dist/server/sse-server-impl.d.ts.map +1 -0
  37. package/dist/server/sse-server-impl.js +39 -0
  38. package/dist/server/sse-server-impl.js.map +1 -0
  39. package/dist/server/stdio-server-impl.d.ts +12 -0
  40. package/dist/server/stdio-server-impl.d.ts.map +1 -0
  41. package/dist/server/stdio-server-impl.js +19 -0
  42. package/dist/server/stdio-server-impl.js.map +1 -0
  43. package/dist/shared/types/vector-search.types.d.ts +1 -0
  44. package/dist/shared/types/vector-search.types.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/scripts/__tests__/check-db-integrity.integration.spec.ts +163 -0
  47. package/scripts/__tests__/fix-migration.integration.spec.ts +203 -0
  48. package/scripts/__tests__/migrate-embedding-data.integration.spec.ts +219 -0
  49. package/scripts/__tests__/regenerate-embeddings.integration.spec.ts +192 -0
  50. package/scripts/backup-embeddings.js +52 -61
  51. package/scripts/check-db-integrity.js +49 -25
  52. package/scripts/check-file-sizes.ts +4 -4
  53. package/scripts/check-pii-masking.ts +0 -3
  54. package/scripts/check-sql-injection.ts +0 -12
  55. package/scripts/debug-embeddings.js +74 -93
  56. package/scripts/fix-migration.js +115 -80
  57. package/scripts/fix-vector-dimensions.js +70 -89
  58. package/scripts/migrate-embedding-data.js +111 -25
  59. package/scripts/regenerate-embeddings.js +31 -15
  60. package/scripts/run-migration.js +144 -107
  61. package/scripts/safe-migration.js +192 -142
  62. package/scripts/save-work-memory.ts +6 -7
  63. package/scripts/simple-migrate.js +66 -34
  64. package/scripts/simple-update.js +147 -109
  65. package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts +0 -56
  66. package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts.map +0 -1
  67. package/dist/domains/search/algorithms/vector-search-engine-refactored.js +0 -101
  68. package/dist/domains/search/algorithms/vector-search-engine-refactored.js.map +0 -1
@@ -1,93 +1,128 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * Memento 마이그레이션 수정 스크립트
4
+ *
5
+ * 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
6
+ *
7
+ * 사용법:
8
+ * - 개발 환경: npx tsx scripts/fix-migration.js
9
+ * - 프로덕션: npm run build && node dist/scripts/fix-migration.js
10
+ */
2
11
 
3
- import Database from 'better-sqlite3';
4
- import { join } from 'path';
5
-
6
- const dbPath = join(process.cwd(), 'data', 'memory.db');
12
+ // TypeScript 소스를 직접 import (tsx로 실행 시)
13
+ // 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
14
+ import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
7
15
 
8
16
  console.log('🔧 마이그레이션 수정 중...');
9
17
 
10
- try {
11
- const db = new Database(dbPath);
12
-
13
- // 1. 현재 상태 확인
14
- console.log('📊 현재 상태 확인...');
15
- const currentSchema = db.prepare("PRAGMA table_info(memory_embedding)").all();
16
- console.log('현재 테이블 구조:');
17
- console.table(currentSchema);
18
-
19
- // 2. 컬럼이 있는지 확인
20
- const hasProvider = currentSchema.some(col => col.name === 'embedding_provider');
21
- const hasDimensions = currentSchema.some(col => col.name === 'dimensions');
22
- const hasCreatedBy = currentSchema.some(col => col.name === 'created_by');
23
-
24
- console.log(`embedding_provider: ${hasProvider ? '✅' : '❌'}`);
25
- console.log(`dimensions: ${hasDimensions ? '✅' : '❌'}`);
26
- console.log(`created_by: ${hasCreatedBy ? '✅' : '❌'}`);
18
+ /**
19
+ * 마이그레이션 수정 메인 함수
20
+ */
21
+ async function fixMigration() {
22
+ let db = null;
27
23
 
28
- // 3. 데이터 업데이트 (컬럼이 있는 경우에만)
29
- if (hasProvider && hasDimensions && hasCreatedBy) {
30
- console.log('🔄 데이터 업데이트 중...');
31
-
32
- const updateResult = db.prepare(`
33
- UPDATE memory_embedding
34
- SET
35
- embedding_provider = CASE
36
- WHEN model = 'lightweight-hybrid' THEN 'tfidf'
37
- WHEN model IS NULL OR model = '' THEN 'tfidf'
38
- ELSE 'unknown'
39
- END,
40
- dimensions = dim,
41
- created_by = 'legacy'
42
- WHERE embedding_provider IS NULL
43
- `).run();
44
-
45
- console.log(`✅ ${updateResult.changes}개 레코드 업데이트 완료`);
46
-
47
- // 4. 인덱스 추가
48
- console.log('📝 인덱스 추가 중...');
49
- db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_provider ON memory_embedding(embedding_provider)');
50
- db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_dimensions ON memory_embedding(dimensions)');
51
- db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_created_by ON memory_embedding(created_by)');
52
- console.log('✅ 인덱스 추가 완료');
24
+ try {
25
+ // 공통 모듈을 사용하여 데이터베이스 초기화
26
+ // initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
27
+ db = await initializeDatabase();
53
28
 
54
- // 5. 최종 검증
55
- console.log('🔍 최종 검증...');
56
- const validation = db.prepare(`
57
- SELECT
58
- COUNT(*) as total,
59
- COUNT(CASE WHEN embedding_provider IS NOT NULL THEN 1 END) as with_provider,
60
- COUNT(CASE WHEN dimensions IS NOT NULL THEN 1 END) as with_dimensions,
61
- COUNT(CASE WHEN created_by IS NOT NULL THEN 1 END) as with_created_by
62
- FROM memory_embedding
63
- `).get();
29
+ // 1. 현재 상태 확인
30
+ console.log('📊 현재 상태 확인...');
31
+ const currentSchema = db.prepare("PRAGMA table_info(memory_embedding)").all();
32
+ console.log('현재 테이블 구조:');
33
+ console.table(currentSchema);
64
34
 
65
- console.log('📊 검증 결과:');
66
- console.table(validation);
35
+ // 2. 컬럼이 있는지 확인
36
+ const hasProvider = currentSchema.some(col => col.name === 'embedding_provider');
37
+ const hasDimensions = currentSchema.some(col => col.name === 'dimensions');
38
+ const hasCreatedBy = currentSchema.some(col => col.name === 'created_by');
67
39
 
68
- // 6. 최종 데이터 분포
69
- const finalAnalysis = db.prepare(`
70
- SELECT
71
- embedding_provider,
72
- dimensions,
73
- COUNT(*) as count
74
- FROM memory_embedding
75
- GROUP BY embedding_provider, dimensions
76
- ORDER BY count DESC
77
- `).all();
40
+ console.log(`embedding_provider: ${hasProvider ? '✅' : '❌'}`);
41
+ console.log(`dimensions: ${hasDimensions ? '✅' : '❌'}`);
42
+ console.log(`created_by: ${hasCreatedBy ? '✅' : '❌'}`);
78
43
 
79
- console.log('\n📊 최종 데이터 분포:');
80
- console.table(finalAnalysis);
44
+ // 3. 데이터 업데이트 (컬럼이 있는 경우에만)
45
+ if (hasProvider && hasDimensions && hasCreatedBy) {
46
+ console.log('🔄 데이터 업데이트 중...');
47
+
48
+ const updateResult = db.prepare(`
49
+ UPDATE memory_embedding
50
+ SET
51
+ embedding_provider = CASE
52
+ WHEN model = 'lightweight-hybrid' THEN 'tfidf'
53
+ WHEN model IS NULL OR model = '' THEN 'tfidf'
54
+ ELSE 'unknown'
55
+ END,
56
+ dimensions = dim,
57
+ created_by = 'legacy'
58
+ WHERE embedding_provider IS NULL
59
+ `).run();
60
+
61
+ console.log(`✅ ${updateResult.changes}개 레코드 업데이트 완료`);
62
+
63
+ // 4. 인덱스 추가
64
+ console.log('📝 인덱스 추가 중...');
65
+ db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_provider ON memory_embedding(embedding_provider)');
66
+ db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_dimensions ON memory_embedding(dimensions)');
67
+ db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_created_by ON memory_embedding(created_by)');
68
+ console.log('✅ 인덱스 추가 완료');
69
+
70
+ // 5. 최종 검증
71
+ console.log('🔍 최종 검증...');
72
+ const validation = db.prepare(`
73
+ SELECT
74
+ COUNT(*) as total,
75
+ COUNT(CASE WHEN embedding_provider IS NOT NULL THEN 1 END) as with_provider,
76
+ COUNT(CASE WHEN dimensions IS NOT NULL THEN 1 END) as with_dimensions,
77
+ COUNT(CASE WHEN created_by IS NOT NULL THEN 1 END) as with_created_by
78
+ FROM memory_embedding
79
+ `).get();
80
+
81
+ console.log('📊 검증 결과:');
82
+ console.table(validation);
83
+
84
+ // 6. 최종 데이터 분포
85
+ const finalAnalysis = db.prepare(`
86
+ SELECT
87
+ embedding_provider,
88
+ dimensions,
89
+ COUNT(*) as count
90
+ FROM memory_embedding
91
+ GROUP BY embedding_provider, dimensions
92
+ ORDER BY count DESC
93
+ `).all();
94
+
95
+ console.log('\n📊 최종 데이터 분포:');
96
+ console.table(finalAnalysis);
97
+
98
+ console.log('\n🎉 마이그레이션 완료!');
99
+
100
+ } else {
101
+ console.log('❌ 필요한 컬럼이 없습니다. 스키마를 먼저 업데이트해주세요.');
102
+ console.log(' initializeDatabase가 자동으로 스키마를 생성하지만, 필요한 컬럼이 없을 수 있습니다.');
103
+ process.exit(1);
104
+ }
81
105
 
82
- console.log('\n🎉 마이그레이션 완료!');
83
-
84
- } else {
85
- console.log(' 필요한 컬럼이 없습니다. 스키마를 먼저 업데이트해주세요.');
106
+ } catch (error) {
107
+ console.error('❌ 오류 발생:', error.message);
108
+ if (error.stack) {
109
+ console.error(' 스택 트레이스:', error.stack);
110
+ }
111
+ process.exit(1);
112
+ } finally {
113
+ // 데이터베이스 연결 종료
114
+ if (db) {
115
+ closeDatabase(db);
116
+ }
86
117
  }
87
-
88
- db.close();
89
-
90
- } catch (error) {
91
- console.error('❌ 오류 발생:', error.message);
92
- process.exit(1);
93
118
  }
119
+
120
+ // 스크립트가 직접 실행될 때만 main 함수 호출
121
+ if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
122
+ fixMigration().catch((error) => {
123
+ console.error('❌ 스크립트 실행 중 오류 발생:', error);
124
+ process.exit(1);
125
+ });
126
+ }
127
+
128
+ export { fixMigration };
@@ -3,27 +3,31 @@
3
3
  /**
4
4
  * 벡터 차원 통일 스크립트
5
5
  * 모든 임베딩을 삭제하고 현재 설정에 맞는 모델로 재생성
6
+ *
7
+ * 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
8
+ *
9
+ * 사용법:
10
+ * - 개발 환경: npx tsx scripts/fix-vector-dimensions.js
11
+ * - 프로덕션: npm run build && node dist/scripts/fix-vector-dimensions.js
6
12
  */
7
13
 
8
- import Database from 'better-sqlite3';
9
- import path from 'path';
10
- import { fileURLToPath } from 'url';
14
+ // TypeScript 소스를 직접 import (tsx로 실행 시)
15
+ // 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
16
+ import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
11
17
  import { EmbeddingService } from '../dist/services/embedding-service.js';
12
18
 
13
- const __filename = fileURLToPath(import.meta.url);
14
- const __dirname = path.dirname(__filename);
15
-
16
- // 데이터베이스 경로 설정
17
- const dbPath = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'memory.db');
18
-
19
19
  async function fixVectorDimensions() {
20
20
  console.log('🔧 벡터 차원 통일 작업 시작...');
21
21
 
22
- // 데이터베이스 연결
23
- const db = new Database(dbPath);
24
- const embeddingService = new EmbeddingService();
22
+ let db = null;
25
23
 
26
24
  try {
25
+ // 공통 모듈을 사용하여 데이터베이스 초기화
26
+ // initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
27
+ db = await initializeDatabase();
28
+
29
+ const embeddingService = new EmbeddingService();
30
+
27
31
  // 1. 현재 상태 확인
28
32
  console.log('\n📊 현재 상태:');
29
33
  const currentStats = db.prepare(`
@@ -50,59 +54,39 @@ async function fixVectorDimensions() {
50
54
  process.exit(1);
51
55
  }
52
56
 
53
- // 3. 기존 임베딩 백업
54
- console.log('\n💾 기존 임베딩 백업 중...');
55
- const backupData = db.prepare(`
56
- SELECT
57
- me.memory_id,
58
- me.embedding,
59
- me.dim,
60
- me.model,
61
- me.created_at,
62
- mi.content,
63
- mi.type
64
- FROM memory_embedding me
65
- JOIN memory_item mi ON me.memory_id = mi.id
66
- `).all();
57
+ // 3. 사용자 확인
58
+ console.log('\n⚠️ 경고: 작업은 모든 임베딩을 삭제하고 재생성합니다.');
59
+ console.log('백업을 먼저 수행하는 것을 권장합니다.');
60
+ console.log('계속하려면 스크립트를 --confirm 플래그와 함께 실행하세요.');
67
61
 
68
- const backupFile = path.join(__dirname, '..', 'backup', `embeddings-backup-${new Date().toISOString().replace(/[:.]/g, '-')}.json`);
69
- const fs = await import('fs');
70
-
71
- if (!fs.existsSync(path.dirname(backupFile))) {
72
- fs.mkdirSync(path.dirname(backupFile), { recursive: true });
62
+ if (!process.argv.includes('--confirm')) {
63
+ console.log('\n❌ 확인 플래그가 없어 작업을 중단합니다.');
64
+ console.log('사용법: npx tsx scripts/fix-vector-dimensions.js --confirm');
65
+ return;
73
66
  }
74
67
 
75
- fs.writeFileSync(backupFile, JSON.stringify({
76
- timestamp: new Date().toISOString(),
77
- totalEmbeddings: backupData.length,
78
- embeddings: backupData.map(emb => ({
79
- memory_id: emb.memory_id,
80
- content: emb.content,
81
- type: emb.type,
82
- embedding: JSON.parse(emb.embedding),
83
- dim: emb.dim,
84
- model: emb.model,
85
- created_at: emb.created_at
86
- }))
87
- }, null, 2));
88
-
89
- console.log(`✅ 백업 완료: ${backupFile}`);
90
-
91
- // 4. 기존 임베딩 삭제
92
- console.log('\n🗑️ 기존 임베딩 삭제 중...');
93
- const deleteResult = db.prepare('DELETE FROM memory_embedding').run();
94
- console.log(`✅ 삭제 완료: ${deleteResult.changes}개 행 삭제`);
95
-
96
- // 5. 모든 메모리에 대해 새로운 임베딩 생성
97
- console.log('\n🔄 새로운 임베딩 생성 중...');
68
+ // 4. 모든 기억 조회
69
+ console.log('\n📋 기억 조회 중...');
98
70
  const memories = db.prepare(`
99
- SELECT id, content, type, importance, created_at
71
+ SELECT id, content, type
100
72
  FROM memory_item
101
73
  ORDER BY created_at
102
74
  `).all();
103
75
 
104
- console.log(`📊 처리할 메모리 개수: ${memories.length}`);
76
+ console.log(`📊 처리할 기억 개수: ${memories.length}`);
77
+
78
+ if (memories.length === 0) {
79
+ console.log('⚠️ 재생성할 기억이 없습니다.');
80
+ return;
81
+ }
105
82
 
83
+ // 5. 기존 임베딩 삭제
84
+ console.log('\n🗑️ 기존 임베딩 삭제 중...');
85
+ const deleteResult = db.prepare('DELETE FROM memory_embedding').run();
86
+ console.log(`✅ ${deleteResult.changes}개 임베딩 삭제 완료`);
87
+
88
+ // 6. 새로운 임베딩 생성
89
+ console.log('\n🔄 새로운 임베딩 생성 중...');
106
90
  let successCount = 0;
107
91
  let errorCount = 0;
108
92
 
@@ -136,7 +120,7 @@ async function fixVectorDimensions() {
136
120
  console.log(`${progress} ✅ 완료: ${memory.id} (${embeddingResult.embedding.length}차원)`);
137
121
  successCount++;
138
122
 
139
- // API 제한을 위한 대기
123
+ // API 제한을 위한 대기 (필요시)
140
124
  if (i % 10 === 0 && i > 0) {
141
125
  console.log('⏳ API 제한 대기 중...');
142
126
  await new Promise(resolve => setTimeout(resolve, 1000));
@@ -144,63 +128,60 @@ async function fixVectorDimensions() {
144
128
 
145
129
  } catch (error) {
146
130
  console.error(`${progress} ❌ 오류: ${memory.id}`, error.message);
131
+ if (error.stack) {
132
+ console.error(' 스택 트레이스:', error.stack);
133
+ }
147
134
  errorCount++;
148
135
  }
149
136
  }
150
137
 
151
- // 6. 최종 검증
152
- console.log('\n🔍 최종 검증:');
138
+ // 7. 결과 통계
139
+ console.log('\n📊 작업 완료!');
140
+ console.log(`✅ 성공: ${successCount}개`);
141
+ console.log(`❌ 실패: ${errorCount}개`);
142
+ console.log(`📈 성공률: ${((successCount / memories.length) * 100).toFixed(1)}%`);
143
+
144
+ // 8. 최종 검증
153
145
  const finalStats = db.prepare(`
154
146
  SELECT
155
147
  COUNT(*) as total,
156
- AVG(dim) as avg_dim,
157
- MIN(dim) as min_dim,
158
- MAX(dim) as max_dim,
159
- COUNT(DISTINCT dim) as unique_dims
148
+ COUNT(DISTINCT dim) as unique_dims,
149
+ AVG(dim) as avg_dim
160
150
  FROM memory_embedding
161
151
  `).get();
162
152
 
153
+ console.log('\n🔍 최종 검증:');
163
154
  console.log(`- 총 임베딩: ${finalStats.total}개`);
164
- console.log(`- 평균 차원: ${finalStats.avg_dim?.toFixed(1) || 'N/A'}`);
165
- console.log(`- 최소 차원: ${finalStats.min_dim || 'N/A'}`);
166
- console.log(`- 최대 차원: ${finalStats.max_dim || 'N/A'}`);
167
155
  console.log(`- 고유 차원 수: ${finalStats.unique_dims}개`);
156
+ console.log(`- 평균 차원: ${finalStats.avg_dim?.toFixed(1) || 'N/A'}`);
168
157
 
169
- // 차원 일치성 확인
170
158
  const expectedDim = modelInfo.dimensions;
171
- const mismatchedDims = db.prepare(`
172
- SELECT COUNT(*) as count FROM memory_embedding WHERE dim != ?
173
- `).get(expectedDim);
174
-
175
- if (mismatchedDims.count > 0) {
176
- console.warn(`⚠️ 차원 불일치 발견: ${mismatchedDims.count}개`);
177
- } else {
159
+ if (finalStats.unique_dims === 1 && finalStats.avg_dim === expectedDim) {
178
160
  console.log('✅ 모든 임베딩의 차원이 일치합니다!');
179
- }
180
-
181
- // 7. 결과 요약
182
- console.log('\n📊 작업 완료 요약:');
183
- console.log(`✅ 성공: ${successCount}개`);
184
- console.log(`❌ 실패: ${errorCount}개`);
185
- console.log(`📈 성공률: ${((successCount / memories.length) * 100).toFixed(1)}%`);
186
-
187
- if (finalStats.unique_dims === 1) {
188
- console.log('🎉 벡터 차원 통일 완료! 이제 검색이 정상 작동할 것입니다.');
189
161
  } else {
190
- console.log('⚠️ 여전히 차원 불일치가 있습니다. 추가 확인이 필요합니다.');
162
+ console.warn(`⚠️ 차원 불일치: 예상 ${expectedDim}차원, 실제 ${finalStats.avg_dim?.toFixed(1) || 'N/A'}차원`);
191
163
  }
192
164
 
193
165
  } catch (error) {
194
- console.error('❌ 벡터 차원 통일 실패:', error);
166
+ console.error('❌ 작업 실패:', error.message);
167
+ if (error.stack) {
168
+ console.error(' 스택 트레이스:', error.stack);
169
+ }
195
170
  process.exit(1);
196
171
  } finally {
197
- db.close();
172
+ // 데이터베이스 연결 종료
173
+ if (db) {
174
+ closeDatabase(db);
175
+ }
198
176
  }
199
177
  }
200
178
 
201
179
  // 스크립트 실행
202
- if (import.meta.url === `file://${process.argv[1]}`) {
203
- fixVectorDimensions().catch(console.error);
180
+ if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
181
+ fixVectorDimensions().catch((error) => {
182
+ console.error('❌ 스크립트 실행 중 오류 발생:', error);
183
+ process.exit(1);
184
+ });
204
185
  }
205
186
 
206
187
  export { fixVectorDimensions };
@@ -3,12 +3,21 @@
3
3
  /**
4
4
  * 임베딩 데이터 마이그레이션 스크립트
5
5
  * 기존 임베딩 데이터를 새로운 통합 시스템으로 마이그레이션
6
+ *
7
+ * 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
8
+ *
9
+ * 사용법:
10
+ * - 개발 환경: npx tsx scripts/migrate-embedding-data.js migrate
11
+ * - 프로덕션: npm run build && node dist/scripts/migrate-embedding-data.js migrate
6
12
  */
7
13
 
14
+ // TypeScript 소스를 직접 import (tsx로 실행 시)
15
+ // 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
16
+ import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
8
17
  import Database from 'better-sqlite3';
9
18
  import { fileURLToPath } from 'url';
10
19
  import { dirname, join } from 'path';
11
- import { readFileSync } from 'fs';
20
+ import { readFileSync, copyFileSync } from 'fs';
12
21
  import { existsSync } from 'fs';
13
22
 
14
23
  const __filename = fileURLToPath(import.meta.url);
@@ -16,17 +25,21 @@ const __dirname = dirname(__filename);
16
25
 
17
26
  class EmbeddingMigration {
18
27
  constructor() {
19
- this.dbPath = join(__dirname, '..', 'data', 'memory.db');
20
- this.backupPath = join(__dirname, '..', 'data', `memory-backup-${Date.now()}.db`);
28
+ // DB 경로는 initializeDatabase가 환경 변수에서 가져옴
29
+ // 백업 경로는 data 디렉토리에 생성
30
+ this.backupPath = join(process.cwd(), 'data', `memory-backup-${Date.now()}.db`);
21
31
  this.db = null;
22
32
  }
23
33
 
24
34
  /**
25
35
  * 데이터베이스 연결
36
+ * 공통 모듈을 사용하여 일관된 초기화 보장
26
37
  */
27
- connect() {
38
+ async connect() {
28
39
  try {
29
- this.db = new Database(this.dbPath);
40
+ // 공통 모듈을 사용하여 데이터베이스 초기화
41
+ // initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
42
+ this.db = await initializeDatabase();
30
43
  console.log('✅ 데이터베이스 연결 성공');
31
44
  } catch (error) {
32
45
  console.error('❌ 데이터베이스 연결 실패:', error);
@@ -39,9 +52,19 @@ class EmbeddingMigration {
39
52
  */
40
53
  createBackup() {
41
54
  try {
42
- const db = new Database(this.dbPath);
43
- db.backup(this.backupPath);
44
- db.close();
55
+ if (!this.db) {
56
+ throw new Error('데이터베이스가 연결되지 않았습니다. connect()를 먼저 호출하세요.');
57
+ }
58
+
59
+ // 백업 디렉토리 생성
60
+ const backupDir = dirname(this.backupPath);
61
+ if (!existsSync(backupDir)) {
62
+ const { mkdirSync } = require('fs');
63
+ mkdirSync(backupDir, { recursive: true });
64
+ }
65
+
66
+ // 백업 생성
67
+ this.db.backup(this.backupPath);
45
68
  console.log(`✅ 백업 생성 완료: ${this.backupPath}`);
46
69
  } catch (error) {
47
70
  console.error('❌ 백업 생성 실패:', error);
@@ -76,11 +99,14 @@ class EmbeddingMigration {
76
99
 
77
100
  } catch (error) {
78
101
  console.error('❌ 마이그레이션 실패:', error);
79
- console.log(`🔄 백업에서 복원하려면: cp ${this.backupPath} ${this.dbPath}`);
102
+ if (error.stack) {
103
+ console.error(' 스택 트레이스:', error.stack);
104
+ }
105
+ console.log(`🔄 백업에서 복원하려면: cp ${this.backupPath} ${join(process.cwd(), 'data', 'memory.db')}`);
80
106
  throw error;
81
107
  } finally {
82
108
  if (this.db) {
83
- this.db.close();
109
+ closeDatabase(this.db);
84
110
  }
85
111
  }
86
112
  }
@@ -122,11 +148,36 @@ class EmbeddingMigration {
122
148
  */
123
149
  async runDirectMigration() {
124
150
  try {
151
+ if (!this.db) {
152
+ throw new Error('데이터베이스가 연결되지 않았습니다.');
153
+ }
154
+
125
155
  // 1. 새로운 컬럼 추가
126
156
  console.log('📝 컬럼 추가 중...');
127
- this.db.exec('ALTER TABLE memory_embedding ADD COLUMN embedding_provider TEXT');
128
- this.db.exec('ALTER TABLE memory_embedding ADD COLUMN dimensions INTEGER');
129
- this.db.exec('ALTER TABLE memory_embedding ADD COLUMN created_by TEXT DEFAULT "migration"');
157
+ try {
158
+ this.db.exec('ALTER TABLE memory_embedding ADD COLUMN embedding_provider TEXT');
159
+ } catch (error) {
160
+ // 컬럼이 이미 존재할 수 있음
161
+ if (!error.message.includes('duplicate column')) {
162
+ throw error;
163
+ }
164
+ }
165
+
166
+ try {
167
+ this.db.exec('ALTER TABLE memory_embedding ADD COLUMN dimensions INTEGER');
168
+ } catch (error) {
169
+ if (!error.message.includes('duplicate column')) {
170
+ throw error;
171
+ }
172
+ }
173
+
174
+ try {
175
+ this.db.exec('ALTER TABLE memory_embedding ADD COLUMN created_by TEXT DEFAULT "migration"');
176
+ } catch (error) {
177
+ if (!error.message.includes('duplicate column')) {
178
+ throw error;
179
+ }
180
+ }
130
181
 
131
182
  // 2. 기존 데이터 업데이트
132
183
  console.log('📝 기존 데이터 업데이트 중...');
@@ -160,6 +211,10 @@ class EmbeddingMigration {
160
211
  * 기존 데이터 분석
161
212
  */
162
213
  analyzeExistingData() {
214
+ if (!this.db) {
215
+ throw new Error('데이터베이스가 연결되지 않았습니다.');
216
+ }
217
+
163
218
  const analysis = {};
164
219
 
165
220
  // 차원별 분포
@@ -206,6 +261,10 @@ class EmbeddingMigration {
206
261
  * 메타데이터 업데이트
207
262
  */
208
263
  updateMetadata() {
264
+ if (!this.db) {
265
+ throw new Error('데이터베이스가 연결되지 않았습니다.');
266
+ }
267
+
209
268
  const updateStmt = this.db.prepare(`
210
269
  UPDATE memory_embedding
211
270
  SET
@@ -240,6 +299,10 @@ class EmbeddingMigration {
240
299
  * 마이그레이션 검증
241
300
  */
242
301
  validateMigration() {
302
+ if (!this.db) {
303
+ throw new Error('데이터베이스가 연결되지 않았습니다.');
304
+ }
305
+
243
306
  console.log('🔍 마이그레이션 검증 중...');
244
307
 
245
308
  const validation = this.db.prepare(`
@@ -265,41 +328,64 @@ class EmbeddingMigration {
265
328
  */
266
329
  rollback() {
267
330
  try {
268
- const fs = require('fs');
269
- fs.copyFileSync(this.backupPath, this.dbPath);
331
+ const dbPath = join(process.cwd(), 'data', 'memory.db');
332
+ copyFileSync(this.backupPath, dbPath);
270
333
  console.log('🔄 백업에서 복원 완료');
271
334
  } catch (error) {
272
335
  console.error('❌ 롤백 실패:', error);
336
+ throw error;
273
337
  }
274
338
  }
275
339
  }
276
340
 
277
341
  // CLI 실행
278
- if (import.meta.url === `file://${process.argv[1]}`) {
342
+ if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
279
343
  const migration = new EmbeddingMigration();
280
344
 
281
345
  const command = process.argv[2];
282
346
 
283
347
  switch (command) {
284
348
  case 'migrate':
285
- migration.connect();
286
- migration.migrate();
349
+ migration.connect().then(() => {
350
+ migration.migrate().catch((error) => {
351
+ console.error('❌ 마이그레이션 실행 중 오류:', error);
352
+ process.exit(1);
353
+ });
354
+ }).catch((error) => {
355
+ console.error('❌ 데이터베이스 연결 중 오류:', error);
356
+ process.exit(1);
357
+ });
287
358
  break;
288
359
  case 'rollback':
289
360
  migration.rollback();
290
361
  break;
291
362
  case 'analyze':
292
- migration.connect();
293
- const analysis = migration.analyzeExistingData();
294
- console.log('📊 데이터 분석 결과:', JSON.stringify(analysis, null, 2));
363
+ migration.connect().then(() => {
364
+ try {
365
+ const analysis = migration.analyzeExistingData();
366
+ console.log('📊 데이터 분석 결과:', JSON.stringify(analysis, null, 2));
367
+ if (migration.db) {
368
+ closeDatabase(migration.db);
369
+ }
370
+ } catch (error) {
371
+ console.error('❌ 분석 중 오류:', error);
372
+ if (migration.db) {
373
+ closeDatabase(migration.db);
374
+ }
375
+ process.exit(1);
376
+ }
377
+ }).catch((error) => {
378
+ console.error('❌ 데이터베이스 연결 중 오류:', error);
379
+ process.exit(1);
380
+ });
295
381
  break;
296
382
  default:
297
383
  console.log(`
298
384
  사용법:
299
- node migrate-embedding-data.js migrate # 마이그레이션 실행
300
- node migrate-embedding-data.js rollback # 백업에서 복원
301
- node migrate-embedding-data.js analyze # 데이터 분석만
302
- node migrate-embedding-data.js migrate --regenerate # 임베딩 재생성 모드
385
+ npx tsx scripts/migrate-embedding-data.js migrate # 마이그레이션 실행
386
+ npx tsx scripts/migrate-embedding-data.js rollback # 백업에서 복원
387
+ npx tsx scripts/migrate-embedding-data.js analyze # 데이터 분석만
388
+ npx tsx scripts/migrate-embedding-data.js migrate --regenerate # 임베딩 재생성 모드
303
389
  `);
304
390
  }
305
391
  }