mcp-memory-keeper 0.10.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/CHANGELOG.md +433 -0
- package/LICENSE +21 -0
- package/README.md +1051 -0
- package/bin/mcp-memory-keeper +52 -0
- package/dist/__tests__/helpers/database-test-helper.js +160 -0
- package/dist/__tests__/helpers/test-server.js +92 -0
- package/dist/__tests__/integration/advanced-features.test.js +614 -0
- package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
- package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
- package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
- package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
- package/dist/__tests__/integration/channels.test.js +376 -0
- package/dist/__tests__/integration/checkpoint.test.js +251 -0
- package/dist/__tests__/integration/concurrent-access.test.js +190 -0
- package/dist/__tests__/integration/context-operations.test.js +243 -0
- package/dist/__tests__/integration/contextDiff.test.js +852 -0
- package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
- package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
- package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
- package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
- package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
- package/dist/__tests__/integration/contextSearch.test.js +938 -0
- package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
- package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
- package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
- package/dist/__tests__/integration/cross-session-sharing.test.js +302 -0
- package/dist/__tests__/integration/database-initialization.test.js +134 -0
- package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
- package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
- package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
- package/dist/__tests__/integration/error-cases.test.js +407 -0
- package/dist/__tests__/integration/export-import.test.js +367 -0
- package/dist/__tests__/integration/feature-flags.test.js +542 -0
- package/dist/__tests__/integration/file-operations.test.js +264 -0
- package/dist/__tests__/integration/git-integration.test.js +237 -0
- package/dist/__tests__/integration/index-tools.test.js +496 -0
- package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
- package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
- package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
- package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
- package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
- package/dist/__tests__/integration/migrations.test.js +528 -0
- package/dist/__tests__/integration/multi-agent.test.js +546 -0
- package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
- package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
- package/dist/__tests__/integration/project-directory.test.js +283 -0
- package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
- package/dist/__tests__/integration/retention.test.js +513 -0
- package/dist/__tests__/integration/search.test.js +333 -0
- package/dist/__tests__/integration/semantic-search.test.js +266 -0
- package/dist/__tests__/integration/server-initialization.test.js +307 -0
- package/dist/__tests__/integration/session-management.test.js +219 -0
- package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
- package/dist/__tests__/integration/smart-compaction.test.js +230 -0
- package/dist/__tests__/integration/summarization.test.js +308 -0
- package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
- package/dist/__tests__/security/input-validation.test.js +115 -0
- package/dist/__tests__/utils/agents.test.js +473 -0
- package/dist/__tests__/utils/database.test.js +177 -0
- package/dist/__tests__/utils/git.test.js +122 -0
- package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
- package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
- package/dist/__tests__/utils/project-directory-messages.test.js +188 -0
- package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
- package/dist/__tests__/utils/validation.test.js +200 -0
- package/dist/__tests__/utils/vector-store.test.js +231 -0
- package/dist/handlers/contextWatchHandlers.js +206 -0
- package/dist/index.js +4310 -0
- package/dist/index.phase1.backup.js +410 -0
- package/dist/index.phase2.backup.js +704 -0
- package/dist/migrations/003_add_channels.js +174 -0
- package/dist/migrations/004_add_context_watch.js +151 -0
- package/dist/migrations/005_add_context_watch.js +98 -0
- package/dist/migrations/simplify-sharing.js +117 -0
- package/dist/repositories/BaseRepository.js +30 -0
- package/dist/repositories/CheckpointRepository.js +140 -0
- package/dist/repositories/ContextRepository.js +1873 -0
- package/dist/repositories/FileRepository.js +104 -0
- package/dist/repositories/RepositoryManager.js +62 -0
- package/dist/repositories/SessionRepository.js +66 -0
- package/dist/repositories/WatcherRepository.js +252 -0
- package/dist/repositories/index.js +15 -0
- package/dist/server.js +384 -0
- package/dist/test-helpers/database-helper.js +128 -0
- package/dist/types/entities.js +3 -0
- package/dist/utils/agents.js +791 -0
- package/dist/utils/channels.js +150 -0
- package/dist/utils/database.js +731 -0
- package/dist/utils/feature-flags.js +476 -0
- package/dist/utils/git.js +145 -0
- package/dist/utils/knowledge-graph.js +264 -0
- package/dist/utils/migrationHealthCheck.js +373 -0
- package/dist/utils/migrations.js +452 -0
- package/dist/utils/retention.js +460 -0
- package/dist/utils/timestamps.js +112 -0
- package/dist/utils/validation.js +296 -0
- package/dist/utils/vector-store.js +247 -0
- package/package.json +84 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addChannelsMigration = void 0;
|
|
4
|
+
exports.addChannelsMigration = {
|
|
5
|
+
version: '3.0.0',
|
|
6
|
+
name: 'Add channels support for context organization',
|
|
7
|
+
description: 'Add channel column to context_items and default_channel to sessions for better context organization.',
|
|
8
|
+
requiresBackup: true,
|
|
9
|
+
dependencies: ['2.0.0'], // Depends on the simplify-sharing migration
|
|
10
|
+
up: `
|
|
11
|
+
-- Add channel column to context_items table with default 'general'
|
|
12
|
+
-- SQLite doesn't support adding columns with constraints directly, so we need to recreate the table
|
|
13
|
+
CREATE TABLE context_items_new (
|
|
14
|
+
id TEXT PRIMARY KEY,
|
|
15
|
+
session_id TEXT NOT NULL,
|
|
16
|
+
key TEXT NOT NULL,
|
|
17
|
+
value TEXT NOT NULL,
|
|
18
|
+
category TEXT,
|
|
19
|
+
priority TEXT DEFAULT 'normal',
|
|
20
|
+
metadata TEXT,
|
|
21
|
+
size INTEGER DEFAULT 0,
|
|
22
|
+
is_private INTEGER DEFAULT 0,
|
|
23
|
+
channel TEXT NOT NULL DEFAULT 'general',
|
|
24
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
25
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
26
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
|
|
27
|
+
UNIQUE(session_id, key)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
-- Copy data from old table to new, using 'general' as default channel
|
|
31
|
+
INSERT INTO context_items_new (
|
|
32
|
+
id, session_id, key, value, category, priority, metadata, size, is_private, channel, created_at, updated_at
|
|
33
|
+
)
|
|
34
|
+
SELECT
|
|
35
|
+
id, session_id, key, value, category, priority, metadata, size, is_private, 'general', created_at, updated_at
|
|
36
|
+
FROM context_items;
|
|
37
|
+
|
|
38
|
+
-- Drop old table and rename new one
|
|
39
|
+
DROP TABLE context_items;
|
|
40
|
+
ALTER TABLE context_items_new RENAME TO context_items;
|
|
41
|
+
|
|
42
|
+
-- Recreate all indexes including new channel index
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_session ON context_items(session_id);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_key ON context_items(key);
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_category ON context_items(category);
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_priority ON context_items(priority);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_private ON context_items(is_private);
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_created ON context_items(created_at);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_channel ON context_items(channel);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_channel_session ON context_items(channel, session_id);
|
|
51
|
+
|
|
52
|
+
-- Recreate the updated_at trigger
|
|
53
|
+
DROP TRIGGER IF EXISTS update_context_items_timestamp;
|
|
54
|
+
CREATE TRIGGER update_context_items_timestamp
|
|
55
|
+
AFTER UPDATE ON context_items
|
|
56
|
+
BEGIN
|
|
57
|
+
UPDATE context_items SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
|
58
|
+
END;
|
|
59
|
+
|
|
60
|
+
-- Add default_channel column to sessions table
|
|
61
|
+
-- Again, need to recreate the table
|
|
62
|
+
CREATE TABLE sessions_new (
|
|
63
|
+
id TEXT PRIMARY KEY,
|
|
64
|
+
name TEXT,
|
|
65
|
+
description TEXT,
|
|
66
|
+
branch TEXT,
|
|
67
|
+
working_directory TEXT,
|
|
68
|
+
parent_id TEXT,
|
|
69
|
+
default_channel TEXT NOT NULL DEFAULT 'general',
|
|
70
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
71
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
72
|
+
FOREIGN KEY (parent_id) REFERENCES sessions(id)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
-- Copy data from old sessions table
|
|
76
|
+
INSERT INTO sessions_new (
|
|
77
|
+
id, name, description, branch, working_directory, parent_id, default_channel, created_at, updated_at
|
|
78
|
+
)
|
|
79
|
+
SELECT
|
|
80
|
+
id, name, description, branch, working_directory, parent_id, 'general', created_at, updated_at
|
|
81
|
+
FROM sessions;
|
|
82
|
+
|
|
83
|
+
-- Drop old table and rename new one
|
|
84
|
+
DROP TABLE sessions;
|
|
85
|
+
ALTER TABLE sessions_new RENAME TO sessions;
|
|
86
|
+
|
|
87
|
+
-- Recreate the updated_at trigger for sessions
|
|
88
|
+
DROP TRIGGER IF EXISTS update_sessions_timestamp;
|
|
89
|
+
CREATE TRIGGER update_sessions_timestamp
|
|
90
|
+
AFTER UPDATE ON sessions
|
|
91
|
+
BEGIN
|
|
92
|
+
UPDATE sessions SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
|
93
|
+
END;
|
|
94
|
+
`,
|
|
95
|
+
down: `
|
|
96
|
+
-- Revert context_items to previous schema
|
|
97
|
+
CREATE TABLE context_items_old (
|
|
98
|
+
id TEXT PRIMARY KEY,
|
|
99
|
+
session_id TEXT NOT NULL,
|
|
100
|
+
key TEXT NOT NULL,
|
|
101
|
+
value TEXT NOT NULL,
|
|
102
|
+
category TEXT,
|
|
103
|
+
priority TEXT DEFAULT 'normal',
|
|
104
|
+
metadata TEXT,
|
|
105
|
+
size INTEGER DEFAULT 0,
|
|
106
|
+
is_private INTEGER DEFAULT 0,
|
|
107
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
108
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
109
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
|
|
110
|
+
UNIQUE(session_id, key)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
-- Copy data back without channel column
|
|
114
|
+
INSERT INTO context_items_old (
|
|
115
|
+
id, session_id, key, value, category, priority, metadata, size, is_private, created_at, updated_at
|
|
116
|
+
)
|
|
117
|
+
SELECT
|
|
118
|
+
id, session_id, key, value, category, priority, metadata, size, is_private, created_at, updated_at
|
|
119
|
+
FROM context_items;
|
|
120
|
+
|
|
121
|
+
-- Drop new table and rename old one back
|
|
122
|
+
DROP TABLE context_items;
|
|
123
|
+
ALTER TABLE context_items_old RENAME TO context_items;
|
|
124
|
+
|
|
125
|
+
-- Recreate original indexes
|
|
126
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_session ON context_items(session_id);
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_key ON context_items(key);
|
|
128
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_category ON context_items(category);
|
|
129
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_priority ON context_items(priority);
|
|
130
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_private ON context_items(is_private);
|
|
131
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_created ON context_items(created_at);
|
|
132
|
+
|
|
133
|
+
-- Recreate the updated_at trigger
|
|
134
|
+
DROP TRIGGER IF EXISTS update_context_items_timestamp;
|
|
135
|
+
CREATE TRIGGER update_context_items_timestamp
|
|
136
|
+
AFTER UPDATE ON context_items
|
|
137
|
+
BEGIN
|
|
138
|
+
UPDATE context_items SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
|
139
|
+
END;
|
|
140
|
+
|
|
141
|
+
-- Revert sessions to previous schema
|
|
142
|
+
CREATE TABLE sessions_old (
|
|
143
|
+
id TEXT PRIMARY KEY,
|
|
144
|
+
name TEXT,
|
|
145
|
+
description TEXT,
|
|
146
|
+
branch TEXT,
|
|
147
|
+
working_directory TEXT,
|
|
148
|
+
parent_id TEXT,
|
|
149
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
150
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
151
|
+
FOREIGN KEY (parent_id) REFERENCES sessions(id)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
-- Copy data back without default_channel column
|
|
155
|
+
INSERT INTO sessions_old (
|
|
156
|
+
id, name, description, branch, working_directory, parent_id, created_at, updated_at
|
|
157
|
+
)
|
|
158
|
+
SELECT
|
|
159
|
+
id, name, description, branch, working_directory, parent_id, created_at, updated_at
|
|
160
|
+
FROM sessions;
|
|
161
|
+
|
|
162
|
+
-- Drop new table and rename old one back
|
|
163
|
+
DROP TABLE sessions;
|
|
164
|
+
ALTER TABLE sessions_old RENAME TO sessions;
|
|
165
|
+
|
|
166
|
+
-- Recreate the updated_at trigger for sessions
|
|
167
|
+
DROP TRIGGER IF EXISTS update_sessions_timestamp;
|
|
168
|
+
CREATE TRIGGER update_sessions_timestamp
|
|
169
|
+
AFTER UPDATE ON sessions
|
|
170
|
+
BEGIN
|
|
171
|
+
UPDATE sessions SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
|
172
|
+
END;
|
|
173
|
+
`,
|
|
174
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.description = exports.version = void 0;
|
|
4
|
+
exports.up = up;
|
|
5
|
+
exports.down = down;
|
|
6
|
+
exports.needsMigration = needsMigration;
|
|
7
|
+
exports.version = '0.4.0';
|
|
8
|
+
exports.description = 'Add context watch functionality with change tracking';
|
|
9
|
+
function up(db) {
|
|
10
|
+
db.transaction(() => {
|
|
11
|
+
// Create change tracking table
|
|
12
|
+
db.exec(`
|
|
13
|
+
CREATE TABLE IF NOT EXISTS context_changes (
|
|
14
|
+
sequence_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15
|
+
session_id TEXT NOT NULL,
|
|
16
|
+
item_id TEXT NOT NULL,
|
|
17
|
+
key TEXT NOT NULL,
|
|
18
|
+
operation TEXT NOT NULL CHECK (operation IN ('CREATE', 'UPDATE', 'DELETE')),
|
|
19
|
+
old_value TEXT,
|
|
20
|
+
new_value TEXT,
|
|
21
|
+
old_metadata TEXT,
|
|
22
|
+
new_metadata TEXT,
|
|
23
|
+
category TEXT,
|
|
24
|
+
priority TEXT,
|
|
25
|
+
channel TEXT,
|
|
26
|
+
size_delta INTEGER DEFAULT 0,
|
|
27
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
28
|
+
created_by TEXT,
|
|
29
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
30
|
+
);
|
|
31
|
+
`);
|
|
32
|
+
// Create watchers registry table
|
|
33
|
+
db.exec(`
|
|
34
|
+
CREATE TABLE IF NOT EXISTS context_watchers (
|
|
35
|
+
id TEXT PRIMARY KEY,
|
|
36
|
+
session_id TEXT,
|
|
37
|
+
filter_keys TEXT,
|
|
38
|
+
filter_categories TEXT,
|
|
39
|
+
filter_channels TEXT,
|
|
40
|
+
filter_priorities TEXT,
|
|
41
|
+
last_sequence INTEGER DEFAULT 0,
|
|
42
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
43
|
+
last_poll_at TIMESTAMP,
|
|
44
|
+
expires_at TIMESTAMP,
|
|
45
|
+
metadata TEXT,
|
|
46
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
47
|
+
);
|
|
48
|
+
`);
|
|
49
|
+
// Create indexes for performance
|
|
50
|
+
db.exec(`
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_changes_sequence ON context_changes(sequence_id);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_changes_session_seq ON context_changes(session_id, sequence_id);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_changes_created ON context_changes(created_at);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_watchers_expires ON context_watchers(expires_at);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_watchers_session ON context_watchers(session_id);
|
|
56
|
+
`);
|
|
57
|
+
// Create triggers for change tracking
|
|
58
|
+
db.exec(`
|
|
59
|
+
-- Trigger for INSERT operations on context_items
|
|
60
|
+
CREATE TRIGGER IF NOT EXISTS track_context_insert
|
|
61
|
+
AFTER INSERT ON context_items
|
|
62
|
+
BEGIN
|
|
63
|
+
INSERT INTO context_changes (
|
|
64
|
+
session_id, item_id, key, operation,
|
|
65
|
+
new_value, new_metadata, category, priority, channel,
|
|
66
|
+
size_delta, created_by
|
|
67
|
+
) VALUES (
|
|
68
|
+
NEW.session_id, NEW.id, NEW.key, 'CREATE',
|
|
69
|
+
NEW.value, NEW.metadata, NEW.category, NEW.priority, NEW.channel,
|
|
70
|
+
NEW.size, 'context_save'
|
|
71
|
+
);
|
|
72
|
+
END;
|
|
73
|
+
|
|
74
|
+
-- Trigger for UPDATE operations on context_items
|
|
75
|
+
CREATE TRIGGER IF NOT EXISTS track_context_update
|
|
76
|
+
AFTER UPDATE ON context_items
|
|
77
|
+
WHEN OLD.value != NEW.value OR
|
|
78
|
+
IFNULL(OLD.metadata, '') != IFNULL(NEW.metadata, '') OR
|
|
79
|
+
IFNULL(OLD.category, '') != IFNULL(NEW.category, '') OR
|
|
80
|
+
IFNULL(OLD.priority, '') != IFNULL(NEW.priority, '') OR
|
|
81
|
+
IFNULL(OLD.channel, '') != IFNULL(NEW.channel, '')
|
|
82
|
+
BEGIN
|
|
83
|
+
INSERT INTO context_changes (
|
|
84
|
+
session_id, item_id, key, operation,
|
|
85
|
+
old_value, new_value, old_metadata, new_metadata,
|
|
86
|
+
category, priority, channel, size_delta, created_by
|
|
87
|
+
) VALUES (
|
|
88
|
+
NEW.session_id, NEW.id, NEW.key, 'UPDATE',
|
|
89
|
+
OLD.value, NEW.value, OLD.metadata, NEW.metadata,
|
|
90
|
+
NEW.category, NEW.priority, NEW.channel,
|
|
91
|
+
NEW.size - OLD.size, 'context_save'
|
|
92
|
+
);
|
|
93
|
+
END;
|
|
94
|
+
|
|
95
|
+
-- Trigger for DELETE operations on context_items
|
|
96
|
+
CREATE TRIGGER IF NOT EXISTS track_context_delete
|
|
97
|
+
AFTER DELETE ON context_items
|
|
98
|
+
BEGIN
|
|
99
|
+
INSERT INTO context_changes (
|
|
100
|
+
session_id, item_id, key, operation,
|
|
101
|
+
old_value, old_metadata, category, priority, channel,
|
|
102
|
+
size_delta, created_by
|
|
103
|
+
) VALUES (
|
|
104
|
+
OLD.session_id, OLD.id, OLD.key, 'DELETE',
|
|
105
|
+
OLD.value, OLD.metadata, OLD.category, OLD.priority, OLD.channel,
|
|
106
|
+
-OLD.size, 'context_delete'
|
|
107
|
+
);
|
|
108
|
+
END;
|
|
109
|
+
`);
|
|
110
|
+
// Add migration record
|
|
111
|
+
db.prepare(`
|
|
112
|
+
INSERT INTO migrations (id, version, name, description, up_sql, applied_at)
|
|
113
|
+
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
114
|
+
`).run(exports.version, exports.version, '004_add_context_watch', exports.description, 'See migration file for full SQL');
|
|
115
|
+
})();
|
|
116
|
+
}
|
|
117
|
+
function down(db) {
|
|
118
|
+
db.transaction(() => {
|
|
119
|
+
// Drop triggers
|
|
120
|
+
db.exec(`
|
|
121
|
+
DROP TRIGGER IF EXISTS track_context_insert;
|
|
122
|
+
DROP TRIGGER IF EXISTS track_context_update;
|
|
123
|
+
DROP TRIGGER IF EXISTS track_context_delete;
|
|
124
|
+
`);
|
|
125
|
+
// Drop indexes
|
|
126
|
+
db.exec(`
|
|
127
|
+
DROP INDEX IF EXISTS idx_changes_sequence;
|
|
128
|
+
DROP INDEX IF EXISTS idx_changes_session_seq;
|
|
129
|
+
DROP INDEX IF EXISTS idx_changes_created;
|
|
130
|
+
DROP INDEX IF EXISTS idx_watchers_expires;
|
|
131
|
+
DROP INDEX IF EXISTS idx_watchers_session;
|
|
132
|
+
`);
|
|
133
|
+
// Drop tables
|
|
134
|
+
db.exec(`
|
|
135
|
+
DROP TABLE IF EXISTS context_watchers;
|
|
136
|
+
DROP TABLE IF EXISTS context_changes;
|
|
137
|
+
`);
|
|
138
|
+
// Remove migration record
|
|
139
|
+
db.prepare('DELETE FROM migrations WHERE version = ?').run(exports.version);
|
|
140
|
+
})();
|
|
141
|
+
}
|
|
142
|
+
// Helper to check if migration is needed
|
|
143
|
+
function needsMigration(db) {
|
|
144
|
+
const result = db
|
|
145
|
+
.prepare(`
|
|
146
|
+
SELECT COUNT(*) as count FROM sqlite_master
|
|
147
|
+
WHERE type='table' AND name IN ('context_changes', 'context_watchers')
|
|
148
|
+
`)
|
|
149
|
+
.get();
|
|
150
|
+
return result.count < 2;
|
|
151
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.down = exports.up = exports.description = exports.version = void 0;
|
|
4
|
+
exports.version = '0.5.0';
|
|
5
|
+
exports.description = 'Add missing context watch functionality';
|
|
6
|
+
const up = (db) => {
|
|
7
|
+
db.transaction(() => {
|
|
8
|
+
// Add is_active column to context_watchers if not exists
|
|
9
|
+
const watchers_columns = db.prepare('PRAGMA table_info(context_watchers)').all();
|
|
10
|
+
if (!watchers_columns.some((col) => col.name === 'is_active')) {
|
|
11
|
+
db.exec('ALTER TABLE context_watchers ADD COLUMN is_active INTEGER DEFAULT 1');
|
|
12
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_watchers_active ON context_watchers(is_active)');
|
|
13
|
+
}
|
|
14
|
+
// Add sequence_number column to context_items if not exists
|
|
15
|
+
const columns = db.prepare('PRAGMA table_info(context_items)').all();
|
|
16
|
+
if (!columns.some((col) => col.name === 'sequence_number')) {
|
|
17
|
+
db.exec('ALTER TABLE context_items ADD COLUMN sequence_number INTEGER DEFAULT 0');
|
|
18
|
+
// Update existing rows with sequence numbers
|
|
19
|
+
db.exec(`
|
|
20
|
+
UPDATE context_items
|
|
21
|
+
SET sequence_number = (
|
|
22
|
+
SELECT COUNT(*)
|
|
23
|
+
FROM context_items c2
|
|
24
|
+
WHERE c2.session_id = context_items.session_id
|
|
25
|
+
AND c2.created_at <= context_items.created_at
|
|
26
|
+
)
|
|
27
|
+
WHERE sequence_number = 0
|
|
28
|
+
`);
|
|
29
|
+
// Create trigger to auto-increment sequence numbers for new inserts
|
|
30
|
+
db.exec(`
|
|
31
|
+
CREATE TRIGGER IF NOT EXISTS increment_sequence_insert
|
|
32
|
+
AFTER INSERT ON context_items
|
|
33
|
+
FOR EACH ROW
|
|
34
|
+
WHEN NEW.sequence_number = 0
|
|
35
|
+
BEGIN
|
|
36
|
+
UPDATE context_items
|
|
37
|
+
SET sequence_number = (
|
|
38
|
+
SELECT COALESCE(MAX(sequence_number), 0) + 1
|
|
39
|
+
FROM context_items
|
|
40
|
+
WHERE session_id = NEW.session_id
|
|
41
|
+
)
|
|
42
|
+
WHERE id = NEW.id;
|
|
43
|
+
END
|
|
44
|
+
`);
|
|
45
|
+
// Create trigger to update sequence numbers on updates
|
|
46
|
+
db.exec(`
|
|
47
|
+
CREATE TRIGGER IF NOT EXISTS increment_sequence_update
|
|
48
|
+
AFTER UPDATE OF value, metadata, category, priority, channel ON context_items
|
|
49
|
+
FOR EACH ROW
|
|
50
|
+
WHEN OLD.value != NEW.value OR
|
|
51
|
+
IFNULL(OLD.metadata, '') != IFNULL(NEW.metadata, '') OR
|
|
52
|
+
IFNULL(OLD.category, '') != IFNULL(NEW.category, '') OR
|
|
53
|
+
IFNULL(OLD.priority, '') != IFNULL(NEW.priority, '') OR
|
|
54
|
+
IFNULL(OLD.channel, '') != IFNULL(NEW.channel, '')
|
|
55
|
+
BEGIN
|
|
56
|
+
UPDATE context_items
|
|
57
|
+
SET sequence_number = (
|
|
58
|
+
SELECT COALESCE(MAX(sequence_number), 0) + 1
|
|
59
|
+
FROM context_items
|
|
60
|
+
WHERE session_id = NEW.session_id
|
|
61
|
+
)
|
|
62
|
+
WHERE id = NEW.id;
|
|
63
|
+
END
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
// Create table for tracking deleted items (needed for tests)
|
|
67
|
+
db.exec(`
|
|
68
|
+
CREATE TABLE IF NOT EXISTS deleted_items (
|
|
69
|
+
id TEXT PRIMARY KEY,
|
|
70
|
+
session_id TEXT NOT NULL,
|
|
71
|
+
key TEXT NOT NULL,
|
|
72
|
+
category TEXT,
|
|
73
|
+
channel TEXT,
|
|
74
|
+
sequence_number INTEGER NOT NULL,
|
|
75
|
+
deleted_at TEXT DEFAULT (datetime('now')),
|
|
76
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
77
|
+
)
|
|
78
|
+
`);
|
|
79
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_deleted_items_session ON deleted_items(session_id)');
|
|
80
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_deleted_items_key ON deleted_items(key)');
|
|
81
|
+
})();
|
|
82
|
+
};
|
|
83
|
+
exports.up = up;
|
|
84
|
+
const down = (db) => {
|
|
85
|
+
db.transaction(() => {
|
|
86
|
+
// Drop triggers
|
|
87
|
+
db.exec('DROP TRIGGER IF EXISTS increment_sequence_insert');
|
|
88
|
+
db.exec('DROP TRIGGER IF EXISTS increment_sequence_update');
|
|
89
|
+
// Drop indexes
|
|
90
|
+
db.exec('DROP INDEX IF EXISTS idx_watchers_active');
|
|
91
|
+
db.exec('DROP INDEX IF EXISTS idx_deleted_items_session');
|
|
92
|
+
db.exec('DROP INDEX IF EXISTS idx_deleted_items_key');
|
|
93
|
+
// Drop tables
|
|
94
|
+
db.exec('DROP TABLE IF EXISTS deleted_items');
|
|
95
|
+
// Note: We don't remove the sequence_number and is_active columns as it might break existing data
|
|
96
|
+
})();
|
|
97
|
+
};
|
|
98
|
+
exports.down = down;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.simplifySharingMigration = void 0;
|
|
4
|
+
exports.simplifySharingMigration = {
|
|
5
|
+
version: '2.0.0',
|
|
6
|
+
name: 'Simplify sharing model - remove complex sharing, add is_private flag',
|
|
7
|
+
description: 'Replace broken sharing mechanism with simple is_private flag. Items are shared by default unless marked private.',
|
|
8
|
+
requiresBackup: true,
|
|
9
|
+
dependencies: ['1.2.0'], // Assuming the latest migration from defaults is 1.2.0
|
|
10
|
+
up: `
|
|
11
|
+
-- Add the new is_private column with default false (shared by default)
|
|
12
|
+
ALTER TABLE context_items ADD COLUMN is_private INTEGER DEFAULT 0;
|
|
13
|
+
|
|
14
|
+
-- All existing items become public (is_private = 0)
|
|
15
|
+
-- This makes all previously saved context accessible across sessions
|
|
16
|
+
UPDATE context_items SET is_private = 0;
|
|
17
|
+
|
|
18
|
+
-- Create index for performance on the new column
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_private ON context_items(is_private);
|
|
20
|
+
|
|
21
|
+
-- Drop the old sharing-related columns
|
|
22
|
+
-- SQLite doesn't support DROP COLUMN directly, so we need to recreate the table
|
|
23
|
+
CREATE TABLE context_items_new (
|
|
24
|
+
id TEXT PRIMARY KEY,
|
|
25
|
+
session_id TEXT NOT NULL,
|
|
26
|
+
key TEXT NOT NULL,
|
|
27
|
+
value TEXT NOT NULL,
|
|
28
|
+
category TEXT,
|
|
29
|
+
priority TEXT DEFAULT 'normal',
|
|
30
|
+
metadata TEXT,
|
|
31
|
+
size INTEGER DEFAULT 0,
|
|
32
|
+
is_private INTEGER DEFAULT 0,
|
|
33
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
34
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
35
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
|
|
36
|
+
UNIQUE(session_id, key)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
-- Copy data from old table to new
|
|
40
|
+
INSERT INTO context_items_new (
|
|
41
|
+
id, session_id, key, value, category, priority, metadata, size, is_private, created_at, updated_at
|
|
42
|
+
)
|
|
43
|
+
SELECT
|
|
44
|
+
id, session_id, key, value, category, priority, metadata, size, is_private, created_at, updated_at
|
|
45
|
+
FROM context_items;
|
|
46
|
+
|
|
47
|
+
-- Drop old table and rename new one
|
|
48
|
+
DROP TABLE context_items;
|
|
49
|
+
ALTER TABLE context_items_new RENAME TO context_items;
|
|
50
|
+
|
|
51
|
+
-- Recreate indexes
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_session ON context_items(session_id);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_key ON context_items(key);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_category ON context_items(category);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_priority ON context_items(priority);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_private ON context_items(is_private);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_created ON context_items(created_at);
|
|
58
|
+
|
|
59
|
+
-- Update the updated_at trigger
|
|
60
|
+
DROP TRIGGER IF EXISTS update_context_items_timestamp;
|
|
61
|
+
CREATE TRIGGER update_context_items_timestamp
|
|
62
|
+
AFTER UPDATE ON context_items
|
|
63
|
+
BEGIN
|
|
64
|
+
UPDATE context_items SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
|
65
|
+
END;
|
|
66
|
+
`,
|
|
67
|
+
down: `
|
|
68
|
+
-- Revert to the old schema
|
|
69
|
+
CREATE TABLE context_items_old (
|
|
70
|
+
id TEXT PRIMARY KEY,
|
|
71
|
+
session_id TEXT NOT NULL,
|
|
72
|
+
key TEXT NOT NULL,
|
|
73
|
+
value TEXT NOT NULL,
|
|
74
|
+
category TEXT,
|
|
75
|
+
priority TEXT DEFAULT 'normal',
|
|
76
|
+
metadata TEXT,
|
|
77
|
+
size INTEGER DEFAULT 0,
|
|
78
|
+
shared INTEGER DEFAULT 0,
|
|
79
|
+
shared_with_sessions TEXT,
|
|
80
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
81
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
82
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
|
|
83
|
+
UNIQUE(session_id, key)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
-- Copy data back
|
|
87
|
+
-- Since we made everything public in the up migration, we'll set shared = 1 for all
|
|
88
|
+
INSERT INTO context_items_old (
|
|
89
|
+
id, session_id, key, value, category, priority, metadata, size, shared, created_at, updated_at
|
|
90
|
+
)
|
|
91
|
+
SELECT
|
|
92
|
+
id, session_id, key, value, category, priority, metadata, size,
|
|
93
|
+
1, -- All items were made public, so mark as shared
|
|
94
|
+
created_at, updated_at
|
|
95
|
+
FROM context_items;
|
|
96
|
+
|
|
97
|
+
-- Drop new table and rename old one back
|
|
98
|
+
DROP TABLE context_items;
|
|
99
|
+
ALTER TABLE context_items_old RENAME TO context_items;
|
|
100
|
+
|
|
101
|
+
-- Recreate original indexes
|
|
102
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_session ON context_items(session_id);
|
|
103
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_key ON context_items(key);
|
|
104
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_category ON context_items(category);
|
|
105
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_priority ON context_items(priority);
|
|
106
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_shared ON context_items(shared);
|
|
107
|
+
CREATE INDEX IF NOT EXISTS idx_context_items_created ON context_items(created_at);
|
|
108
|
+
|
|
109
|
+
-- Recreate the updated_at trigger
|
|
110
|
+
DROP TRIGGER IF EXISTS update_context_items_timestamp;
|
|
111
|
+
CREATE TRIGGER update_context_items_timestamp
|
|
112
|
+
AFTER UPDATE ON context_items
|
|
113
|
+
BEGIN
|
|
114
|
+
UPDATE context_items SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
|
115
|
+
END;
|
|
116
|
+
`,
|
|
117
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseRepository = void 0;
|
|
4
|
+
class BaseRepository {
|
|
5
|
+
db;
|
|
6
|
+
dbManager;
|
|
7
|
+
constructor(dbManager) {
|
|
8
|
+
this.dbManager = dbManager;
|
|
9
|
+
this.db = dbManager.getDatabase();
|
|
10
|
+
}
|
|
11
|
+
calculateSize(value) {
|
|
12
|
+
return Buffer.byteLength(value, 'utf8');
|
|
13
|
+
}
|
|
14
|
+
generateId() {
|
|
15
|
+
// Using crypto.randomUUID() for better performance if available
|
|
16
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
17
|
+
return crypto.randomUUID();
|
|
18
|
+
}
|
|
19
|
+
// Fallback to uuid v4 pattern
|
|
20
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
21
|
+
const r = (Math.random() * 16) | 0;
|
|
22
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
23
|
+
return v.toString(16);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
getCurrentTimestamp() {
|
|
27
|
+
return new Date().toISOString();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.BaseRepository = BaseRepository;
|