a2a-memory 0.1.2 → 0.6.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.
Files changed (191) hide show
  1. package/README.md +262 -45
  2. package/dist/adapters/anthropic.d.ts +69 -0
  3. package/dist/adapters/anthropic.d.ts.map +1 -0
  4. package/dist/adapters/anthropic.js +116 -0
  5. package/dist/adapters/anthropic.js.map +1 -0
  6. package/dist/claude/sync.d.ts +57 -0
  7. package/dist/claude/sync.d.ts.map +1 -0
  8. package/dist/claude/sync.js +201 -0
  9. package/dist/claude/sync.js.map +1 -0
  10. package/dist/cli/commands/add.d.ts +8 -0
  11. package/dist/cli/commands/add.d.ts.map +1 -0
  12. package/dist/cli/commands/add.js +45 -0
  13. package/dist/cli/commands/add.js.map +1 -0
  14. package/dist/cli/commands/claude-sync.d.ts +11 -0
  15. package/dist/cli/commands/claude-sync.d.ts.map +1 -0
  16. package/dist/cli/commands/claude-sync.js +69 -0
  17. package/dist/cli/commands/claude-sync.js.map +1 -0
  18. package/dist/cli/commands/cleanup.d.ts +8 -0
  19. package/dist/cli/commands/cleanup.d.ts.map +1 -0
  20. package/dist/cli/commands/cleanup.js +82 -0
  21. package/dist/cli/commands/cleanup.js.map +1 -0
  22. package/dist/cli/commands/edit.d.ts +8 -0
  23. package/dist/cli/commands/edit.d.ts.map +1 -0
  24. package/dist/cli/commands/edit.js +66 -0
  25. package/dist/cli/commands/edit.js.map +1 -0
  26. package/dist/cli/commands/embed.d.ts +8 -0
  27. package/dist/cli/commands/embed.d.ts.map +1 -0
  28. package/dist/cli/commands/embed.js +93 -0
  29. package/dist/cli/commands/embed.js.map +1 -0
  30. package/dist/cli/commands/health.d.ts +8 -0
  31. package/dist/cli/commands/health.d.ts.map +1 -0
  32. package/dist/cli/commands/health.js +108 -0
  33. package/dist/cli/commands/health.js.map +1 -0
  34. package/dist/cli/commands/rm.d.ts +8 -0
  35. package/dist/cli/commands/rm.d.ts.map +1 -0
  36. package/dist/cli/commands/rm.js +60 -0
  37. package/dist/cli/commands/rm.js.map +1 -0
  38. package/dist/cli/commands/sync.d.ts.map +1 -1
  39. package/dist/cli/commands/sync.js +52 -0
  40. package/dist/cli/commands/sync.js.map +1 -1
  41. package/dist/cli/commands/team.d.ts +7 -0
  42. package/dist/cli/commands/team.d.ts.map +1 -0
  43. package/dist/cli/commands/team.js +144 -0
  44. package/dist/cli/commands/team.js.map +1 -0
  45. package/dist/cli/index.d.ts +5 -0
  46. package/dist/cli/index.d.ts.map +1 -1
  47. package/dist/cli/index.js +44 -1
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/config/manager.d.ts +16 -0
  50. package/dist/config/manager.d.ts.map +1 -1
  51. package/dist/config/manager.js +170 -5
  52. package/dist/config/manager.js.map +1 -1
  53. package/dist/db/database.d.ts +28 -3
  54. package/dist/db/database.d.ts.map +1 -1
  55. package/dist/db/database.js +291 -64
  56. package/dist/db/database.js.map +1 -1
  57. package/dist/embedding/index.d.ts +21 -0
  58. package/dist/embedding/index.d.ts.map +1 -0
  59. package/dist/embedding/index.js +29 -0
  60. package/dist/embedding/index.js.map +1 -0
  61. package/dist/embedding/local-provider.d.ts +40 -0
  62. package/dist/embedding/local-provider.d.ts.map +1 -0
  63. package/dist/embedding/local-provider.js +157 -0
  64. package/dist/embedding/local-provider.js.map +1 -0
  65. package/dist/embedding/openai-provider.d.ts +31 -0
  66. package/dist/embedding/openai-provider.d.ts.map +1 -0
  67. package/dist/embedding/openai-provider.js +92 -0
  68. package/dist/embedding/openai-provider.js.map +1 -0
  69. package/dist/embedding/quantization.d.ts +34 -0
  70. package/dist/embedding/quantization.d.ts.map +1 -0
  71. package/dist/embedding/quantization.js +89 -0
  72. package/dist/embedding/quantization.js.map +1 -0
  73. package/dist/extraction/extractor.d.ts +11 -1
  74. package/dist/extraction/extractor.d.ts.map +1 -1
  75. package/dist/extraction/extractor.js +63 -20
  76. package/dist/extraction/extractor.js.map +1 -1
  77. package/dist/extraction/filter.d.ts.map +1 -1
  78. package/dist/extraction/filter.js +25 -3
  79. package/dist/extraction/filter.js.map +1 -1
  80. package/dist/extraction/scorer.d.ts +2 -0
  81. package/dist/extraction/scorer.d.ts.map +1 -1
  82. package/dist/extraction/scorer.js +23 -1
  83. package/dist/extraction/scorer.js.map +1 -1
  84. package/dist/extraction/similarity.d.ts +25 -0
  85. package/dist/extraction/similarity.d.ts.map +1 -0
  86. package/dist/extraction/similarity.js +85 -0
  87. package/dist/extraction/similarity.js.map +1 -0
  88. package/dist/hooks/post-tool-use.d.ts.map +1 -1
  89. package/dist/hooks/post-tool-use.js +88 -53
  90. package/dist/hooks/post-tool-use.js.map +1 -1
  91. package/dist/hooks/session-end.d.ts +1 -0
  92. package/dist/hooks/session-end.d.ts.map +1 -1
  93. package/dist/hooks/session-end.js +173 -33
  94. package/dist/hooks/session-end.js.map +1 -1
  95. package/dist/hooks/session-start.d.ts +2 -2
  96. package/dist/hooks/session-start.d.ts.map +1 -1
  97. package/dist/hooks/session-start.js +172 -22
  98. package/dist/hooks/session-start.js.map +1 -1
  99. package/dist/hooks/shared.d.ts +18 -0
  100. package/dist/hooks/shared.d.ts.map +1 -0
  101. package/dist/hooks/shared.js +46 -0
  102. package/dist/hooks/shared.js.map +1 -0
  103. package/dist/i18n/index.d.ts +3 -0
  104. package/dist/i18n/index.d.ts.map +1 -0
  105. package/dist/i18n/index.js +2 -0
  106. package/dist/i18n/index.js.map +1 -0
  107. package/dist/i18n/messages.d.ts +82 -0
  108. package/dist/i18n/messages.d.ts.map +1 -0
  109. package/dist/i18n/messages.js +150 -0
  110. package/dist/i18n/messages.js.map +1 -0
  111. package/dist/index.d.ts +25 -5
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +20 -3
  114. package/dist/index.js.map +1 -1
  115. package/dist/lifecycle/cleanup.d.ts +25 -0
  116. package/dist/lifecycle/cleanup.d.ts.map +1 -0
  117. package/dist/lifecycle/cleanup.js +52 -0
  118. package/dist/lifecycle/cleanup.js.map +1 -0
  119. package/dist/lifecycle/index.d.ts +15 -0
  120. package/dist/lifecycle/index.d.ts.map +1 -0
  121. package/dist/lifecycle/index.js +12 -0
  122. package/dist/lifecycle/index.js.map +1 -0
  123. package/dist/lifecycle/quality-scorer.d.ts +19 -0
  124. package/dist/lifecycle/quality-scorer.d.ts.map +1 -0
  125. package/dist/lifecycle/quality-scorer.js +40 -0
  126. package/dist/lifecycle/quality-scorer.js.map +1 -0
  127. package/dist/lifecycle/tiering.d.ts +50 -0
  128. package/dist/lifecycle/tiering.d.ts.map +1 -0
  129. package/dist/lifecycle/tiering.js +235 -0
  130. package/dist/lifecycle/tiering.js.map +1 -0
  131. package/dist/llm/client.d.ts +37 -0
  132. package/dist/llm/client.d.ts.map +1 -0
  133. package/dist/llm/client.js +154 -0
  134. package/dist/llm/client.js.map +1 -0
  135. package/dist/llm/index.d.ts +6 -0
  136. package/dist/llm/index.d.ts.map +1 -0
  137. package/dist/llm/index.js +5 -0
  138. package/dist/llm/index.js.map +1 -0
  139. package/dist/search/index.d.ts +8 -0
  140. package/dist/search/index.d.ts.map +1 -0
  141. package/dist/search/index.js +7 -0
  142. package/dist/search/index.js.map +1 -0
  143. package/dist/search/ranker.d.ts +30 -0
  144. package/dist/search/ranker.d.ts.map +1 -0
  145. package/dist/search/ranker.js +91 -0
  146. package/dist/search/ranker.js.map +1 -0
  147. package/dist/sync/client.d.ts +103 -0
  148. package/dist/sync/client.d.ts.map +1 -1
  149. package/dist/sync/client.js +186 -6
  150. package/dist/sync/client.js.map +1 -1
  151. package/dist/sync/encryption.d.ts +72 -0
  152. package/dist/sync/encryption.d.ts.map +1 -0
  153. package/dist/sync/encryption.js +203 -0
  154. package/dist/sync/encryption.js.map +1 -0
  155. package/dist/sync/index.d.ts +9 -0
  156. package/dist/sync/index.d.ts.map +1 -1
  157. package/dist/sync/index.js +6 -0
  158. package/dist/sync/index.js.map +1 -1
  159. package/dist/sync/queue.d.ts +49 -0
  160. package/dist/sync/queue.d.ts.map +1 -0
  161. package/dist/sync/queue.js +112 -0
  162. package/dist/sync/queue.js.map +1 -0
  163. package/dist/sync/scheduler.d.ts +69 -0
  164. package/dist/sync/scheduler.d.ts.map +1 -0
  165. package/dist/sync/scheduler.js +140 -0
  166. package/dist/sync/scheduler.js.map +1 -0
  167. package/dist/sync/synchronizer.d.ts +19 -5
  168. package/dist/sync/synchronizer.d.ts.map +1 -1
  169. package/dist/sync/synchronizer.js +128 -70
  170. package/dist/sync/synchronizer.js.map +1 -1
  171. package/dist/sync/team-synchronizer.d.ts +43 -0
  172. package/dist/sync/team-synchronizer.d.ts.map +1 -0
  173. package/dist/sync/team-synchronizer.js +126 -0
  174. package/dist/sync/team-synchronizer.js.map +1 -0
  175. package/dist/sync/vector-clock.d.ts +23 -0
  176. package/dist/sync/vector-clock.d.ts.map +1 -0
  177. package/dist/sync/vector-clock.js +40 -0
  178. package/dist/sync/vector-clock.js.map +1 -0
  179. package/dist/types/index.d.ts +74 -2
  180. package/dist/types/index.d.ts.map +1 -1
  181. package/dist/types/index.js +56 -4
  182. package/dist/types/index.js.map +1 -1
  183. package/dist/utils/keychain.d.ts +50 -0
  184. package/dist/utils/keychain.d.ts.map +1 -0
  185. package/dist/utils/keychain.js +166 -0
  186. package/dist/utils/keychain.js.map +1 -0
  187. package/dist/utils/logger.d.ts +16 -0
  188. package/dist/utils/logger.d.ts.map +1 -0
  189. package/dist/utils/logger.js +118 -0
  190. package/dist/utils/logger.js.map +1 -0
  191. package/package.json +1 -1
