@vaur94/agz-memory 0.4.0 → 0.4.1
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/ARCHITECTURE.md +3 -3
- package/CHANGELOG.md +19 -0
- package/README.md +11 -11
- package/README.tr.md +11 -11
- package/dist/admin.js +51 -11
- package/dist/core.js +51 -11
- package/dist/server.js +51 -11
- package/dist/types/server.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/docs/backup-restore-runbook.md +15 -15
- package/docs/backup-restore-runbook.tr.md +15 -15
- package/package.json +1 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGZ Memory Architecture
|
|
2
2
|
|
|
3
|
-
This document describes the `0.4.
|
|
3
|
+
This document describes the `0.4.1` runtime and SQLite schema v10.
|
|
4
4
|
|
|
5
5
|
## System Boundaries
|
|
6
6
|
|
|
@@ -20,7 +20,7 @@ OpenCode V2 beta-18743
|
|
|
20
20
|
|
|
21
21
|
SQLite outbox
|
|
22
22
|
-> optional derived retrieval backend
|
|
23
|
-
-> disabled in 0.4.
|
|
23
|
+
-> disabled in 0.4.1; backend = none
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
The MCP adapter owns tool schemas, annotations, and result envelopes. The core
|
|
@@ -128,7 +128,7 @@ original OpenCode context unchanged.
|
|
|
128
128
|
|
|
129
129
|
Semantic providers implement an optional backend contract: project-filtered
|
|
130
130
|
query, idempotent upsert, deterministic delete, full project purge, and health.
|
|
131
|
-
No provider is enabled in `0.4.
|
|
131
|
+
No provider is enabled in `0.4.1`. The SQLite lexical/graph path remains fully
|
|
132
132
|
functional without one.
|
|
133
133
|
|
|
134
134
|
## Derived Index Outbox
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
All notable changes to AGZ Memory are recorded here. The project follows
|
|
4
4
|
[Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.4.1] - 2026-09-01
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Re-read the SQLite schema after acquiring the migration lock so concurrent
|
|
11
|
+
MCP processes do not act on a stale pre-lock version.
|
|
12
|
+
- Skip backup and migration work when another process already completed the
|
|
13
|
+
target schema, preventing redundant backups whose manifest version could
|
|
14
|
+
differ from the copied database.
|
|
15
|
+
- Retry migration-lock handoff when the previous owner releases the lock
|
|
16
|
+
between a competing process's create attempt and ownership check.
|
|
17
|
+
- Reopen the canonical SQLite file after taking the migration lock so a waiter
|
|
18
|
+
cannot continue on a database file replaced during automatic recovery.
|
|
19
|
+
- Verify versioned backup manifests against the schema stored in their SQLite
|
|
20
|
+
database instead of trusting the caller-provided source label.
|
|
21
|
+
- Added a six-process regression test that requires exactly one v9 backup, a
|
|
22
|
+
v9 backup database, and one healthy v10 canonical database.
|
|
23
|
+
|
|
6
24
|
## [0.4.0] - 2026-09-01
|
|
7
25
|
|
|
8
26
|
### Added
|
|
@@ -56,5 +74,6 @@ All notable changes to AGZ Memory are recorded here. The project follows
|
|
|
56
74
|
- Preserved the nine-tool MCP interface and project-isolation guarantees while
|
|
57
75
|
establishing the AGZ Memory public packages and repository.
|
|
58
76
|
|
|
77
|
+
[0.4.1]: https://github.com/ugur-murat-alt/agz-memory/releases/tag/v0.4.1
|
|
59
78
|
[0.4.0]: https://github.com/ugur-murat-alt/agz-memory/releases/tag/v0.4.0
|
|
60
79
|
[0.4.0-beta.1]: https://github.com/ugur-murat-alt/agz-memory/releases/tag/v0.4.0-beta.1
|
package/README.md
CHANGED
|
@@ -32,8 +32,8 @@ binding and rollout mode are configured.
|
|
|
32
32
|
|
|
33
33
|
| Component | Supported version |
|
|
34
34
|
|---|---|
|
|
35
|
-
| Core and MCP | `0.4.
|
|
36
|
-
| OpenCode plugin | `0.4.
|
|
35
|
+
| Core and MCP | `0.4.1` |
|
|
36
|
+
| OpenCode plugin | `0.4.1` |
|
|
37
37
|
| OpenCode V2 | `0.0.0-beta-18743` |
|
|
38
38
|
| `@opencode-ai/plugin` | `0.0.0-beta-18743` |
|
|
39
39
|
| Bun | `>=1.3.14` |
|
|
@@ -47,7 +47,7 @@ itself unless the running OpenCode version exactly matches the supported beta.
|
|
|
47
47
|
Run the server directly:
|
|
48
48
|
|
|
49
49
|
```sh
|
|
50
|
-
bunx @vaur94/agz-memory@0.4.
|
|
50
|
+
bunx @vaur94/agz-memory@0.4.1
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
Or register it in OpenCode V2 under `mcp.servers`:
|
|
@@ -58,7 +58,7 @@ Or register it in OpenCode V2 under `mcp.servers`:
|
|
|
58
58
|
"servers": {
|
|
59
59
|
"agz-memory": {
|
|
60
60
|
"type": "local",
|
|
61
|
-
"command": ["bunx", "@vaur94/agz-memory@0.4.
|
|
61
|
+
"command": ["bunx", "@vaur94/agz-memory@0.4.1"],
|
|
62
62
|
"environment": {
|
|
63
63
|
"OPENCODE_MEMORY_DATABASE_PATH": "{env:OPENCODE_MEMORY_DATABASE_PATH}"
|
|
64
64
|
},
|
|
@@ -113,7 +113,7 @@ options:
|
|
|
113
113
|
{
|
|
114
114
|
"plugins": [
|
|
115
115
|
{
|
|
116
|
-
"package": "@vaur94/agz-memory-plugin@0.4.
|
|
116
|
+
"package": "@vaur94/agz-memory-plugin@0.4.1",
|
|
117
117
|
"options": {
|
|
118
118
|
"mode": "off",
|
|
119
119
|
"autoCreateProjects": false,
|
|
@@ -186,11 +186,11 @@ quality, and latency gates.
|
|
|
186
186
|
The admin CLI reads the same `OPENCODE_MEMORY_DATABASE_PATH`:
|
|
187
187
|
|
|
188
188
|
```sh
|
|
189
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
190
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
191
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
192
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
193
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
189
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
190
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup
|
|
191
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin upgrade --to 10
|
|
192
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin capture status
|
|
193
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin outbox status
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
Upgrades take an exclusive migration lock and create a verified backup before
|
|
@@ -199,7 +199,7 @@ restore. Restore and backup deletion use dry-run output plus explicit
|
|
|
199
199
|
confirmation values; never guess them.
|
|
200
200
|
|
|
201
201
|
Use [the backup and restore runbook](docs/backup-restore-runbook.md) for a full
|
|
202
|
-
rehearsal. Final `0.4.
|
|
202
|
+
rehearsal. Final `0.4.1` backup manifests use `agz-memory-backup/1`; prerelease
|
|
203
203
|
manifests must be handled by the prerelease that created them.
|
|
204
204
|
|
|
205
205
|
## Security Model
|
package/README.tr.md
CHANGED
|
@@ -32,8 +32,8 @@ bağlam eklemez.
|
|
|
32
32
|
|
|
33
33
|
| Bileşen | Desteklenen sürüm |
|
|
34
34
|
|---|---|
|
|
35
|
-
| Çekirdek ve MCP | `0.4.
|
|
36
|
-
| OpenCode eklentisi | `0.4.
|
|
35
|
+
| Çekirdek ve MCP | `0.4.1` |
|
|
36
|
+
| OpenCode eklentisi | `0.4.1` |
|
|
37
37
|
| OpenCode V2 | `0.0.0-beta-18743` |
|
|
38
38
|
| `@opencode-ai/plugin` | `0.0.0-beta-18743` |
|
|
39
39
|
| Bun | `>=1.3.14` |
|
|
@@ -47,7 +47,7 @@ MCP sunucusu bir OpenCode beta sürümüne bağlı değildir. İsteğe bağlı e
|
|
|
47
47
|
Sunucuyu doğrudan çalıştırın:
|
|
48
48
|
|
|
49
49
|
```sh
|
|
50
|
-
bunx @vaur94/agz-memory@0.4.
|
|
50
|
+
bunx @vaur94/agz-memory@0.4.1
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
Ya da OpenCode V2 içinde `mcp.servers` altına kaydedin:
|
|
@@ -58,7 +58,7 @@ Ya da OpenCode V2 içinde `mcp.servers` altına kaydedin:
|
|
|
58
58
|
"servers": {
|
|
59
59
|
"agz-memory": {
|
|
60
60
|
"type": "local",
|
|
61
|
-
"command": ["bunx", "@vaur94/agz-memory@0.4.
|
|
61
|
+
"command": ["bunx", "@vaur94/agz-memory@0.4.1"],
|
|
62
62
|
"environment": {
|
|
63
63
|
"OPENCODE_MEMORY_DATABASE_PATH": "{env:OPENCODE_MEMORY_DATABASE_PATH}"
|
|
64
64
|
},
|
|
@@ -114,7 +114,7 @@ ayarlarla ekleyin:
|
|
|
114
114
|
{
|
|
115
115
|
"plugins": [
|
|
116
116
|
{
|
|
117
|
-
"package": "@vaur94/agz-memory-plugin@0.4.
|
|
117
|
+
"package": "@vaur94/agz-memory-plugin@0.4.1",
|
|
118
118
|
"options": {
|
|
119
119
|
"mode": "off",
|
|
120
120
|
"autoCreateProjects": false,
|
|
@@ -187,11 +187,11 @@ kapılarından geçene kadar `semanticBackend` değeri `none` olmalıdır.
|
|
|
187
187
|
Yönetim aracı aynı `OPENCODE_MEMORY_DATABASE_PATH` değerini okur:
|
|
188
188
|
|
|
189
189
|
```sh
|
|
190
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
191
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
192
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
193
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
194
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
190
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
191
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup
|
|
192
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin upgrade --to 10
|
|
193
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin capture status
|
|
194
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin outbox status
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
Yükseltmeler özel bir geçiş kilidi alır ve veritabanını değiştirmeden önce
|
|
@@ -200,7 +200,7 @@ dener. Geri yükleme ve yedek silme işlemleri önce deneme çıktısı, sonra a
|
|
|
200
200
|
onay değerleri ister; bu değerleri tahmin etmeyin.
|
|
201
201
|
|
|
202
202
|
Tam prova için [yedekleme ve geri yükleme runbook'unu](docs/backup-restore-runbook.tr.md)
|
|
203
|
-
kullanın. Final `0.4.
|
|
203
|
+
kullanın. Final `0.4.1` yedek manifestleri `agz-memory-backup/1` kullanır;
|
|
204
204
|
ön sürüm manifestleri onları oluşturan ön sürümle işlenmelidir.
|
|
205
205
|
|
|
206
206
|
## Güvenlik Modeli
|
package/dist/admin.js
CHANGED
|
@@ -107,6 +107,9 @@ function hasTable(db, table) {
|
|
|
107
107
|
var BACKUP_FORMAT = "agz-memory-backup/1";
|
|
108
108
|
function createVerifiedBackup(db, databasePath, sourceSchema, targetSchema, productVersion) {
|
|
109
109
|
const sourceHealth = assertHealthyDatabase(db);
|
|
110
|
+
if (sourceHealth.schemaVersion !== undefined && sourceHealth.schemaVersion !== sourceSchema) {
|
|
111
|
+
throw new Error(`backup source schema v${sourceSchema} does not match database schema v${sourceHealth.schemaVersion}`);
|
|
112
|
+
}
|
|
110
113
|
const checkpoint = db.query("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
111
114
|
if (checkpoint.busy !== 0)
|
|
112
115
|
throw new Error("database WAL checkpoint is busy");
|
|
@@ -134,6 +137,9 @@ function createVerifiedBackup(db, databasePath, sourceSchema, targetSchema, prod
|
|
|
134
137
|
if (JSON.stringify(backupHealth.counts) !== JSON.stringify(sourceHealth.counts)) {
|
|
135
138
|
throw new Error("backup row counts differ from source database");
|
|
136
139
|
}
|
|
140
|
+
if (backupHealth.schemaVersion !== undefined && backupHealth.schemaVersion !== sourceSchema) {
|
|
141
|
+
throw new Error("backup schema does not match source schema");
|
|
142
|
+
}
|
|
137
143
|
const bytes = readFileSync(temporaryDatabasePath);
|
|
138
144
|
const manifest = {
|
|
139
145
|
format: BACKUP_FORMAT,
|
|
@@ -206,6 +212,9 @@ function verifyBackupManifest(manifestPath) {
|
|
|
206
212
|
if (JSON.stringify(health.counts) !== JSON.stringify(manifest.counts)) {
|
|
207
213
|
throw new Error("backup manifest row counts do not match");
|
|
208
214
|
}
|
|
215
|
+
if (health.schemaVersion !== undefined && health.schemaVersion !== manifest.sourceSchema) {
|
|
216
|
+
throw new Error("backup manifest source schema does not match database");
|
|
217
|
+
}
|
|
209
218
|
} finally {
|
|
210
219
|
db.close();
|
|
211
220
|
}
|
|
@@ -391,7 +400,7 @@ function acquireMigrationLock(databasePath, targetSchema, timeoutMs = 30000) {
|
|
|
391
400
|
rmSync2(path, { recursive: true, force: true });
|
|
392
401
|
throw error;
|
|
393
402
|
}
|
|
394
|
-
if (!
|
|
403
|
+
if (!isAlreadyExistsError(error))
|
|
395
404
|
throw error;
|
|
396
405
|
if (Date.now() >= deadline) {
|
|
397
406
|
const current = readMigrationLockOwner(path);
|
|
@@ -464,6 +473,9 @@ function processIsAlive(pid) {
|
|
|
464
473
|
return false;
|
|
465
474
|
}
|
|
466
475
|
}
|
|
476
|
+
function isAlreadyExistsError(error) {
|
|
477
|
+
return Boolean(error && typeof error === "object" && "code" in error && String(error.code) === "EEXIST");
|
|
478
|
+
}
|
|
467
479
|
|
|
468
480
|
// src/db/migrations/v009.ts
|
|
469
481
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
@@ -866,7 +878,7 @@ function migrateV9ToV10(db) {
|
|
|
866
878
|
}
|
|
867
879
|
|
|
868
880
|
// src/version.ts
|
|
869
|
-
var PRODUCT_VERSION = "0.4.
|
|
881
|
+
var PRODUCT_VERSION = "0.4.1";
|
|
870
882
|
|
|
871
883
|
// src/db.ts
|
|
872
884
|
var DDL = `
|
|
@@ -909,13 +921,11 @@ CREATE INDEX IF NOT EXISTS note_edges_target_idx ON note_edges(project_id, targe
|
|
|
909
921
|
CREATE TABLE IF NOT EXISTS schema_state (version INTEGER PRIMARY KEY);
|
|
910
922
|
`;
|
|
911
923
|
function openMemoryDatabase(path) {
|
|
912
|
-
|
|
913
|
-
|
|
924
|
+
let db = openDatabase(path);
|
|
925
|
+
let dbOpen = true;
|
|
914
926
|
let lock;
|
|
915
927
|
let backup;
|
|
916
928
|
try {
|
|
917
|
-
db.exec("PRAGMA busy_timeout=5000");
|
|
918
|
-
db.exec("PRAGMA journal_mode=WAL");
|
|
919
929
|
const existingVersion = getSchemaVersion(db);
|
|
920
930
|
if (existingVersion && existingVersion.version > SCHEMA_VERSION) {
|
|
921
931
|
throw new Error(`database schema v${existingVersion.version} is newer than supported v${SCHEMA_VERSION}`);
|
|
@@ -928,14 +938,31 @@ function openMemoryDatabase(path) {
|
|
|
928
938
|
return { db, close: () => db.close() };
|
|
929
939
|
}
|
|
930
940
|
if ((existingVersion?.version ?? 0) < SCHEMA_VERSION) {
|
|
941
|
+
db.close();
|
|
942
|
+
dbOpen = false;
|
|
931
943
|
lock = acquireMigrationLock(path, SCHEMA_VERSION);
|
|
932
|
-
|
|
944
|
+
db = openDatabase(path);
|
|
945
|
+
dbOpen = true;
|
|
946
|
+
const migrationVersion = getSchemaVersion(db);
|
|
947
|
+
if (migrationVersion && migrationVersion.version > SCHEMA_VERSION) {
|
|
948
|
+
throw new Error(`database schema v${migrationVersion.version} is newer than supported v${SCHEMA_VERSION}`);
|
|
949
|
+
}
|
|
950
|
+
if ((migrationVersion?.version ?? 0) === SCHEMA_VERSION) {
|
|
951
|
+
lock.release();
|
|
952
|
+
lock = undefined;
|
|
953
|
+
db.exec("PRAGMA foreign_keys=ON");
|
|
954
|
+
db.exec(SCHEMA_TABLES);
|
|
955
|
+
db.exec(FTS_V9);
|
|
956
|
+
assertHealthyDatabase(db);
|
|
957
|
+
return { db, close: () => db.close() };
|
|
958
|
+
}
|
|
959
|
+
backup = createVerifiedBackup(db, path, migrationVersion?.version ?? 2, SCHEMA_VERSION, PRODUCT_VERSION);
|
|
933
960
|
db.exec("PRAGMA foreign_keys=OFF");
|
|
934
|
-
if (!
|
|
961
|
+
if (!migrationVersion && hasLegacyV2(db)) {
|
|
935
962
|
db.exec(DDL);
|
|
936
963
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
937
964
|
migrateFromV2(db, path);
|
|
938
|
-
} else if (!
|
|
965
|
+
} else if (!migrationVersion) {
|
|
939
966
|
db.exec(DDL);
|
|
940
967
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
941
968
|
db.transaction(() => {
|
|
@@ -943,7 +970,7 @@ function openMemoryDatabase(path) {
|
|
|
943
970
|
db.query("DELETE FROM schema_state").run();
|
|
944
971
|
db.query("INSERT INTO schema_state (version) VALUES (8)").run();
|
|
945
972
|
})();
|
|
946
|
-
} else if (
|
|
973
|
+
} else if (migrationVersion.version < 8) {
|
|
947
974
|
db.exec(DDL);
|
|
948
975
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
949
976
|
migrateToV8(db);
|
|
@@ -972,7 +999,8 @@ function openMemoryDatabase(path) {
|
|
|
972
999
|
db.exec("PRAGMA foreign_keys=ON");
|
|
973
1000
|
return { db, close: () => db.close() };
|
|
974
1001
|
} catch (error) {
|
|
975
|
-
|
|
1002
|
+
if (dbOpen)
|
|
1003
|
+
db.close();
|
|
976
1004
|
if (backup) {
|
|
977
1005
|
try {
|
|
978
1006
|
restoreVerifiedBackup(backup.manifestPath, path, "RESTORE_DATABASE_FROM_VERIFIED_BACKUP");
|
|
@@ -985,6 +1013,18 @@ function openMemoryDatabase(path) {
|
|
|
985
1013
|
lock?.release();
|
|
986
1014
|
}
|
|
987
1015
|
}
|
|
1016
|
+
function openDatabase(path) {
|
|
1017
|
+
const db = new Database2(path, { create: true });
|
|
1018
|
+
try {
|
|
1019
|
+
chmodSync2(path, 384);
|
|
1020
|
+
db.exec("PRAGMA busy_timeout=5000");
|
|
1021
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
1022
|
+
return db;
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
db.close();
|
|
1025
|
+
throw error;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
988
1028
|
function getSchemaVersion(db) {
|
|
989
1029
|
if (!hasTable2(db, "schema_state"))
|
|
990
1030
|
return;
|
package/dist/core.js
CHANGED
|
@@ -95,6 +95,9 @@ function hasTable(db, table) {
|
|
|
95
95
|
var BACKUP_FORMAT = "agz-memory-backup/1";
|
|
96
96
|
function createVerifiedBackup(db, databasePath, sourceSchema, targetSchema, productVersion) {
|
|
97
97
|
const sourceHealth = assertHealthyDatabase(db);
|
|
98
|
+
if (sourceHealth.schemaVersion !== undefined && sourceHealth.schemaVersion !== sourceSchema) {
|
|
99
|
+
throw new Error(`backup source schema v${sourceSchema} does not match database schema v${sourceHealth.schemaVersion}`);
|
|
100
|
+
}
|
|
98
101
|
const checkpoint = db.query("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
99
102
|
if (checkpoint.busy !== 0)
|
|
100
103
|
throw new Error("database WAL checkpoint is busy");
|
|
@@ -122,6 +125,9 @@ function createVerifiedBackup(db, databasePath, sourceSchema, targetSchema, prod
|
|
|
122
125
|
if (JSON.stringify(backupHealth.counts) !== JSON.stringify(sourceHealth.counts)) {
|
|
123
126
|
throw new Error("backup row counts differ from source database");
|
|
124
127
|
}
|
|
128
|
+
if (backupHealth.schemaVersion !== undefined && backupHealth.schemaVersion !== sourceSchema) {
|
|
129
|
+
throw new Error("backup schema does not match source schema");
|
|
130
|
+
}
|
|
125
131
|
const bytes = readFileSync(temporaryDatabasePath);
|
|
126
132
|
const manifest = {
|
|
127
133
|
format: BACKUP_FORMAT,
|
|
@@ -194,6 +200,9 @@ function verifyBackupManifest(manifestPath) {
|
|
|
194
200
|
if (JSON.stringify(health.counts) !== JSON.stringify(manifest.counts)) {
|
|
195
201
|
throw new Error("backup manifest row counts do not match");
|
|
196
202
|
}
|
|
203
|
+
if (health.schemaVersion !== undefined && health.schemaVersion !== manifest.sourceSchema) {
|
|
204
|
+
throw new Error("backup manifest source schema does not match database");
|
|
205
|
+
}
|
|
197
206
|
} finally {
|
|
198
207
|
db.close();
|
|
199
208
|
}
|
|
@@ -379,7 +388,7 @@ function acquireMigrationLock(databasePath, targetSchema, timeoutMs = 30000) {
|
|
|
379
388
|
rmSync2(path, { recursive: true, force: true });
|
|
380
389
|
throw error;
|
|
381
390
|
}
|
|
382
|
-
if (!
|
|
391
|
+
if (!isAlreadyExistsError(error))
|
|
383
392
|
throw error;
|
|
384
393
|
if (Date.now() >= deadline) {
|
|
385
394
|
const current = readMigrationLockOwner(path);
|
|
@@ -422,6 +431,9 @@ function processStartMarker(pid) {
|
|
|
422
431
|
return;
|
|
423
432
|
}
|
|
424
433
|
}
|
|
434
|
+
function isAlreadyExistsError(error) {
|
|
435
|
+
return Boolean(error && typeof error === "object" && "code" in error && String(error.code) === "EEXIST");
|
|
436
|
+
}
|
|
425
437
|
|
|
426
438
|
// src/db/migrations/v009.ts
|
|
427
439
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
@@ -824,7 +836,7 @@ function migrateV9ToV10(db) {
|
|
|
824
836
|
}
|
|
825
837
|
|
|
826
838
|
// src/version.ts
|
|
827
|
-
var PRODUCT_VERSION = "0.4.
|
|
839
|
+
var PRODUCT_VERSION = "0.4.1";
|
|
828
840
|
|
|
829
841
|
// src/db.ts
|
|
830
842
|
var DDL = `
|
|
@@ -867,13 +879,11 @@ CREATE INDEX IF NOT EXISTS note_edges_target_idx ON note_edges(project_id, targe
|
|
|
867
879
|
CREATE TABLE IF NOT EXISTS schema_state (version INTEGER PRIMARY KEY);
|
|
868
880
|
`;
|
|
869
881
|
function openMemoryDatabase(path) {
|
|
870
|
-
|
|
871
|
-
|
|
882
|
+
let db = openDatabase(path);
|
|
883
|
+
let dbOpen = true;
|
|
872
884
|
let lock;
|
|
873
885
|
let backup;
|
|
874
886
|
try {
|
|
875
|
-
db.exec("PRAGMA busy_timeout=5000");
|
|
876
|
-
db.exec("PRAGMA journal_mode=WAL");
|
|
877
887
|
const existingVersion = getSchemaVersion(db);
|
|
878
888
|
if (existingVersion && existingVersion.version > SCHEMA_VERSION) {
|
|
879
889
|
throw new Error(`database schema v${existingVersion.version} is newer than supported v${SCHEMA_VERSION}`);
|
|
@@ -886,14 +896,31 @@ function openMemoryDatabase(path) {
|
|
|
886
896
|
return { db, close: () => db.close() };
|
|
887
897
|
}
|
|
888
898
|
if ((existingVersion?.version ?? 0) < SCHEMA_VERSION) {
|
|
899
|
+
db.close();
|
|
900
|
+
dbOpen = false;
|
|
889
901
|
lock = acquireMigrationLock(path, SCHEMA_VERSION);
|
|
890
|
-
|
|
902
|
+
db = openDatabase(path);
|
|
903
|
+
dbOpen = true;
|
|
904
|
+
const migrationVersion = getSchemaVersion(db);
|
|
905
|
+
if (migrationVersion && migrationVersion.version > SCHEMA_VERSION) {
|
|
906
|
+
throw new Error(`database schema v${migrationVersion.version} is newer than supported v${SCHEMA_VERSION}`);
|
|
907
|
+
}
|
|
908
|
+
if ((migrationVersion?.version ?? 0) === SCHEMA_VERSION) {
|
|
909
|
+
lock.release();
|
|
910
|
+
lock = undefined;
|
|
911
|
+
db.exec("PRAGMA foreign_keys=ON");
|
|
912
|
+
db.exec(SCHEMA_TABLES);
|
|
913
|
+
db.exec(FTS_V9);
|
|
914
|
+
assertHealthyDatabase(db);
|
|
915
|
+
return { db, close: () => db.close() };
|
|
916
|
+
}
|
|
917
|
+
backup = createVerifiedBackup(db, path, migrationVersion?.version ?? 2, SCHEMA_VERSION, PRODUCT_VERSION);
|
|
891
918
|
db.exec("PRAGMA foreign_keys=OFF");
|
|
892
|
-
if (!
|
|
919
|
+
if (!migrationVersion && hasLegacyV2(db)) {
|
|
893
920
|
db.exec(DDL);
|
|
894
921
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
895
922
|
migrateFromV2(db, path);
|
|
896
|
-
} else if (!
|
|
923
|
+
} else if (!migrationVersion) {
|
|
897
924
|
db.exec(DDL);
|
|
898
925
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
899
926
|
db.transaction(() => {
|
|
@@ -901,7 +928,7 @@ function openMemoryDatabase(path) {
|
|
|
901
928
|
db.query("DELETE FROM schema_state").run();
|
|
902
929
|
db.query("INSERT INTO schema_state (version) VALUES (8)").run();
|
|
903
930
|
})();
|
|
904
|
-
} else if (
|
|
931
|
+
} else if (migrationVersion.version < 8) {
|
|
905
932
|
db.exec(DDL);
|
|
906
933
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
907
934
|
migrateToV8(db);
|
|
@@ -930,7 +957,8 @@ function openMemoryDatabase(path) {
|
|
|
930
957
|
db.exec("PRAGMA foreign_keys=ON");
|
|
931
958
|
return { db, close: () => db.close() };
|
|
932
959
|
} catch (error) {
|
|
933
|
-
|
|
960
|
+
if (dbOpen)
|
|
961
|
+
db.close();
|
|
934
962
|
if (backup) {
|
|
935
963
|
try {
|
|
936
964
|
restoreVerifiedBackup(backup.manifestPath, path, "RESTORE_DATABASE_FROM_VERIFIED_BACKUP");
|
|
@@ -943,6 +971,18 @@ function openMemoryDatabase(path) {
|
|
|
943
971
|
lock?.release();
|
|
944
972
|
}
|
|
945
973
|
}
|
|
974
|
+
function openDatabase(path) {
|
|
975
|
+
const db = new Database2(path, { create: true });
|
|
976
|
+
try {
|
|
977
|
+
chmodSync2(path, 384);
|
|
978
|
+
db.exec("PRAGMA busy_timeout=5000");
|
|
979
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
980
|
+
return db;
|
|
981
|
+
} catch (error) {
|
|
982
|
+
db.close();
|
|
983
|
+
throw error;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
946
986
|
function getSchemaVersion(db) {
|
|
947
987
|
if (!hasTable2(db, "schema_state"))
|
|
948
988
|
return;
|
package/dist/server.js
CHANGED
|
@@ -110,6 +110,9 @@ function hasTable(db, table) {
|
|
|
110
110
|
var BACKUP_FORMAT = "agz-memory-backup/1";
|
|
111
111
|
function createVerifiedBackup(db, databasePath, sourceSchema, targetSchema, productVersion) {
|
|
112
112
|
const sourceHealth = assertHealthyDatabase(db);
|
|
113
|
+
if (sourceHealth.schemaVersion !== undefined && sourceHealth.schemaVersion !== sourceSchema) {
|
|
114
|
+
throw new Error(`backup source schema v${sourceSchema} does not match database schema v${sourceHealth.schemaVersion}`);
|
|
115
|
+
}
|
|
113
116
|
const checkpoint = db.query("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
114
117
|
if (checkpoint.busy !== 0)
|
|
115
118
|
throw new Error("database WAL checkpoint is busy");
|
|
@@ -137,6 +140,9 @@ function createVerifiedBackup(db, databasePath, sourceSchema, targetSchema, prod
|
|
|
137
140
|
if (JSON.stringify(backupHealth.counts) !== JSON.stringify(sourceHealth.counts)) {
|
|
138
141
|
throw new Error("backup row counts differ from source database");
|
|
139
142
|
}
|
|
143
|
+
if (backupHealth.schemaVersion !== undefined && backupHealth.schemaVersion !== sourceSchema) {
|
|
144
|
+
throw new Error("backup schema does not match source schema");
|
|
145
|
+
}
|
|
140
146
|
const bytes = readFileSync(temporaryDatabasePath);
|
|
141
147
|
const manifest = {
|
|
142
148
|
format: BACKUP_FORMAT,
|
|
@@ -209,6 +215,9 @@ function verifyBackupManifest(manifestPath) {
|
|
|
209
215
|
if (JSON.stringify(health.counts) !== JSON.stringify(manifest.counts)) {
|
|
210
216
|
throw new Error("backup manifest row counts do not match");
|
|
211
217
|
}
|
|
218
|
+
if (health.schemaVersion !== undefined && health.schemaVersion !== manifest.sourceSchema) {
|
|
219
|
+
throw new Error("backup manifest source schema does not match database");
|
|
220
|
+
}
|
|
212
221
|
} finally {
|
|
213
222
|
db.close();
|
|
214
223
|
}
|
|
@@ -394,7 +403,7 @@ function acquireMigrationLock(databasePath, targetSchema, timeoutMs = 30000) {
|
|
|
394
403
|
rmSync2(path, { recursive: true, force: true });
|
|
395
404
|
throw error;
|
|
396
405
|
}
|
|
397
|
-
if (!
|
|
406
|
+
if (!isAlreadyExistsError(error))
|
|
398
407
|
throw error;
|
|
399
408
|
if (Date.now() >= deadline) {
|
|
400
409
|
const current = readMigrationLockOwner(path);
|
|
@@ -437,6 +446,9 @@ function processStartMarker(pid) {
|
|
|
437
446
|
return;
|
|
438
447
|
}
|
|
439
448
|
}
|
|
449
|
+
function isAlreadyExistsError(error) {
|
|
450
|
+
return Boolean(error && typeof error === "object" && "code" in error && String(error.code) === "EEXIST");
|
|
451
|
+
}
|
|
440
452
|
|
|
441
453
|
// src/db/migrations/v009.ts
|
|
442
454
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
@@ -839,7 +851,7 @@ function migrateV9ToV10(db) {
|
|
|
839
851
|
}
|
|
840
852
|
|
|
841
853
|
// src/version.ts
|
|
842
|
-
var PRODUCT_VERSION = "0.4.
|
|
854
|
+
var PRODUCT_VERSION = "0.4.1";
|
|
843
855
|
|
|
844
856
|
// src/db.ts
|
|
845
857
|
var DDL = `
|
|
@@ -882,13 +894,11 @@ CREATE INDEX IF NOT EXISTS note_edges_target_idx ON note_edges(project_id, targe
|
|
|
882
894
|
CREATE TABLE IF NOT EXISTS schema_state (version INTEGER PRIMARY KEY);
|
|
883
895
|
`;
|
|
884
896
|
function openMemoryDatabase(path) {
|
|
885
|
-
|
|
886
|
-
|
|
897
|
+
let db = openDatabase(path);
|
|
898
|
+
let dbOpen = true;
|
|
887
899
|
let lock;
|
|
888
900
|
let backup;
|
|
889
901
|
try {
|
|
890
|
-
db.exec("PRAGMA busy_timeout=5000");
|
|
891
|
-
db.exec("PRAGMA journal_mode=WAL");
|
|
892
902
|
const existingVersion = getSchemaVersion(db);
|
|
893
903
|
if (existingVersion && existingVersion.version > SCHEMA_VERSION) {
|
|
894
904
|
throw new Error(`database schema v${existingVersion.version} is newer than supported v${SCHEMA_VERSION}`);
|
|
@@ -901,14 +911,31 @@ function openMemoryDatabase(path) {
|
|
|
901
911
|
return { db, close: () => db.close() };
|
|
902
912
|
}
|
|
903
913
|
if ((existingVersion?.version ?? 0) < SCHEMA_VERSION) {
|
|
914
|
+
db.close();
|
|
915
|
+
dbOpen = false;
|
|
904
916
|
lock = acquireMigrationLock(path, SCHEMA_VERSION);
|
|
905
|
-
|
|
917
|
+
db = openDatabase(path);
|
|
918
|
+
dbOpen = true;
|
|
919
|
+
const migrationVersion = getSchemaVersion(db);
|
|
920
|
+
if (migrationVersion && migrationVersion.version > SCHEMA_VERSION) {
|
|
921
|
+
throw new Error(`database schema v${migrationVersion.version} is newer than supported v${SCHEMA_VERSION}`);
|
|
922
|
+
}
|
|
923
|
+
if ((migrationVersion?.version ?? 0) === SCHEMA_VERSION) {
|
|
924
|
+
lock.release();
|
|
925
|
+
lock = undefined;
|
|
926
|
+
db.exec("PRAGMA foreign_keys=ON");
|
|
927
|
+
db.exec(SCHEMA_TABLES);
|
|
928
|
+
db.exec(FTS_V9);
|
|
929
|
+
assertHealthyDatabase(db);
|
|
930
|
+
return { db, close: () => db.close() };
|
|
931
|
+
}
|
|
932
|
+
backup = createVerifiedBackup(db, path, migrationVersion?.version ?? 2, SCHEMA_VERSION, PRODUCT_VERSION);
|
|
906
933
|
db.exec("PRAGMA foreign_keys=OFF");
|
|
907
|
-
if (!
|
|
934
|
+
if (!migrationVersion && hasLegacyV2(db)) {
|
|
908
935
|
db.exec(DDL);
|
|
909
936
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
910
937
|
migrateFromV2(db, path);
|
|
911
|
-
} else if (!
|
|
938
|
+
} else if (!migrationVersion) {
|
|
912
939
|
db.exec(DDL);
|
|
913
940
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
914
941
|
db.transaction(() => {
|
|
@@ -916,7 +943,7 @@ function openMemoryDatabase(path) {
|
|
|
916
943
|
db.query("DELETE FROM schema_state").run();
|
|
917
944
|
db.query("INSERT INTO schema_state (version) VALUES (8)").run();
|
|
918
945
|
})();
|
|
919
|
-
} else if (
|
|
946
|
+
} else if (migrationVersion.version < 8) {
|
|
920
947
|
db.exec(DDL);
|
|
921
948
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
922
949
|
migrateToV8(db);
|
|
@@ -945,7 +972,8 @@ function openMemoryDatabase(path) {
|
|
|
945
972
|
db.exec("PRAGMA foreign_keys=ON");
|
|
946
973
|
return { db, close: () => db.close() };
|
|
947
974
|
} catch (error) {
|
|
948
|
-
|
|
975
|
+
if (dbOpen)
|
|
976
|
+
db.close();
|
|
949
977
|
if (backup) {
|
|
950
978
|
try {
|
|
951
979
|
restoreVerifiedBackup(backup.manifestPath, path, "RESTORE_DATABASE_FROM_VERIFIED_BACKUP");
|
|
@@ -958,6 +986,18 @@ function openMemoryDatabase(path) {
|
|
|
958
986
|
lock?.release();
|
|
959
987
|
}
|
|
960
988
|
}
|
|
989
|
+
function openDatabase(path) {
|
|
990
|
+
const db = new Database2(path, { create: true });
|
|
991
|
+
try {
|
|
992
|
+
chmodSync2(path, 384);
|
|
993
|
+
db.exec("PRAGMA busy_timeout=5000");
|
|
994
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
995
|
+
return db;
|
|
996
|
+
} catch (error) {
|
|
997
|
+
db.close();
|
|
998
|
+
throw error;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
961
1001
|
function getSchemaVersion(db) {
|
|
962
1002
|
if (!hasTable2(db, "schema_state"))
|
|
963
1003
|
return;
|
package/dist/types/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
2
|
import type { MemoryStore } from "./store";
|
|
3
3
|
export declare const SERVER_NAME = "agz-memory";
|
|
4
|
-
export declare const SERVER_VERSION: "0.4.
|
|
4
|
+
export declare const SERVER_VERSION: "0.4.1";
|
|
5
5
|
export declare function createMemoryServer(store: MemoryStore): McpServer;
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PRODUCT_VERSION: "0.4.
|
|
1
|
+
export declare const PRODUCT_VERSION: "0.4.1";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [Türkçe](backup-restore-runbook.tr.md)
|
|
4
4
|
|
|
5
|
-
This runbook applies to `@vaur94/agz-memory@0.4.
|
|
5
|
+
This runbook applies to `@vaur94/agz-memory@0.4.1` and SQLite schema v10.
|
|
6
6
|
|
|
7
7
|
## Preconditions
|
|
8
8
|
|
|
@@ -25,16 +25,16 @@ Do not proceed with a guessed or empty path.
|
|
|
25
25
|
Run a read-only health report first:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
28
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
`ok` must be `true`. Record `schemaVersion`, row counts, and invariant counts.
|
|
32
32
|
Then create a standalone verified backup and upgrade:
|
|
33
33
|
|
|
34
34
|
```sh
|
|
35
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
36
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
37
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
35
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup
|
|
36
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin upgrade --to 10
|
|
37
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
The upgrade itself creates another verified pre-migration backup when the
|
|
@@ -54,16 +54,16 @@ The manifest format is `agz-memory-backup/1`. `agz-memory-admin restore` verifie
|
|
|
54
54
|
that the manifest and database are regular files in the same backup directory,
|
|
55
55
|
then checks size, SHA-256, SQLite integrity, foreign keys, and row counts.
|
|
56
56
|
|
|
57
|
-
Final `0.4.
|
|
57
|
+
Final `0.4.1` does not accept prerelease manifest formats. Use the originating
|
|
58
58
|
prerelease to restore such a backup, run its doctor check, and only then upgrade
|
|
59
|
-
that restored database with `0.4.
|
|
59
|
+
that restored database with `0.4.1`.
|
|
60
60
|
|
|
61
61
|
## Restore Rehearsal
|
|
62
62
|
|
|
63
63
|
Keep all writers stopped. First request a dry run by omitting confirmation:
|
|
64
64
|
|
|
65
65
|
```sh
|
|
66
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
66
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
|
|
67
67
|
"$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json"
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -71,7 +71,7 @@ Compare `targetPath`, `sourceSchema`, `targetSchema`, row counts, size, and
|
|
|
71
71
|
SHA-256 with the recorded backup. Then use the exact manifest hash:
|
|
72
72
|
|
|
73
73
|
```sh
|
|
74
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
74
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
|
|
75
75
|
"$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json" \
|
|
76
76
|
--sha256 <manifest-database-sha256> \
|
|
77
77
|
--confirm RESTORE_DATABASE_FROM_VERIFIED_BACKUP
|
|
@@ -84,9 +84,9 @@ database passes all checks.
|
|
|
84
84
|
## Post-Restore Validation
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
88
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
89
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
87
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
88
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin capture status
|
|
89
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin outbox status
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
Start only the MCP server and perform read-only `project_list`, `memory_recall`,
|
|
@@ -104,7 +104,7 @@ style error first if uncertain. Break only a proven stale lock with the exact
|
|
|
104
104
|
owner ID and confirmation:
|
|
105
105
|
|
|
106
106
|
```sh
|
|
107
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
107
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin unlock \
|
|
108
108
|
--owner <owner-id> \
|
|
109
109
|
--confirm BREAK_STALE_MIGRATION_LOCK
|
|
110
110
|
```
|
|
@@ -118,13 +118,13 @@ The first command is non-destructive and returns a digest over the exact backup
|
|
|
118
118
|
set:
|
|
119
119
|
|
|
120
120
|
```sh
|
|
121
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
121
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
Review every listed manifest/database pair. Delete only that unchanged set:
|
|
125
125
|
|
|
126
126
|
```sh
|
|
127
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
127
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune \
|
|
128
128
|
--digest <dry-run-digest> \
|
|
129
129
|
--confirm DELETE_VERIFIED_BACKUPS
|
|
130
130
|
```
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](backup-restore-runbook.md) | Türkçe
|
|
4
4
|
|
|
5
|
-
Bu runbook `@vaur94/agz-memory@0.4.
|
|
5
|
+
Bu runbook `@vaur94/agz-memory@0.4.1` ve SQLite schema v10 için geçerlidir.
|
|
6
6
|
|
|
7
7
|
## Ön Koşullar
|
|
8
8
|
|
|
@@ -25,16 +25,16 @@ Tahmin edilmiş veya boş bir yolla devam etmeyin.
|
|
|
25
25
|
Önce salt-okunur sağlık raporu alın:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
28
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
`ok` değeri `true` olmalıdır. `schemaVersion`, satır sayıları ve değişmez kural
|
|
32
32
|
sayılarını kaydedin. Sonra bağımsız doğrulanmış yedek oluşturup yükseltin:
|
|
33
33
|
|
|
34
34
|
```sh
|
|
35
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
36
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
37
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
35
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup
|
|
36
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin upgrade --to 10
|
|
37
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
Veritabanı v10'dan eskiyse yükseltme ayrıca değişiklikten önce doğrulanmış yedek
|
|
@@ -54,16 +54,16 @@ Manifest formatı `agz-memory-backup/1` olur. `agz-memory-admin restore`, manife
|
|
|
54
54
|
ile veritabanının aynı yedek dizinindeki normal dosyalar olduğunu doğrular;
|
|
55
55
|
ardından boyut, SHA-256, SQLite bütünlüğü, foreign key ve satır sayılarını denetler.
|
|
56
56
|
|
|
57
|
-
Final `0.4.
|
|
57
|
+
Final `0.4.1` ön sürüm manifest formatlarını kabul etmez. Böyle bir yedeği onu
|
|
58
58
|
oluşturan ön sürümle geri yükleyin, o sürümün doctor kontrolünü çalıştırın ve
|
|
59
|
-
yalnız bundan sonra geri yüklenen veritabanını `0.4.
|
|
59
|
+
yalnız bundan sonra geri yüklenen veritabanını `0.4.1` ile yükseltin.
|
|
60
60
|
|
|
61
61
|
## Geri Yükleme Provası
|
|
62
62
|
|
|
63
63
|
Tüm yazıcıları kapalı tutun. Önce onay vermeden deneme yapın:
|
|
64
64
|
|
|
65
65
|
```sh
|
|
66
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
66
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
|
|
67
67
|
"$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json"
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -71,7 +71,7 @@ bunx --package @vaur94/agz-memory@0.4.0 agz-memory-admin restore \
|
|
|
71
71
|
değerlerini kaydedilen yedekle karşılaştırın. Sonra tam manifest özetini kullanın:
|
|
72
72
|
|
|
73
73
|
```sh
|
|
74
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
74
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
|
|
75
75
|
"$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json" \
|
|
76
76
|
--sha256 <manifest-database-sha256> \
|
|
77
77
|
--confirm RESTORE_DATABASE_FROM_VERIFIED_BACKUP
|
|
@@ -84,9 +84,9 @@ veritabanı tüm kontrollerden geçmeden bunu silmeyin.
|
|
|
84
84
|
## Geri Yükleme Sonrası Doğrulama
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
88
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
89
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
87
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
|
|
88
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin capture status
|
|
89
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin outbox status
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
Yalnız MCP sunucusunu başlatın ve salt-okunur `project_list`, `memory_recall` ve
|
|
@@ -103,7 +103,7 @@ Sahip dosyasındaki PID, makine ve başlangıç zamanını doğrulayın. Yalnız
|
|
|
103
103
|
kanıtlanan kilidi tam sahip ID'si ve onayla kırın:
|
|
104
104
|
|
|
105
105
|
```sh
|
|
106
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
106
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin unlock \
|
|
107
107
|
--owner <owner-id> \
|
|
108
108
|
--confirm BREAK_STALE_MIGRATION_LOCK
|
|
109
109
|
```
|
|
@@ -116,14 +116,14 @@ kanıtıdır; veritabanı doğrulamasını atlama izni değildir.
|
|
|
116
116
|
İlk komut silme yapmaz ve tam yedek kümesinin özetini döndürür:
|
|
117
117
|
|
|
118
118
|
```sh
|
|
119
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
119
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
Listelenen her manifest/veritabanı çiftini inceleyin. Yalnız değişmemiş kümeyi
|
|
123
123
|
silin:
|
|
124
124
|
|
|
125
125
|
```sh
|
|
126
|
-
bunx --package @vaur94/agz-memory@0.4.
|
|
126
|
+
bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune \
|
|
127
127
|
--digest <dry-run-digest> \
|
|
128
128
|
--confirm DELETE_VERIFIED_BACKUPS
|
|
129
129
|
```
|