@prmichaelsen/remember-mcp 3.0.0 → 3.13.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 (208) hide show
  1. package/AGENT.md +296 -250
  2. package/CHANGELOG.md +358 -0
  3. package/README.md +68 -45
  4. package/agent/commands/acp.clarification-create.md +382 -0
  5. package/agent/commands/acp.project-info.md +309 -0
  6. package/agent/commands/acp.project-remove.md +379 -0
  7. package/agent/commands/acp.project-update.md +296 -0
  8. package/agent/commands/acp.task-create.md +17 -9
  9. package/agent/commands/git.commit.md +13 -1
  10. package/agent/design/comment-memory-type.md +2 -2
  11. package/agent/design/local.collaborative-memory-sync.md +265 -0
  12. package/agent/design/local.content-flags.md +210 -0
  13. package/agent/design/local.ghost-persona-system.md +273 -0
  14. package/agent/design/local.group-acl-integration.md +338 -0
  15. package/agent/design/local.memory-acl-schema.md +352 -0
  16. package/agent/design/local.memory-collection-pattern-v2.md +348 -0
  17. package/agent/design/local.moderation-and-space-config.md +257 -0
  18. package/agent/design/local.v2-api-reference.md +621 -0
  19. package/agent/design/local.v2-migration-guide.md +191 -0
  20. package/agent/design/local.v2-usage-examples.md +265 -0
  21. package/agent/design/permissions-storage-architecture.md +11 -3
  22. package/agent/design/trust-escalation-prevention.md +9 -2
  23. package/agent/design/trust-system-implementation.md +12 -3
  24. package/agent/milestones/milestone-14-memory-collection-v2.md +182 -0
  25. package/agent/milestones/milestone-15-moderation-space-config.md +126 -0
  26. package/agent/progress.yaml +628 -49
  27. package/agent/scripts/acp.common.sh +2 -0
  28. package/agent/scripts/acp.install.sh +11 -1
  29. package/agent/scripts/acp.package-install-optimized.sh +454 -0
  30. package/agent/scripts/acp.package-install.sh +247 -300
  31. package/agent/scripts/acp.project-info.sh +218 -0
  32. package/agent/scripts/acp.project-remove.sh +302 -0
  33. package/agent/scripts/acp.project-update.sh +296 -0
  34. package/agent/scripts/acp.yaml-parser.sh +128 -10
  35. package/agent/tasks/milestone-14-memory-collection-v2/task-165-core-infrastructure-setup.md +171 -0
  36. package/agent/tasks/milestone-14-memory-collection-v2/task-166-update-remember-publish.md +191 -0
  37. package/agent/tasks/milestone-14-memory-collection-v2/task-167-update-remember-retract.md +186 -0
  38. package/agent/tasks/milestone-14-memory-collection-v2/task-168-implement-remember-revise.md +184 -0
  39. package/agent/tasks/milestone-14-memory-collection-v2/task-169-update-remember-search-space.md +179 -0
  40. package/agent/tasks/milestone-14-memory-collection-v2/task-170-update-remember-create-update.md +139 -0
  41. package/agent/tasks/milestone-14-memory-collection-v2/task-172-performance-testing-optimization.md +161 -0
  42. package/agent/tasks/milestone-14-memory-collection-v2/task-173-documentation-examples.md +258 -0
  43. package/agent/tasks/milestone-15-moderation-space-config/task-174-add-moderation-schema-fields.md +57 -0
  44. package/agent/tasks/milestone-15-moderation-space-config/task-175-create-space-config-service.md +64 -0
  45. package/agent/tasks/milestone-15-moderation-space-config/task-176-wire-moderation-publish-flow.md +45 -0
  46. package/agent/tasks/milestone-15-moderation-space-config/task-177-add-moderation-search-filters.md +70 -0
  47. package/agent/tasks/milestone-15-moderation-space-config/task-178-create-remember-moderate-tool.md +69 -0
  48. package/agent/tasks/milestone-15-moderation-space-config/task-179-documentation-integration-tests.md +58 -0
  49. package/agent/tasks/milestone-16-ghost-system/task-187-ghost-config-firestore.md +41 -0
  50. package/agent/tasks/milestone-16-ghost-system/task-188-trust-filter-integration.md +44 -0
  51. package/agent/tasks/milestone-16-ghost-system/task-189-ghost-memory-filtering.md +43 -0
  52. package/agent/tasks/milestone-16-ghost-system/task-190-ghost-config-tools.md +45 -0
  53. package/agent/tasks/milestone-16-ghost-system/task-191-escalation-firestore.md +38 -0
  54. package/agent/tasks/milestone-16-ghost-system/task-192-documentation-verification.md +39 -0
  55. package/agent/tasks/milestone-7-trust-permissions/task-180-access-result-permission-types.md +69 -0
  56. package/agent/tasks/milestone-7-trust-permissions/task-181-firestore-permissions-access-logs.md +56 -0
  57. package/agent/tasks/milestone-7-trust-permissions/task-182-trust-enforcement-service.md +68 -0
  58. package/agent/tasks/milestone-7-trust-permissions/task-183-access-control-service.md +70 -0
  59. package/agent/tasks/milestone-7-trust-permissions/task-184-permission-tools.md +79 -0
  60. package/agent/tasks/milestone-7-trust-permissions/task-185-wire-trust-into-search-query.md +55 -0
  61. package/agent/tasks/milestone-7-trust-permissions/task-186-documentation-verification.md +56 -0
  62. package/agent/tasks/task-76-fix-indexnullstate-schema-bug.md +197 -0
  63. package/dist/collections/composite-ids.d.ts +106 -0
  64. package/dist/collections/core-infrastructure.spec.d.ts +11 -0
  65. package/dist/collections/dot-notation.d.ts +106 -0
  66. package/dist/collections/tracking-arrays.d.ts +176 -0
  67. package/dist/constants/content-types.d.ts +1 -0
  68. package/dist/schema/v2-collections-comments.spec.d.ts +8 -0
  69. package/dist/schema/v2-collections.d.ts +210 -0
  70. package/dist/server-factory.d.ts +15 -0
  71. package/dist/server-factory.js +2798 -1029
  72. package/dist/server.js +2526 -1012
  73. package/dist/services/access-control.d.ts +103 -0
  74. package/dist/services/access-control.spec.d.ts +2 -0
  75. package/dist/services/credentials-provider.d.ts +24 -0
  76. package/dist/services/credentials-provider.spec.d.ts +2 -0
  77. package/dist/services/escalation.service.d.ts +22 -0
  78. package/dist/services/escalation.service.spec.d.ts +2 -0
  79. package/dist/services/ghost-config.service.d.ts +55 -0
  80. package/dist/services/ghost-config.service.spec.d.ts +2 -0
  81. package/dist/services/space-config.service.d.ts +23 -0
  82. package/dist/services/space-config.service.spec.d.ts +2 -0
  83. package/dist/services/trust-enforcement.d.ts +83 -0
  84. package/dist/services/trust-enforcement.spec.d.ts +2 -0
  85. package/dist/services/trust-validator.d.ts +43 -0
  86. package/dist/services/trust-validator.spec.d.ts +2 -0
  87. package/dist/tools/confirm-publish-moderation.spec.d.ts +8 -0
  88. package/dist/tools/confirm.d.ts +8 -1
  89. package/dist/tools/create-memory.d.ts +2 -1
  90. package/dist/tools/create-memory.spec.d.ts +10 -0
  91. package/dist/tools/create-relationship.d.ts +2 -1
  92. package/dist/tools/delete-memory.d.ts +2 -1
  93. package/dist/tools/delete-relationship.d.ts +2 -1
  94. package/dist/tools/deny.d.ts +2 -1
  95. package/dist/tools/find-similar.d.ts +2 -1
  96. package/dist/tools/get-preferences.d.ts +2 -1
  97. package/dist/tools/ghost-config.d.ts +27 -0
  98. package/dist/tools/ghost-config.spec.d.ts +2 -0
  99. package/dist/tools/moderate.d.ts +20 -0
  100. package/dist/tools/moderate.spec.d.ts +5 -0
  101. package/dist/tools/publish.d.ts +11 -3
  102. package/dist/tools/query-memory.d.ts +3 -1
  103. package/dist/tools/query-space.d.ts +4 -1
  104. package/dist/tools/retract.d.ts +29 -0
  105. package/dist/tools/revise.d.ts +45 -0
  106. package/dist/tools/revise.spec.d.ts +8 -0
  107. package/dist/tools/search-memory.d.ts +2 -1
  108. package/dist/tools/search-relationship.d.ts +2 -1
  109. package/dist/tools/search-space.d.ts +25 -5
  110. package/dist/tools/search-space.spec.d.ts +9 -0
  111. package/dist/tools/set-preference.d.ts +2 -1
  112. package/dist/tools/update-memory.d.ts +2 -1
  113. package/dist/tools/update-relationship.d.ts +2 -1
  114. package/dist/types/access-result.d.ts +48 -0
  115. package/dist/types/access-result.spec.d.ts +2 -0
  116. package/dist/types/auth.d.ts +46 -0
  117. package/dist/types/ghost-config.d.ts +36 -0
  118. package/dist/types/memory.d.ts +3 -1
  119. package/dist/types/preferences.d.ts +1 -1
  120. package/dist/utils/auth-helpers.d.ts +14 -0
  121. package/dist/utils/auth-helpers.spec.d.ts +2 -0
  122. package/dist/utils/test-data-generator.d.ts +124 -0
  123. package/dist/utils/test-data-generator.spec.d.ts +12 -0
  124. package/dist/v2-performance.e2e.d.ts +17 -0
  125. package/dist/v2-smoke.e2e.d.ts +14 -0
  126. package/dist/weaviate/client.d.ts +5 -8
  127. package/dist/weaviate/space-schema.d.ts +2 -2
  128. package/docs/performance/v2-benchmarks.md +80 -0
  129. package/jest.e2e.config.js +14 -3
  130. package/package.json +1 -1
  131. package/scripts/.collection-recreation-state.yaml +16 -0
  132. package/scripts/.gitkeep +5 -0
  133. package/scripts/README-collection-recreation.md +224 -0
  134. package/scripts/README.md +51 -0
  135. package/scripts/backup-collections.ts +543 -0
  136. package/scripts/delete-collection.ts +137 -0
  137. package/scripts/migrate-recreate-collections.ts +578 -0
  138. package/scripts/migrate-v1-to-v2.ts +1094 -0
  139. package/scripts/package-lock.json +1113 -0
  140. package/scripts/package.json +27 -0
  141. package/src/collections/composite-ids.ts +193 -0
  142. package/src/collections/core-infrastructure.spec.ts +353 -0
  143. package/src/collections/dot-notation.ts +212 -0
  144. package/src/collections/tracking-arrays.ts +298 -0
  145. package/src/constants/content-types.ts +20 -0
  146. package/src/schema/v2-collections-comments.spec.ts +141 -0
  147. package/src/schema/v2-collections.ts +433 -0
  148. package/src/server-factory.ts +89 -20
  149. package/src/server.ts +45 -17
  150. package/src/services/access-control.spec.ts +383 -0
  151. package/src/services/access-control.ts +291 -0
  152. package/src/services/credentials-provider.spec.ts +22 -0
  153. package/src/services/credentials-provider.ts +34 -0
  154. package/src/services/escalation.service.spec.ts +183 -0
  155. package/src/services/escalation.service.ts +150 -0
  156. package/src/services/ghost-config.service.spec.ts +339 -0
  157. package/src/services/ghost-config.service.ts +219 -0
  158. package/src/services/space-config.service.spec.ts +102 -0
  159. package/src/services/space-config.service.ts +79 -0
  160. package/src/services/trust-enforcement.spec.ts +309 -0
  161. package/src/services/trust-enforcement.ts +197 -0
  162. package/src/services/trust-validator.spec.ts +108 -0
  163. package/src/services/trust-validator.ts +105 -0
  164. package/src/tools/confirm-publish-moderation.spec.ts +240 -0
  165. package/src/tools/confirm.ts +869 -135
  166. package/src/tools/create-memory.spec.ts +126 -0
  167. package/src/tools/create-memory.ts +20 -27
  168. package/src/tools/create-relationship.ts +17 -8
  169. package/src/tools/delete-memory.ts +13 -6
  170. package/src/tools/delete-relationship.ts +15 -6
  171. package/src/tools/deny.ts +8 -1
  172. package/src/tools/find-similar.ts +21 -8
  173. package/src/tools/get-preferences.ts +10 -1
  174. package/src/tools/ghost-config.spec.ts +180 -0
  175. package/src/tools/ghost-config.ts +230 -0
  176. package/src/tools/moderate.spec.ts +277 -0
  177. package/src/tools/moderate.ts +219 -0
  178. package/src/tools/publish.ts +99 -41
  179. package/src/tools/query-memory.ts +28 -6
  180. package/src/tools/query-space.ts +39 -4
  181. package/src/tools/retract.ts +292 -0
  182. package/src/tools/revise.spec.ts +146 -0
  183. package/src/tools/revise.ts +283 -0
  184. package/src/tools/search-memory.ts +30 -7
  185. package/src/tools/search-relationship.ts +11 -2
  186. package/src/tools/search-space.spec.ts +341 -0
  187. package/src/tools/search-space.ts +323 -99
  188. package/src/tools/set-preference.ts +10 -1
  189. package/src/tools/update-memory.ts +16 -5
  190. package/src/tools/update-relationship.ts +10 -1
  191. package/src/types/access-result.spec.ts +193 -0
  192. package/src/types/access-result.ts +62 -0
  193. package/src/types/auth.ts +52 -0
  194. package/src/types/ghost-config.ts +46 -0
  195. package/src/types/memory.ts +9 -1
  196. package/src/types/preferences.ts +2 -2
  197. package/src/utils/auth-helpers.spec.ts +75 -0
  198. package/src/utils/auth-helpers.ts +25 -0
  199. package/src/utils/test-data-generator.spec.ts +317 -0
  200. package/src/utils/test-data-generator.ts +292 -0
  201. package/src/utils/weaviate-filters.ts +4 -4
  202. package/src/v2-performance.e2e.ts +173 -0
  203. package/src/v2-smoke.e2e.ts +401 -0
  204. package/src/weaviate/client.spec.ts +5 -5
  205. package/src/weaviate/client.ts +51 -36
  206. package/src/weaviate/schema.ts +11 -256
  207. package/src/weaviate/space-schema.spec.ts +24 -24
  208. package/src/weaviate/space-schema.ts +18 -6