@@ -0,0 +1,203 @@
1
+ /**
2
+ * E2E Encryption Module
3
+ *
4
+ * AES-256-GCM 대칭 암호화를 사용하여 메모리 content/embedding 보호
5
+ * Node.js 내장 crypto 모듈만 사용 (외부 의존성 없음)
6
+ */
7
+ import { randomBytes, createCipheriv, createDecipheriv, pbkdf2Sync } from 'node:crypto';
8
+ // ============================================================
9
+ // Constants
10
+ // ============================================================
11
+ const ALGORITHM = 'aes-256-gcm';
12
+ const KEY_LENGTH = 32; // 256 bits
13
+ const IV_LENGTH = 12; // GCM recommended IV length
14
+ const TAG_LENGTH = 16; // GCM auth tag length
15
+ const SALT_LENGTH = 16;
16
+ const PBKDF2_ITERATIONS = 100000;
17
+ const PBKDF2_DIGEST = 'sha512';
18
+ // ============================================================
19
+ // Key Management
20
+ // ============================================================
21
+ /**
22
+ * 랜덤 암호화 키 생성 (32 bytes)
23
+ */
24
+ export function generateEncryptionKey() {
25
+ return randomBytes(KEY_LENGTH);
26
+ }
27
+ /**
28
+ * PBKDF2로 사용자 비밀번호에서 키 유도
29
+ * @param password - 사용자 입력 비밀번호
30
+ * @param salt - 선택적 salt (재현성 필요 시 제공)
31
+ * @returns 키와 salt
32
+ */
33
+ export function deriveKey(password, salt) {
34
+ const actualSalt = salt ?? randomBytes(SALT_LENGTH);
35
+ const key = pbkdf2Sync(password, actualSalt, PBKDF2_ITERATIONS, KEY_LENGTH, PBKDF2_DIGEST);
36
+ return {
37
+ key,
38
+ salt: actualSalt,
39
+ };
40
+ }
41
+ // ============================================================
42
+ // Content Encryption
43
+ // ============================================================
44
+ /**
45
+ * 텍스트 암호화 (AES-256-GCM)
46
+ * @param plaintext - 원본 텍스트
47
+ * @param key - 32 bytes 암호화 키
48
+ * @returns 암호화된 페이로드
49
+ */
50
+ export function encryptContent(plaintext, key) {
51
+ if (key.length !== KEY_LENGTH) {
52
+ throw new Error(`암호화 키는 ${KEY_LENGTH} bytes여야 합니다 (현재: ${key.length})`);
53
+ }
54
+ const iv = randomBytes(IV_LENGTH);
55
+ const cipher = createCipheriv(ALGORITHM, key, iv);
56
+ const plaintextBuffer = Buffer.from(plaintext, 'utf-8');
57
+ const encrypted = Buffer.concat([
58
+ cipher.update(plaintextBuffer),
59
+ cipher.final(),
60
+ ]);
61
+ const tag = cipher.getAuthTag();
62
+ return {
63
+ ciphertext: encrypted.toString('base64'),
64
+ iv: iv.toString('base64'),
65
+ tag: tag.toString('base64'),
66
+ };
67
+ }
68
+ /**
69
+ * 텍스트 복호화 (AES-256-GCM)
70
+ * @param encrypted - 암호화된 페이로드
71
+ * @param key - 32 bytes 암호화 키
72
+ * @returns 원본 텍스트
73
+ * @throws 키가 잘못되었거나 데이터가 변조된 경우
74
+ */
75
+ export function decryptContent(encrypted, key) {
76
+ if (key.length !== KEY_LENGTH) {
77
+ throw new Error(`암호화 키는 ${KEY_LENGTH} bytes여야 합니다 (현재: ${key.length})`);
78
+ }
79
+ const iv = Buffer.from(encrypted.iv, 'base64');
80
+ const ciphertext = Buffer.from(encrypted.ciphertext, 'base64');
81
+ const tag = Buffer.from(encrypted.tag, 'base64');
82
+ const decipher = createDecipheriv(ALGORITHM, key, iv);
83
+ decipher.setAuthTag(tag);
84
+ try {
85
+ const decrypted = Buffer.concat([
86
+ decipher.update(ciphertext),
87
+ decipher.final(),
88
+ ]);
89
+ return decrypted.toString('utf-8');
90
+ }
91
+ catch (error) {
92
+ if (error instanceof Error) {
93
+ throw new Error(`복호화 실패: ${error.message} (잘못된 키 또는 데이터 변조)`);
94
+ }
95
+ throw error;
96
+ }
97
+ }
98
+ // ============================================================
99
+ // Embedding Encryption
100
+ // ============================================================
101
+ /**
102
+ * 임베딩 벡터 암호화
103
+ * @param embedding - number[] 벡터
104
+ * @param key - 32 bytes 암호화 키
105
+ * @returns base64 암호화된 문자열
106
+ */
107
+ export function encryptEmbedding(embedding, key) {
108
+ // Float64Array로 변환하여 바이너리 직렬화
109
+ const float64Array = new Float64Array(embedding);
110
+ const buffer = Buffer.from(float64Array.buffer);
111
+ const iv = randomBytes(IV_LENGTH);
112
+ const cipher = createCipheriv(ALGORITHM, key, iv);
113
+ const encrypted = Buffer.concat([
114
+ cipher.update(buffer),
115
+ cipher.final(),
116
+ ]);
117
+ const tag = cipher.getAuthTag();
118
+ // iv + tag + ciphertext를 하나의 버퍼로 결합
119
+ const combined = Buffer.concat([iv, tag, encrypted]);
120
+ return combined.toString('base64');
121
+ }
122
+ /**
123
+ * 임베딩 벡터 복호화
124
+ * @param encrypted - base64 암호화된 문자열
125
+ * @param key - 32 bytes 암호화 키
126
+ * @returns number[] 벡터
127
+ */
128
+ export function decryptEmbedding(encrypted, key) {
129
+ const combined = Buffer.from(encrypted, 'base64');
130
+ // iv (12) + tag (16) + ciphertext
131
+ if (combined.length < IV_LENGTH + TAG_LENGTH) {
132
+ throw new Error('암호화된 임베딩 데이터가 너무 짧습니다');
133
+ }
134
+ const iv = combined.subarray(0, IV_LENGTH);
135
+ const tag = combined.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
136
+ const ciphertext = combined.subarray(IV_LENGTH + TAG_LENGTH);
137
+ const decipher = createDecipheriv(ALGORITHM, key, iv);
138
+ decipher.setAuthTag(tag);
139
+ try {
140
+ const decrypted = Buffer.concat([
141
+ decipher.update(ciphertext),
142
+ decipher.final(),
143
+ ]);
144
+ // Buffer를 Float64Array로 변환
145
+ const float64Array = new Float64Array(decrypted.buffer, decrypted.byteOffset, decrypted.byteLength / Float64Array.BYTES_PER_ELEMENT);
146
+ return Array.from(float64Array);
147
+ }
148
+ catch (error) {
149
+ if (error instanceof Error) {
150
+ throw new Error(`임베딩 복호화 실패: ${error.message}`);
151
+ }
152
+ throw error;
153
+ }
154
+ }
155
+ // ============================================================
156
+ // Utilities
157
+ // ============================================================
158
+ /**
159
+ * EncryptedPayload 검증
160
+ */
161
+ export function isValidEncryptedPayload(obj) {
162
+ if (typeof obj !== 'object' || obj === null)
163
+ return false;
164
+ const payload = obj;
165
+ return (typeof payload.ciphertext === 'string' &&
166
+ typeof payload.iv === 'string' &&
167
+ typeof payload.tag === 'string');
168
+ }
169
+ /**
170
+ * 암호화된 컨텐츠를 JSON으로 래핑
171
+ */
172
+ export function wrapEncryptedContent(encrypted) {
173
+ return JSON.stringify({
174
+ encrypted: true,
175
+ data: encrypted.ciphertext,
176
+ iv: encrypted.iv,
177
+ tag: encrypted.tag,
178
+ });
179
+ }
180
+ /**
181
+ * JSON에서 암호화된 컨텐츠 추출
182
+ */
183
+ export function unwrapEncryptedContent(wrapped) {
184
+ try {
185
+ const parsed = JSON.parse(wrapped);
186
+ if (parsed.encrypted !== true)
187
+ return null;
188
+ if (typeof parsed.data !== 'string' ||
189
+ typeof parsed.iv !== 'string' ||
190
+ typeof parsed.tag !== 'string') {
191
+ return null;
192
+ }
193
+ return {
194
+ ciphertext: parsed.data,
195
+ iv: parsed.iv,
196
+ tag: parsed.tag,
197
+ };
198
+ }
199
+ catch {
200
+ return null;
201
+ }
202
+ }
203
+ //# sourceMappingURL=encryption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/sync/encryption.ts"],"names":[],"mappings":"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"}
@@ -5,4 +5,13 @@ export { A2AClient } from './client.js';
5
5
  export type { A2AClientConfig, RemoteMemory } from './client.js';
