@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.
- package/AGENT.md +296 -250
- package/CHANGELOG.md +358 -0
- package/README.md +68 -45
- package/agent/commands/acp.clarification-create.md +382 -0
- package/agent/commands/acp.project-info.md +309 -0
- package/agent/commands/acp.project-remove.md +379 -0
- package/agent/commands/acp.project-update.md +296 -0
- package/agent/commands/acp.task-create.md +17 -9
- package/agent/commands/git.commit.md +13 -1
- package/agent/design/comment-memory-type.md +2 -2
- package/agent/design/local.collaborative-memory-sync.md +265 -0
- package/agent/design/local.content-flags.md +210 -0
- package/agent/design/local.ghost-persona-system.md +273 -0
- package/agent/design/local.group-acl-integration.md +338 -0
- package/agent/design/local.memory-acl-schema.md +352 -0
- package/agent/design/local.memory-collection-pattern-v2.md +348 -0
- package/agent/design/local.moderation-and-space-config.md +257 -0
- package/agent/design/local.v2-api-reference.md +621 -0
- package/agent/design/local.v2-migration-guide.md +191 -0
- package/agent/design/local.v2-usage-examples.md +265 -0
- package/agent/design/permissions-storage-architecture.md +11 -3
- package/agent/design/trust-escalation-prevention.md +9 -2
- package/agent/design/trust-system-implementation.md +12 -3
- package/agent/milestones/milestone-14-memory-collection-v2.md +182 -0
- package/agent/milestones/milestone-15-moderation-space-config.md +126 -0
- package/agent/progress.yaml +628 -49
- package/agent/scripts/acp.common.sh +2 -0
- package/agent/scripts/acp.install.sh +11 -1
- package/agent/scripts/acp.package-install-optimized.sh +454 -0
- package/agent/scripts/acp.package-install.sh +247 -300
- package/agent/scripts/acp.project-info.sh +218 -0
- package/agent/scripts/acp.project-remove.sh +302 -0
- package/agent/scripts/acp.project-update.sh +296 -0
- package/agent/scripts/acp.yaml-parser.sh +128 -10
- package/agent/tasks/milestone-14-memory-collection-v2/task-165-core-infrastructure-setup.md +171 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-166-update-remember-publish.md +191 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-167-update-remember-retract.md +186 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-168-implement-remember-revise.md +184 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-169-update-remember-search-space.md +179 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-170-update-remember-create-update.md +139 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-172-performance-testing-optimization.md +161 -0
- package/agent/tasks/milestone-14-memory-collection-v2/task-173-documentation-examples.md +258 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-174-add-moderation-schema-fields.md +57 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-175-create-space-config-service.md +64 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-176-wire-moderation-publish-flow.md +45 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-177-add-moderation-search-filters.md +70 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-178-create-remember-moderate-tool.md +69 -0
- package/agent/tasks/milestone-15-moderation-space-config/task-179-documentation-integration-tests.md +58 -0
- package/agent/tasks/milestone-16-ghost-system/task-187-ghost-config-firestore.md +41 -0
- package/agent/tasks/milestone-16-ghost-system/task-188-trust-filter-integration.md +44 -0
- package/agent/tasks/milestone-16-ghost-system/task-189-ghost-memory-filtering.md +43 -0
- package/agent/tasks/milestone-16-ghost-system/task-190-ghost-config-tools.md +45 -0
- package/agent/tasks/milestone-16-ghost-system/task-191-escalation-firestore.md +38 -0
- package/agent/tasks/milestone-16-ghost-system/task-192-documentation-verification.md +39 -0
- package/agent/tasks/milestone-7-trust-permissions/task-180-access-result-permission-types.md +69 -0
- package/agent/tasks/milestone-7-trust-permissions/task-181-firestore-permissions-access-logs.md +56 -0
- package/agent/tasks/milestone-7-trust-permissions/task-182-trust-enforcement-service.md +68 -0
- package/agent/tasks/milestone-7-trust-permissions/task-183-access-control-service.md +70 -0
- package/agent/tasks/milestone-7-trust-permissions/task-184-permission-tools.md +79 -0
- package/agent/tasks/milestone-7-trust-permissions/task-185-wire-trust-into-search-query.md +55 -0
- package/agent/tasks/milestone-7-trust-permissions/task-186-documentation-verification.md +56 -0
- package/agent/tasks/task-76-fix-indexnullstate-schema-bug.md +197 -0
- package/dist/collections/composite-ids.d.ts +106 -0
- package/dist/collections/core-infrastructure.spec.d.ts +11 -0
- package/dist/collections/dot-notation.d.ts +106 -0
- package/dist/collections/tracking-arrays.d.ts +176 -0
- package/dist/constants/content-types.d.ts +1 -0
- package/dist/schema/v2-collections-comments.spec.d.ts +8 -0
- package/dist/schema/v2-collections.d.ts +210 -0
- package/dist/server-factory.d.ts +15 -0
- package/dist/server-factory.js +2798 -1029
- package/dist/server.js +2526 -1012
- package/dist/services/access-control.d.ts +103 -0
- package/dist/services/access-control.spec.d.ts +2 -0
- package/dist/services/credentials-provider.d.ts +24 -0
- package/dist/services/credentials-provider.spec.d.ts +2 -0
- package/dist/services/escalation.service.d.ts +22 -0
- package/dist/services/escalation.service.spec.d.ts +2 -0
- package/dist/services/ghost-config.service.d.ts +55 -0
- package/dist/services/ghost-config.service.spec.d.ts +2 -0
- package/dist/services/space-config.service.d.ts +23 -0
- package/dist/services/space-config.service.spec.d.ts +2 -0
- package/dist/services/trust-enforcement.d.ts +83 -0
- package/dist/services/trust-enforcement.spec.d.ts +2 -0
- package/dist/services/trust-validator.d.ts +43 -0
- package/dist/services/trust-validator.spec.d.ts +2 -0
- package/dist/tools/confirm-publish-moderation.spec.d.ts +8 -0
- package/dist/tools/confirm.d.ts +8 -1
- package/dist/tools/create-memory.d.ts +2 -1
- package/dist/tools/create-memory.spec.d.ts +10 -0
- package/dist/tools/create-relationship.d.ts +2 -1
- package/dist/tools/delete-memory.d.ts +2 -1
- package/dist/tools/delete-relationship.d.ts +2 -1
- package/dist/tools/deny.d.ts +2 -1
- package/dist/tools/find-similar.d.ts +2 -1
- package/dist/tools/get-preferences.d.ts +2 -1
- package/dist/tools/ghost-config.d.ts +27 -0
- package/dist/tools/ghost-config.spec.d.ts +2 -0
- package/dist/tools/moderate.d.ts +20 -0
- package/dist/tools/moderate.spec.d.ts +5 -0
- package/dist/tools/publish.d.ts +11 -3
- package/dist/tools/query-memory.d.ts +3 -1
- package/dist/tools/query-space.d.ts +4 -1
- package/dist/tools/retract.d.ts +29 -0
- package/dist/tools/revise.d.ts +45 -0
- package/dist/tools/revise.spec.d.ts +8 -0
- package/dist/tools/search-memory.d.ts +2 -1
- package/dist/tools/search-relationship.d.ts +2 -1
- package/dist/tools/search-space.d.ts +25 -5
- package/dist/tools/search-space.spec.d.ts +9 -0
- package/dist/tools/set-preference.d.ts +2 -1
- package/dist/tools/update-memory.d.ts +2 -1
- package/dist/tools/update-relationship.d.ts +2 -1
- package/dist/types/access-result.d.ts +48 -0
- package/dist/types/access-result.spec.d.ts +2 -0
- package/dist/types/auth.d.ts +46 -0
- package/dist/types/ghost-config.d.ts +36 -0
- package/dist/types/memory.d.ts +3 -1
- package/dist/types/preferences.d.ts +1 -1
- package/dist/utils/auth-helpers.d.ts +14 -0
- package/dist/utils/auth-helpers.spec.d.ts +2 -0
- package/dist/utils/test-data-generator.d.ts +124 -0
- package/dist/utils/test-data-generator.spec.d.ts +12 -0
- package/dist/v2-performance.e2e.d.ts +17 -0
- package/dist/v2-smoke.e2e.d.ts +14 -0
- package/dist/weaviate/client.d.ts +5 -8
- package/dist/weaviate/space-schema.d.ts +2 -2
- package/docs/performance/v2-benchmarks.md +80 -0
- package/jest.e2e.config.js +14 -3
- package/package.json +1 -1
- package/scripts/.collection-recreation-state.yaml +16 -0
- package/scripts/.gitkeep +5 -0
- package/scripts/README-collection-recreation.md +224 -0
- package/scripts/README.md +51 -0
- package/scripts/backup-collections.ts +543 -0
- package/scripts/delete-collection.ts +137 -0
- package/scripts/migrate-recreate-collections.ts +578 -0
- package/scripts/migrate-v1-to-v2.ts +1094 -0
- package/scripts/package-lock.json +1113 -0
- package/scripts/package.json +27 -0
- package/src/collections/composite-ids.ts +193 -0
- package/src/collections/core-infrastructure.spec.ts +353 -0
- package/src/collections/dot-notation.ts +212 -0
- package/src/collections/tracking-arrays.ts +298 -0
- package/src/constants/content-types.ts +20 -0
- package/src/schema/v2-collections-comments.spec.ts +141 -0
- package/src/schema/v2-collections.ts +433 -0
- package/src/server-factory.ts +89 -20
- package/src/server.ts +45 -17
- package/src/services/access-control.spec.ts +383 -0
- package/src/services/access-control.ts +291 -0
- package/src/services/credentials-provider.spec.ts +22 -0
- package/src/services/credentials-provider.ts +34 -0
- package/src/services/escalation.service.spec.ts +183 -0
- package/src/services/escalation.service.ts +150 -0
- package/src/services/ghost-config.service.spec.ts +339 -0
- package/src/services/ghost-config.service.ts +219 -0
- package/src/services/space-config.service.spec.ts +102 -0
- package/src/services/space-config.service.ts +79 -0
- package/src/services/trust-enforcement.spec.ts +309 -0
- package/src/services/trust-enforcement.ts +197 -0
- package/src/services/trust-validator.spec.ts +108 -0
- package/src/services/trust-validator.ts +105 -0
- package/src/tools/confirm-publish-moderation.spec.ts +240 -0
- package/src/tools/confirm.ts +869 -135
- package/src/tools/create-memory.spec.ts +126 -0
- package/src/tools/create-memory.ts +20 -27
- package/src/tools/create-relationship.ts +17 -8
- package/src/tools/delete-memory.ts +13 -6
- package/src/tools/delete-relationship.ts +15 -6
- package/src/tools/deny.ts +8 -1
- package/src/tools/find-similar.ts +21 -8
- package/src/tools/get-preferences.ts +10 -1
- package/src/tools/ghost-config.spec.ts +180 -0
- package/src/tools/ghost-config.ts +230 -0
- package/src/tools/moderate.spec.ts +277 -0
- package/src/tools/moderate.ts +219 -0
- package/src/tools/publish.ts +99 -41
- package/src/tools/query-memory.ts +28 -6
- package/src/tools/query-space.ts +39 -4
- package/src/tools/retract.ts +292 -0
- package/src/tools/revise.spec.ts +146 -0
- package/src/tools/revise.ts +283 -0
- package/src/tools/search-memory.ts +30 -7
- package/src/tools/search-relationship.ts +11 -2
- package/src/tools/search-space.spec.ts +341 -0
- package/src/tools/search-space.ts +323 -99
- package/src/tools/set-preference.ts +10 -1
- package/src/tools/update-memory.ts +16 -5
- package/src/tools/update-relationship.ts +10 -1
- package/src/types/access-result.spec.ts +193 -0
- package/src/types/access-result.ts +62 -0
- package/src/types/auth.ts +52 -0
- package/src/types/ghost-config.ts +46 -0
- package/src/types/memory.ts +9 -1
- package/src/types/preferences.ts +2 -2
- package/src/utils/auth-helpers.spec.ts +75 -0
- package/src/utils/auth-helpers.ts +25 -0
- package/src/utils/test-data-generator.spec.ts +317 -0
- package/src/utils/test-data-generator.ts +292 -0
- package/src/utils/weaviate-filters.ts +4 -4
- package/src/v2-performance.e2e.ts +173 -0
- package/src/v2-smoke.e2e.ts +401 -0
- package/src/weaviate/client.spec.ts +5 -5
- package/src/weaviate/client.ts +51 -36
- package/src/weaviate/schema.ts +11 -256
- package/src/weaviate/space-schema.spec.ts +24 -24
- 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
|
+
}
|