@@ -0,0 +1,543 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Backup Collections to Weaviate
4
+ *
5
+ * Creates backup copies of collections directly in Weaviate with "Backup_" prefix.
6
+ * This provides higher confidence that backups are safely stored before any destructive operations.
7
+ *
8
+ * Usage:
9
+ * # Backup all Memory_ collections
10
+ * npx tsx scripts/backup-collections.ts
11
+ *
12
+ * # Backup specific collections
13
+ * npx tsx scripts/backup-collections.ts --collections "Memory_user123,Memory_public"
14
+ *
15
+ * # Dry run
16
+ * npx tsx scripts/backup-collections.ts --dry-run
17
+ */
18
+
19
+ import weaviate, { WeaviateClient } from 'weaviate-client';
20
+ import * as fs from 'fs';
21
+ import * as yaml from 'yaml';
22
+ import * as dotenv from 'dotenv';
23
+
24
+ // Load environment
25
+ dotenv.config();
26
+
27
+ // ============================================================================
28
+ // Types
29
+ // ============================================================================
30
+
31
+ interface BackupConfig {
32
+ weaviate: {
33
+ url: string;
34
+ apiKey?: string;
35
+ openaiApiKey?: string;
36
+ };
37
+ options: {
38
+ batchSize: number;
39
+ dryRun: boolean;
40
+ collections?: string[];
41
+ stateFile: string;
42
+ };
43
+ }
44
+
45
+ interface BackupState {
46
+ backup: {
47
+ id: string;
48
+ started_at: string;
49
+ updated_at: string;
50
+ status: 'not_started' | 'in_progress' | 'completed' | 'failed';
51
+ };
52
+ collections: {
53
+ name: string;
54
+ backup_name: string;
55
+ status: 'not_started' | 'schema_created' | 'copying' | 'completed' | 'failed';
56
+ total_documents: number;
57
+ copied_documents: number;
58
+ }[];
59
+ progress: {
60
+ total_collections: number;
61
+ completed_collections: number;
62
+ percentage: number;
63
+ };
64
+ errors: Array<{
65
+ collection: string;
66
+ error: string;
67
+ timestamp: string;
68
+ }>;
69
+ }
70
+
71
+ // ============================================================================
72
+ // State Manager
73
+ // ============================================================================
74
+
75
+ class BackupStateManager {
76
+ private stateFile: string;
77
+ private state!: BackupState;
78
+
79
+ constructor(stateFile: string) {
80
+ this.stateFile = stateFile;
81
+ }
82
+
83
+ async initialize(collections: string[]): Promise<void> {
84
+ if (fs.existsSync(this.stateFile)) {
85
+ await this.load();
86
+ console.log(`✓ Resuming backup from ${this.stateFile}\n`);
87
+ } else {
88
+ this.state = this.createInitialState(collections);
89
+ await this.save();
90
+ console.log(`✓ Created backup state file: ${this.stateFile}\n`);
91
+ }
92
+ }
93
+
94
+ private createInitialState(collections: string[]): BackupState {
95
+ const now = new Date().toISOString();
96
+ return {
97
+ backup: {
98
+ id: `backup-${now.replace(/[:.]/g, '-').slice(0, 19)}`,
99
+ started_at: now,
100
+ updated_at: now,
101
+ status: 'not_started',
102
+ },
103
+ collections: collections.map(name => ({
104
+ name,
105
+ backup_name: `Backup_${name}`,
106
+ status: 'not_started',
107
+ total_documents: 0,
108
+ copied_documents: 0,
109
+ })),
110
+ progress: {
111
+ total_collections: collections.length,
112
+ completed_collections: 0,
113
+ percentage: 0,
114
+ },
115
+ errors: [],
116
+ };
117
+ }
118
+
119
+ async load(): Promise<void> {
120
+ const content = fs.readFileSync(this.stateFile, 'utf8');
121
+ this.state = yaml.parse(content);
122
+ }
123
+
124
+ async save(): Promise<void> {
125
+ this.state.backup.updated_at = new Date().toISOString();
126
+ const content = yaml.stringify(this.state);
127
+ fs.writeFileSync(this.stateFile, content, 'utf8');
128
+ }
129
+
130
+ updateCollectionStatus(
131
+ collectionName: string,
132
+ status: BackupState['collections'][0]['status'],
133
+ updates?: Partial<BackupState['collections'][0]>
134
+ ): void {
135
+ const collection = this.state.collections.find(c => c.name === collectionName);
136
+ if (collection) {
137
+ collection.status = status;
138
+ if (updates) {
139
+ Object.assign(collection, updates);
140
+ }
141
+ }
142
+ }
143
+
144
+ async completeCollection(collectionName: string): Promise<void> {
145
+ this.updateCollectionStatus(collectionName, 'completed');
146
+ this.state.progress.completed_collections++;
147
+ this.state.progress.percentage =
148
+ (this.state.progress.completed_collections / this.state.progress.total_collections) * 100;
149
+ await this.save();
150
+ }
151
+
152
+ async addError(collection: string, error: string): Promise<void> {
153
+ this.state.errors.push({
154
+ collection,
155
+ error,
156
+ timestamp: new Date().toISOString(),
157
+ });
158
+ await this.save();
159
+ }
160
+
161
+ async complete(): Promise<void> {
162
+ this.state.backup.status = 'completed';
163
+ await this.save();
164
+ }
165
+
166
+ async fail(error: string): Promise<void> {
167
+ this.state.backup.status = 'failed';
168
+ await this.addError('global', error);
169
+ }
170
+
171
+ getState(): BackupState {
172
+ return this.state;
173
+ }
174
+
175
+ async cleanup(): Promise<void> {
176
+ if (fs.existsSync(this.stateFile)) {
177
+ fs.unlinkSync(this.stateFile);
178
+ console.log(`✓ Cleaned up state file: ${this.stateFile}`);
179
+ }
180
+ }
181
+ }
182
+
183
+ // ============================================================================
184
+ // Backup Class
185
+ // ============================================================================
186
+
187
+ class WeaviateBackup {
188
+ private client!: WeaviateClient;
189
+ private config: BackupConfig;
190
+ private stateManager: BackupStateManager;
191
+
192
+ constructor(config: BackupConfig) {
193
+ this.config = config;
194
+ this.stateManager = new BackupStateManager(config.options.stateFile);
195
+ }
196
+
197
+ async connect(): Promise<void> {
198
+ console.log('🔌 Connecting to Weaviate...');
199
+
200
+ const clientConfig: any = {
201
+ authCredentials: this.config.weaviate.apiKey
202
+ ? new weaviate.ApiKey(this.config.weaviate.apiKey)
203
+ : undefined,
204
+ };
205
+
206
+ if (this.config.weaviate.openaiApiKey) {
207
+ clientConfig.headers = {
208
+ 'X-Openai-Api-Key': this.config.weaviate.openaiApiKey,
209
+ };
210
+ }
211
+
212
+ this.client = await weaviate.connectToWeaviateCloud(
213
+ this.config.weaviate.url,
214
+ clientConfig
215
+ );
216
+
217
+ console.log('✓ Connected\n');
218
+ }
219
+
220
+ async disconnect(): Promise<void> {
221
+ await this.client?.close();
222
+ }
223
+
224
+ async discoverCollections(): Promise<string[]> {
225
+ console.log('🔍 Discovering collections...');
226
+
227
+ const allCollections = await this.client.collections.listAll();
228
+ let collectionNames = allCollections
229
+ .map(c => c.name)
230
+ .filter(name => name.startsWith('Memory_') && !name.startsWith('Backup_'));
231
+
232
+ // Filter by specified collections if provided
233
+ if (this.config.options.collections && this.config.options.collections.length > 0) {
234
+ collectionNames = collectionNames.filter(name =>
235
+ this.config.options.collections!.includes(name)
236
+ );
237
+ }
238
+
239
+ console.log(` Found ${collectionNames.length} collections: ${collectionNames.join(', ')}\n`);
240
+ return collectionNames;
241
+ }
242
+
243
+ async backupCollection(collectionName: string): Promise<boolean> {
244
+ console.log(`\n📦 Backing up: ${collectionName}`);
245
+ console.log('━'.repeat(60));
246
+
247
+ const backupName = `Backup_${collectionName}`;
248
+
249
+ try {
250
+ // Check if backup already exists
251
+ const backupExists = await this.client.collections.exists(backupName);
252
+ if (backupExists) {
253
+ console.log(` ⚠️ Backup collection ${backupName} already exists`);
254
+ console.log(` ℹ️ Delete it first if you want to recreate the backup`);
255
+ await this.stateManager.completeCollection(collectionName);
256
+ return true;
257
+ }
258
+
259
+ // Get source collection
260
+ const sourceCollection = this.client.collections.get(collectionName);
261
+
262
+ console.log(` ✓ Analyzing collection`);
263
+
264
+ // Get total count
265
+ const aggregate = await sourceCollection.aggregate.overAll();
266
+ const totalCount = aggregate.totalCount || 0;
267
+ console.log(` ✓ Found ${totalCount} documents`);
268
+
269
+ this.stateManager.updateCollectionStatus(collectionName, 'not_started', {
270
+ total_documents: totalCount,
271
+ });
272
+ await this.stateManager.save();
273
+
274
+ // Create backup collection with same schema
275
+ if (!this.config.options.dryRun) {
276
+ console.log(` 🔨 Creating backup collection: ${backupName}`);
277
+
278
+ // Import schema creation functions from src (tsx will compile on the fly)
279
+ const { createMemoryCollection } = await import('../src/weaviate/schema.js');
280
+ const { PUBLIC_COLLECTION_NAME } = await import('../src/weaviate/space-schema.js');
281
+
282
+ // Create backup by temporarily creating with backup name
283
+ // We'll use the same schema creation logic
284
+ if (collectionName === PUBLIC_COLLECTION_NAME || collectionName === 'Memory_public') {
285
+ // For public collection, create manually with backup name
286
+ const sourceCollectionRef = this.client.collections.get(collectionName);
287
+ const publicSchema = await sourceCollectionRef.config.get();
288
+
289
+ await this.client.collections.create({
290
+ name: backupName,
291
+ vectorizers: weaviate.configure.vectorizer.text2VecOpenAI({
292
+ model: 'text-embedding-3-small',
293
+ sourceProperties: ['content', 'title', 'summary', 'observation'],
294
+ }),
295
+ properties: publicSchema.properties as any,
296
+ });
297
+ } else {
298
+ // For user collections, create with backup name
299
+ // Extract user ID and create using our schema function
300
+ const userId = collectionName.replace('Memory_', '');
301
+
302
+ // Temporarily override the collection name by creating directly
303
+ const userCollection = this.client.collections.get(collectionName);
304
+ const userSchema = await userCollection.config.get();
305
+
306
+ await this.client.collections.create({
307
+ name: backupName,
308
+ vectorizers: weaviate.configure.vectorizer.text2VecOpenAI({
309
+ model: 'text-embedding-3-small',
310
+ sourceProperties: ['content', 'title', 'summary', 'observation'],
311
+ }),
312
+ properties: userSchema.properties as any,
313
+ });
314
+ }
315
+
316
+ console.log(` ✓ Backup collection created`);
317
+ this.stateManager.updateCollectionStatus(collectionName, 'schema_created');
318
+ await this.stateManager.save();
319
+ } else {
320
+ console.log(` [DRY RUN] Would create backup collection: ${backupName}`);
321
+ }
322
+
323
+ // Copy documents in batches
324
+ console.log(` 📋 Copying documents...`);
325
+ this.stateManager.updateCollectionStatus(collectionName, 'copying');
326
+ await this.stateManager.save();
327
+
328
+ let offset = 0;
329
+ let copiedCount = 0;
330
+
331
+ while (offset < totalCount) {
332
+ const result = await sourceCollection.query.fetchObjects({
333
+ limit: this.config.options.batchSize,
334
+ offset,
335
+ includeVector: true,
336
+ });
337
+
338
+ if (result.objects.length === 0) {
339
+ break;
340
+ }
341
+
342
+ if (!this.config.options.dryRun) {
343
+ const backupCollection = this.client.collections.get(backupName);
344
+
345
+ const objects = result.objects.map(doc => ({
346
+ properties: doc.properties,
347
+ vectors: doc.vectors, // v3 API uses 'vectors' not 'vector'
348
+ uuid: doc.uuid, // Preserve original IDs
349
+ }));
350
+
351
+ await backupCollection.data.insertMany(objects);
352
+ }
353
+
354
+ copiedCount += result.objects.length;
355
+ offset += result.objects.length;
356
+
357
+ const percentage = (copiedCount / totalCount) * 100;
358
+ const progressBar = this.createProgressBar(percentage);
359
+ process.stdout.write(`\r ${progressBar} ${percentage.toFixed(1)}% (${copiedCount}/${totalCount})`);
360
+
361
+ this.stateManager.updateCollectionStatus(collectionName, 'copying', {
362
+ copied_documents: copiedCount,
363
+ });
364
+ await this.stateManager.save();
365
+ }
366
+
367
+ console.log('\n ✓ Copy complete');
368
+
369
+ // Verify
370
+ if (!this.config.options.dryRun) {
371
+ const backupCollection = this.client.collections.get(backupName);
372
+ const backupAggregate = await backupCollection.aggregate.overAll();
373
+ const backupCount = backupAggregate.totalCount || 0;
374
+
375
+ if (backupCount !== totalCount) {
376
+ throw new Error(
377
+ `Document count mismatch: source ${totalCount}, backup ${backupCount}`
378
+ );
379
+ }
380
+
381
+ console.log(` ✓ Verified: ${backupCount} documents in backup`);
382
+ }
383
+
384
+ await this.stateManager.completeCollection(collectionName);
385
+ console.log(` ✅ Backup complete: ${backupName}\n`);
386
+
387
+ return true;
388
+ } catch (error: any) {
389
+ console.error(` ❌ Backup failed: ${error.message}\n`);
390
+ await this.stateManager.addError(collectionName, error.message);
391
+ this.stateManager.updateCollectionStatus(collectionName, 'failed');
392
+ await this.stateManager.save();
393
+ return false;
394
+ }
395
+ }
396
+
397
+ private createProgressBar(percentage: number, width: number = 20): string {
398
+ const filled = Math.round((percentage / 100) * width);
399
+ const empty = width - filled;
400
+ return `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
401
+ }
402
+
403
+ async backupAll(): Promise<void> {
404
+ console.log('🚀 Weaviate Collection Backup');
405
+ console.log('━'.repeat(60));
406
+ console.log('\n📊 Configuration:');
407
+ console.log(` Weaviate URL: ${this.config.weaviate.url}`);
408
+ console.log(` Batch Size: ${this.config.options.batchSize}`);
409
+ console.log(` Dry Run: ${this.config.options.dryRun}\n`);
410
+
411
+ if (this.config.options.dryRun) {
412
+ console.log('⚠️ DRY RUN MODE - No changes will be made\n');
413
+ }
414
+
415
+ try {
416
+ await this.connect();
417
+
418
+ const collections = await this.discoverCollections();
419
+ await this.stateManager.initialize(collections);
420
+
421
+ let successCount = 0;
422
+ let failCount = 0;
423
+
424
+ for (const collectionName of collections) {
425
+ const success = await this.backupCollection(collectionName);
426
+ if (success) {
427
+ successCount++;
428
+ } else {
429
+ failCount++;
430
+ }
431
+ }
432
+
433
+ // Summary
434
+ console.log('━'.repeat(60));
435
+ console.log('✅ Backup Summary:');
436
+ console.log('━'.repeat(60));
437
+ console.log(` Total Collections: ${collections.length}`);
438
+ console.log(` Successful: ${successCount}`);
439
+ console.log(` Failed: ${failCount}`);
440
+ console.log(` Dry Run: ${this.config.options.dryRun}\n`);
441
+
442
+ if (failCount === 0) {
443
+ await this.stateManager.complete();
444
+
445
+ if (!this.config.options.dryRun) {
446
+ await this.stateManager.cleanup();
447
+ console.log('✨ Backup completed successfully!\n');
448
+ console.log('📋 Backup collections created:');
449
+ collections.forEach(name => {
450
+ console.log(` - Backup_${name}`);
451
+ });
452
+ console.log('\n⚠️ Remember to delete backup collections after migration succeeds:\n');
453
+ collections.forEach(name => {
454
+ console.log(` npx tsx scripts/delete-collection.ts --collection "Backup_${name}"`);
455
+ });
456
+ console.log('');
457
+ } else {
458
+ console.log('✨ Dry run completed successfully!\n');
459
+ }
460
+ } else {
461
+ console.log(`⚠️ Backup completed with ${failCount} failures`);
462
+ console.log(` Check ${this.config.options.stateFile} for details\n`);
463
+ }
464
+
465
+ } catch (error: any) {
466
+ console.error(`\n❌ Backup failed: ${error.message}`);
467
+ await this.stateManager.fail(error.message);
468
+ throw error;
469
+ } finally {
470
+ await this.disconnect();
471
+ }
472
+ }
473
+ }
474
+
475
+ // ============================================================================
476
+ // Configuration
477
+ // ============================================================================
478
+
479
+ function loadConfig(): BackupConfig {
480
+ // Parse CLI arguments
481
+ const args = process.argv.slice(2);
482
+ const cliArgs: Record<string, string> = {};
483
+
484
+ for (let i = 0; i < args.length; i++) {
485
+ if (args[i].startsWith('--')) {
486
+ const key = args[i].slice(2);
487
+ const value = args[i + 1];
488
+ if (value && !value.startsWith('--')) {
489
+ cliArgs[key] = value;
490
+ i++;
491
+ } else {
492
+ cliArgs[key] = 'true';
493
+ }
494
+ }
495
+ }
496
+
497
+ const config: BackupConfig = {
498
+ weaviate: {
499
+ url: cliArgs['weaviate-url'] || process.env.WEAVIATE_REST_URL || '',
500
+ apiKey: cliArgs['weaviate-key'] || process.env.WEAVIATE_API_KEY,
501
+ openaiApiKey: cliArgs['openai-key'] || process.env.OPENAI_EMBEDDINGS_API_KEY,
502
+ },
503
+ options: {
504
+ batchSize: parseInt(cliArgs['batch-size'] || process.env.BATCH_SIZE || '100'),
505
+ dryRun: cliArgs['dry-run'] === 'true',
506
+ collections: cliArgs['collections']?.split(',').map(c => c.trim()),
507
+ stateFile: cliArgs['state-file'] || '.collection-backup-state.yaml',
508
+ },
509
+ };
510
+
511
+ // Validate
512
+ if (!config.weaviate.url) {
513
+ throw new Error('Weaviate URL is required (--weaviate-url or WEAVIATE_REST_URL)');
514
+ }
515
+
516
+ return config;
517
+ }
518
+
519
+ // ============================================================================
520
+ // Main
521
+ // ============================================================================
522
+
523
+ async function main() {
524
+ try {
525
+ const config = loadConfig();
526
+ const backup = new WeaviateBackup(config);
527
+
528
+ await backup.backupAll();
529
+
530
+ process.exit(0);
531
+ } catch (error: any) {
532
+ console.error(`\n❌ Fatal error: ${error.message}`);
533
+ console.error(error.stack);
534
+ process.exit(1);
535
+ }
536
+ }
537
+
538
+ // Run if executed directly
539
+ if (import.meta.url === `file://${process.argv[1]}`) {
540
+ main();
541
+ }
542
+
543
+ export { WeaviateBackup, type BackupConfig };
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Delete Collection Script
4
+ *
5
+ * Safely deletes a Weaviate collection with confirmation.
6
+ *
7
+ * Usage:
8
+ * # Delete a specific collection
9
+ * npx tsx scripts/delete-collection.ts --collection "Memory_user123"
10
+ *
11
+ * # Delete with confirmation skip (dangerous!)
12
+ * npx tsx scripts/delete-collection.ts --collection "Memory_user123" --force
13
+ */
14
+
15
+ import weaviate, { WeaviateClient } from 'weaviate-client';
16
+ import * as dotenv from 'dotenv';
17
+ import * as readline from 'readline';
18
+
19
+ // Load environment
20
+ dotenv.config();
21
+
22
+ async function promptConfirmation(message: string): Promise<boolean> {
23
+ const rl = readline.createInterface({
24
+ input: process.stdin,
25
+ output: process.stdout,
26
+ });
27
+
28
+ return new Promise((resolve) => {
29
+ rl.question(`${message} (yes/no): `, (answer) => {
30
+ rl.close();
31
+ resolve(answer.toLowerCase() === 'yes');
32
+ });
33
+ });
34
+ }
35
+
36
+ async function main() {
37
+ // Parse CLI arguments
38
+ const args = process.argv.slice(2);
39
+ const cliArgs: Record<string, string> = {};
40
+
41
+ for (let i = 0; i < args.length; i++) {
42
+ if (args[i].startsWith('--')) {
43
+ const key = args[i].slice(2);
44
+ const value = args[i + 1];
45
+ if (value && !value.startsWith('--')) {
46
+ cliArgs[key] = value;
47
+ i++;
48
+ } else {
49
+ cliArgs[key] = 'true';
50
+ }
51
+ }
52
+ }
53
+
54
+ const collectionName = cliArgs['collection'];
55
+ const force = cliArgs['force'] === 'true';
56
+ const weaviateUrl = cliArgs['weaviate-url'] || process.env.WEAVIATE_REST_URL || '';
57
+ const weaviateApiKey = cliArgs['weaviate-key'] || process.env.WEAVIATE_API_KEY;
58
+ const openaiApiKey = cliArgs['openai-key'] || process.env.OPENAI_EMBEDDINGS_API_KEY;
59
+
60
+ if (!collectionName) {
61
+ console.error('❌ Error: --collection argument is required');
62
+ console.log('\nUsage:');
63
+ console.log(' npx tsx scripts/delete-collection.ts --collection "Memory_user123"');
64
+ process.exit(1);
65
+ }
66
+
67
+ if (!weaviateUrl) {
68
+ console.error('❌ Error: WEAVIATE_REST_URL environment variable is required');
69
+ process.exit(1);
70
+ }
71
+
72
+ try {
73
+ console.log('🔌 Connecting to Weaviate...');
74
+
75
+ const clientConfig: any = {
76
+ authCredentials: weaviateApiKey ? new weaviate.ApiKey(weaviateApiKey) : undefined,
77
+ };
78
+
79
+ if (openaiApiKey) {
80
+ clientConfig.headers = {
81
+ 'X-Openai-Api-Key': openaiApiKey,
82
+ };
83
+ }
84
+
85
+ const client = await weaviate.connectToWeaviateCloud(weaviateUrl, clientConfig);
86
+ console.log('✓ Connected\n');
87
+
88
+ // Check if collection exists
89
+ const exists = await client.collections.exists(collectionName);
90
+ if (!exists) {
91
+ console.log(`ℹ️ Collection ${collectionName} does not exist`);
92
+ await client.close();
93
+ process.exit(0);
94
+ }
95
+
96
+ // Get document count
97
+ const collection = client.collections.get(collectionName);
98
+ const aggregate = await collection.aggregate.overAll();
99
+ const documentCount = aggregate.totalCount || 0;
100
+
101
+ console.log(`📦 Collection: ${collectionName}`);
102
+ console.log(`📊 Documents: ${documentCount}`);
103
+ console.log('');
104
+
105
+ // Confirm deletion
106
+ if (!force) {
107
+ const confirmed = await promptConfirmation(
108
+ `⚠️ Are you sure you want to delete "${collectionName}" with ${documentCount} documents?`
109
+ );
110
+
111
+ if (!confirmed) {
112
+ console.log('❌ Deletion cancelled');
113
+ await client.close();
114
+ process.exit(0);
115
+ }
116
+ }
117
+
118
+ // Delete collection
119
+ console.log(`\n🗑️ Deleting ${collectionName}...`);
120
+ await client.collections.delete(collectionName);
121
+ console.log('✓ Collection deleted\n');
122
+
123
+ await client.close();
124
+
125
+ console.log('✨ Done!\n');
126
+ process.exit(0);
127
+ } catch (error: any) {
128
+ console.error(`\n❌ Error: ${error.message}`);
129
+ console.error(error.stack);
130
+ process.exit(1);
131
+ }
132
+ }
133
+
134
+ // Run if executed directly
135
+ if (import.meta.url === `file://${process.argv[1]}`) {
136
+ main();
137
+ }