6
6
  export { MemorySynchronizer } from './synchronizer.js';
7
7
  export type { SyncResult } from './synchronizer.js';
8
+ export { SyncQueue, mapCategory, mapTier } from './queue.js';
9
+ export type { QueueFlushResult, FlushOptions } from './queue.js';
10
+ export { LocalVectorClock } from './vector-clock.js';
11
+ export { TeamSynchronizer } from './team-synchronizer.js';
12
+ export type { TeamSyncResult, TeamSyncStatus } from './team-synchronizer.js';
13
+ export { SyncScheduler } from './scheduler.js';
14
+ export type { SyncSchedulerOptions, SyncSchedulerStatus } from './scheduler.js';
15
+ export { encryptContent, decryptContent, encryptEmbedding, decryptEmbedding, deriveKey, generateEncryptionKey, wrapEncryptedContent, unwrapEncryptedContent, isValidEncryptedPayload, } from './encryption.js';
16
+ export type { EncryptedPayload, EncryptionConfig, KeyDerivationResult, } from './encryption.js';
8
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGhF,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC"}
@@ -3,4 +3,10 @@
3
3
  */
4
4
  export { A2AClient } from './client.js';
5
5
  export { MemorySynchronizer } from './synchronizer.js';
6
+ export { SyncQueue, mapCategory, mapTier } from './queue.js';
7
+ export { LocalVectorClock } from './vector-clock.js';
8
+ export { TeamSynchronizer } from './team-synchronizer.js';
9
+ export { SyncScheduler } from './scheduler.js';
10
+ // Encryption
11
+ export { encryptContent, decryptContent, encryptEmbedding, decryptEmbedding, deriveKey, generateEncryptionKey, wrapEncryptedContent, unwrapEncryptedContent, isValidEncryptedPayload, } from './encryption.js';
6
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG/C,aAAa;AACb,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Write-Behind Sync Queue
3
+ *
4
+ * 로컬 DB에 저장된 메모리를 원격 서버로 일괄 push합니다.
5
+ * 로컬 DB = 캐시, 원격 서버 = Single Source of Truth.
6
+ */
7
+ import type { MemoryDatabase } from '../db/database.js';
8
+ import type { A2AClient } from './client.js';
9
+ import type { MemoryCategory, MemoryTier } from '../types/index.js';
10
+ export interface QueueFlushResult {
11
+ pushed: number;
12
+ failed: number;
13
+ errors: string[];
14
+ duration: number;
15
+ }
16
+ export interface FlushOptions {
17
+ timeoutMs?: number;
18
+ maxRetries?: number;
19
+ }
20
+ /**
21
+ * 로컬 category를 서버 호환 category로 변환
22
+ */
23
+ export declare function mapCategory(category: string): MemoryCategory;
24
+ /**
25
+ * 로컬 tier를 서버 호환 tier로 변환
26
+ */
27
+ export declare function mapTier(tier: string): MemoryTier;
28
+ export declare class SyncQueue {
29
+ private db;
30
+ private client;
31
+ constructor(db: MemoryDatabase, client: A2AClient);
32
+ /**
33
+ * 메모리를 sync 대기 상태로 설정 (~1ms)
34
+ */
35
+ enqueue(memoryId: string): void;
36
+ /**
37
+ * pending 메모리를 일괄 push
38
+ */
39
+ flush(options?: FlushOptions): Promise<QueueFlushResult>;
40
+ /**
41
+ * 대기 중인 메모리 수
42
+ */
43
+ getPendingCount(): number;
44
+ /**
45
+ * 재시도 포함 push (exponential backoff)
46
+ */
47
+ private retryPush;
48
+ }
49
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/sync/queue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAgB,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAU,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE5E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAcD;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAE5D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAEhD;AAED,qBAAa,SAAS;IAElB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,MAAM;gBADN,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,SAAS;IAG3B;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0C9D;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;YACW,SAAS;CAuBxB"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Write-Behind Sync Queue
3
+ *
4
+ * 로컬 DB에 저장된 메모리를 원격 서버로 일괄 push합니다.
5
+ * 로컬 DB = 캐시, 원격 서버 = Single Source of Truth.
6
+ */
7
+ // ============================================================
8
+ // Category/Tier 매핑 (로컬 레거시 → 서버 호환)
9
+ // ============================================================
10
+ const CATEGORY_MAP = {
11
+ preference: 'convention',
12
+ };
13
+ const TIER_MAP = {
14
+ procedural: 'semantic',
15
+ };
16
+ /**
17
+ * 로컬 category를 서버 호환 category로 변환
18
+ */
19
+ export function mapCategory(category) {
20
+ return (CATEGORY_MAP[category] ?? category);
21
+ }
22
+ /**
23
+ * 로컬 tier를 서버 호환 tier로 변환
24
+ */
25
+ export function mapTier(tier) {
26
+ return (TIER_MAP[tier] ?? tier);
27
+ }
28
+ export class SyncQueue {
29
+ db;
30
+ client;
31
+ constructor(db, client) {
32
+ this.db = db;
33
+ this.client = client;
34
+ }
35
+ /**
36
+ * 메모리를 sync 대기 상태로 설정 (~1ms)
37
+ */
38
+ enqueue(memoryId) {
39
+ this.db.setSyncStatus(memoryId, null, 'pending');
40
+ }
41
+ /**
42
+ * pending 메모리를 일괄 push
43
+ */
44
+ async flush(options) {
45
+ const startTime = Date.now();
46
+ const timeoutMs = options?.timeoutMs ?? 5000;
47
+ const maxRetries = options?.maxRetries ?? 3;
48
+ const result = {
49
+ pushed: 0,
50
+ failed: 0,
51
+ errors: [],
52
+ duration: 0,
53
+ };
54
+ const pending = this.db.getPendingSyncMemories();
55
+ if (pending.length === 0) {
56
+ result.duration = Date.now() - startTime;
57
+ return result;
58
+ }
59
+ for (const memory of pending) {
60
+ // 타임아웃 체크
61
+ if (Date.now() - startTime > timeoutMs) {
62
+ result.errors.push(`타임아웃: ${result.pushed}/${pending.length} 처리 완료`);
63
+ result.failed += pending.length - result.pushed - result.failed;
64
+ break;
65
+ }
66
+ try {
67
+ const remoteMemory = await this.retryPush(memory, maxRetries);
68
+ this.db.setSyncStatus(memory.id, remoteMemory.id, 'synced');
69
+ result.pushed++;
70
+ }
71
+ catch (error) {
72
+ result.failed++;
73
+ if (error instanceof Error) {
74
+ result.errors.push(`메모리 ${memory.id} push 실패: ${error.message}`);
75
+ }
76
+ }
77
+ }
78
+ result.duration = Date.now() - startTime;
79
+ return result;
80
+ }
81
+ /**
82
+ * 대기 중인 메모리 수
83
+ */
84
+ getPendingCount() {
85
+ return this.db.getPendingSyncMemories().length;
86
+ }
87
+ /**
88
+ * 재시도 포함 push (exponential backoff)
89
+ */
90
+ async retryPush(memory, maxRetries = 3) {
91
+ let lastError = null;
92
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
93
+ try {
94
+ return await this.client.createMemory({
95
+ content: memory.content,
96
+ category: mapCategory(memory.category),
97
+ tier: mapTier(memory.tier),
98
+ tags: memory.tags,
99
+ });
100
+ }
101
+ catch (error) {
102
+ lastError = error instanceof Error ? error : new Error(String(error));
103
+ if (attempt < maxRetries - 1) {
104
+ const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
105
+ await new Promise((resolve) => setTimeout(resolve, delay));
106
+ }
107
+ }
108
+ }
109
+ throw lastError;
110
+ }
111
+ }
112
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/sync/queue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkBH,+DAA+D;AAC/D,oCAAoC;AACpC,+DAA+D;AAE/D,MAAM,YAAY,GAAmC;IACnD,UAAU,EAAE,YAAY;CACzB,CAAC;AAEF,MAAM,QAAQ,GAA+B;IAC3C,UAAU,EAAE,UAAU;CACvB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAmB,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAe,CAAC;AAChD,CAAC;AAED,MAAM,OAAO,SAAS;IAEV;IACA;IAFV,YACU,EAAkB,EAClB,MAAiB;QADjB,OAAE,GAAF,EAAE,CAAgB;QAClB,WAAM,GAAN,MAAM,CAAW;IACxB,CAAC;IAEJ;;OAEG;IACH,OAAO,CAAC,QAAgB;QACtB,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,OAAsB;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC;QAC7C,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAqB;YAC/B,MAAM,EAAE,CAAC;YACT,MAAM,EAAE,CAAC;YACT,MAAM,EAAE,EAAE;YACV,QAAQ,EAAE,CAAC;SACZ,CAAC;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,sBAAsB,EAAE,CAAC;QACjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACzC,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,UAAU;YACV,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;gBACvC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,QAAQ,CAAC,CAAC;gBACrE,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;gBAChE,MAAM;YACR,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;gBAE9D,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;gBAC5D,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;oBAC3B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE,aAAa,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACzC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,IAAI,CAAC,EAAE,CAAC,sBAAsB,EAAE,CAAC,MAAM,CAAC;IACjD,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,SAAS,CACrB,MAAc,EACd,aAAqB,CAAC;QAEtB,IAAI,SAAS,GAAiB,IAAI,CAAC;QACnC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YACtD,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;oBACpC,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC;oBACtC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;oBAC1B,IAAI,EAAE,MAAM,CAAC,IAAI;iBAClB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBACtE,IAAI,OAAO,GAAG,UAAU,GAAG,CAAC,EAAE,CAAC;oBAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;oBAC3D,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;gBAC7D,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,SAAU,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Sync Scheduler
3
+ *
4
+ * 주기적으로 MemorySynchronizer.sync()를 실행하는 스케줄러.
5
+ * CLI `sync --watch` 또는 라이브러리로 사용 가능.
6
+ */
7
+ import type { MemorySynchronizer, SyncResult } from './synchronizer.js';
8
+ import type { HookLogger } from '../utils/logger.js';
9
+ export interface SyncSchedulerOptions {
10
+ /** 동기화 주기 (ms). 기본: 1800000 (30분). 0이면 비활성 */
11
+ intervalMs: number;
12
+ /** 시작 시 즉시 sync 실행 여부. 기본: true */
13
+ syncOnStart?: boolean;
14
+ /** sync 완료 콜백 */
15
+ onSync?: (result: SyncResult) => void;
16
+ /** 에러 콜백 */
17
+ onError?: (error: Error) => void;
18
+ }
19
+ export interface SyncSchedulerStatus {
20
+ running: boolean;
21
+ syncing: boolean;
22
+ syncCount: number;
23
+ lastSyncAt: string | null;
24
+ lastResult: SyncResult | null;
25
+ intervalMs: number;
26
+ nextSyncAt: string | null;
27
+ }
28
+ export declare class SyncScheduler {
29
+ private synchronizer;
30
+ private options;
31
+ private logger?;
32
+ private timer;
33
+ private running;
34
+ private syncing;
35
+ private syncCount;
36
+ private lastSyncAt;
37
+ private lastResult;
38
+ private startedAt;
39
+ constructor(synchronizer: MemorySynchronizer, options: SyncSchedulerOptions, logger?: HookLogger | undefined);
40
+ /**
41
+ * 스케줄러 시작
42
+ */
43
+ start(): Promise<void>;
44
+ /**
45
+ * 스케줄러 중지
46
+ */
47
+ stop(): void;
48
+ /**
49
+ * 실행 중 여부
50
+ */
51
+ isRunning(): boolean;
52
+ /**
53
+ * 현재 sync 중 여부
54
+ */
55
+ isSyncing(): boolean;
56
+ /**
57
+ * 스케줄러 상태
58
+ */
59
+ getStatus(): SyncSchedulerStatus;
60
+ /**
61
+ * 수동으로 즉시 sync 실행 (스케줄러 타이머와 무관)
62
+ */
63
+ syncNow(): Promise<SyncResult | null>;
64
+ /**
65
+ * 한 번의 sync 사이클
66
+ */
67
+ private tick;
68
+ }
69
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/sync/scheduler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB;IACjB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,YAAY;IACZ,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,qBAAa,aAAa;IAUtB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM,CAAC;IAXjB,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,SAAS,CAAqB;gBAG5B,YAAY,EAAE,kBAAkB,EAChC,OAAO,EAAE,oBAAoB,EAC7B,MAAM,CAAC,EAAE,UAAU,YAAA;IAG7B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAYZ;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,SAAS,IAAI,mBAAmB;IAqBhC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAI3C;;OAEG;YACW,IAAI;CAgCnB"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Sync Scheduler
3
+ *
4
+ * 주기적으로 MemorySynchronizer.sync()를 실행하는 스케줄러.
5
+ * CLI `sync --watch` 또는 라이브러리로 사용 가능.
6
+ */
7
+ export class SyncScheduler {
8
+ synchronizer;
9
+ options;
10
+ logger;
11
+ timer = null;
12
+ running = false;
13
+ syncing = false;
14
+ syncCount = 0;
15
+ lastSyncAt = null;
16
+ lastResult = null;
17
+ startedAt = null;
18
+ constructor(synchronizer, options, logger) {
19
+ this.synchronizer = synchronizer;
20
+ this.options = options;
21
+ this.logger = logger;
22
+ }
23
+ /**
24
+ * 스케줄러 시작
25
+ */
26
+ async start() {
27
+ if (this.running) {
28
+ this.logger?.warn('Scheduler already running');
29
+ return;
30
+ }
31
+ if (this.options.intervalMs <= 0) {
32
+ this.logger?.warn('Scheduler disabled (intervalMs <= 0)');
33
+ return;
34
+ }
35
+ this.running = true;
36
+ this.startedAt = new Date();
37
+ this.logger?.info('Scheduler started', { intervalMs: this.options.intervalMs });
38
+ // 시작 시 즉시 sync (기본 동작)
39
+ if (this.options.syncOnStart !== false) {
40
+ await this.tick();
41
+ }
42
+ this.timer = setInterval(() => {
43
+ this.tick().catch((err) => {
44
+ this.logger?.error('Scheduler tick error', err instanceof Error ? err : new Error(String(err)));
45
+ });
46
+ }, this.options.intervalMs);
47
+ // unref: Node.js 프로세스가 타이머 때문에 종료되지 않는 것을 방지
48
+ if (this.timer && typeof this.timer === 'object' && 'unref' in this.timer) {
49
+ this.timer.unref();
50
+ }
51
+ }
52
+ /**
53
+ * 스케줄러 중지
54
+ */
55
+ stop() {
56
+ if (!this.running)
57
+ return;
58
+ if (this.timer) {
59
+ clearInterval(this.timer);
60
+ this.timer = null;
61
+ }
62
+ this.running = false;
63
+ this.logger?.info('Scheduler stopped', { syncCount: this.syncCount });
64
+ }
65
+ /**
66
+ * 실행 중 여부
67
+ */
68
+ isRunning() {
69
+ return this.running;
70
+ }
71
+ /**
72
+ * 현재 sync 중 여부
73
+ */
74
+ isSyncing() {
75
+ return this.syncing;
76
+ }
77
+ /**
78
+ * 스케줄러 상태
79
+ */
80
+ getStatus() {
81
+ let nextSyncAt = null;
82
+ if (this.running && this.lastSyncAt) {
83
+ const next = new Date(this.lastSyncAt.getTime() + this.options.intervalMs);
84
+ nextSyncAt = next.toISOString();
85
+ }
86
+ else if (this.running && this.startedAt) {
87
+ const next = new Date(this.startedAt.getTime() + this.options.intervalMs);
88
+ nextSyncAt = next.toISOString();
89
+ }
90
+ return {
91
+ running: this.running,
92
+ syncing: this.syncing,
93
+ syncCount: this.syncCount,
94
+ lastSyncAt: this.lastSyncAt?.toISOString() ?? null,
95
+ lastResult: this.lastResult,
96
+ intervalMs: this.options.intervalMs,
97
+ nextSyncAt,
98
+ };
99
+ }
100
+ /**
101
+ * 수동으로 즉시 sync 실행 (스케줄러 타이머와 무관)
102
+ */
103
+ async syncNow() {
104
+ return this.tick();
105
+ }
106
+ /**
107
+ * 한 번의 sync 사이클
108
+ */
109
+ async tick() {
110
+ if (this.syncing) {
111
+ this.logger?.debug('Sync already in progress, skipping');
112
+ return null;
113
+ }
114
+ this.syncing = true;
115
+ try {
116
+ const result = await this.synchronizer.sync();
117
+ this.syncCount++;
118
+ this.lastSyncAt = new Date();
119
+ this.lastResult = result;
120
+ this.logger?.info('Sync completed', {
121
+ pushed: result.pushed,
122
+ pulled: result.pulled,
123
+ errors: result.errors.length,
124
+ duration: result.duration,
125
+ });
126
+ this.options.onSync?.(result);
127
+ return result;
128
+ }
129
+ catch (error) {
130
+ const err = error instanceof Error ? error : new Error(String(error));
131
+ this.logger?.error('Sync failed', err);
132
+ this.options.onError?.(err);
133
+ return null;
134
+ }
135
+ finally {
136
+ this.syncing = false;
137
+ }
138
+ }
139
+ }
140
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/sync/scheduler.ts"],"names":[],"mappings":"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"}