@vaur94/agz-memory 0.4.0-beta.1 → 0.4.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/ARCHITECTURE.md +154 -156
- package/CHANGELOG.md +58 -16
- package/README.md +157 -141
- package/README.tr.md +158 -144
- package/dist/admin.js +172 -44
- package/dist/core.js +184 -113
- package/dist/server.js +577 -85
- package/dist/types/capture/contract.d.ts +2 -2
- package/dist/types/core.d.ts +1 -0
- package/dist/types/db/backup.d.ts +1 -1
- package/dist/types/db/migrations/v010.d.ts +2 -0
- package/dist/types/db/schema.d.ts +3 -2
- package/dist/types/server.d.ts +1 -1
- package/dist/types/store/capture.d.ts +5 -0
- package/dist/types/types.d.ts +1 -1
- package/dist/types/version.d.ts +1 -0
- package/docs/backup-restore-runbook.md +109 -33
- package/docs/backup-restore-runbook.tr.md +148 -0
- package/package.json +4 -2
package/dist/admin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/admin/index.ts
|
|
5
|
-
import { createHash as
|
|
5
|
+
import { createHash as createHash6 } from "crypto";
|
|
6
6
|
import { Database as Database3 } from "bun:sqlite";
|
|
7
7
|
import {
|
|
8
8
|
existsSync as existsSync4,
|
|
@@ -41,7 +41,8 @@ function normalizeProjectName(value) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// src/types.ts
|
|
44
|
-
var SCHEMA_VERSION =
|
|
44
|
+
var SCHEMA_VERSION = 10;
|
|
45
|
+
var KINDS = ["decision", "fact", "procedure", "context", "research", "preference", "task"];
|
|
45
46
|
var PREDICATES = ["SUPPORTS", "DERIVED_FROM", "PART_OF", "ABOUT", "PRECEDES", "SUPERSEDES"];
|
|
46
47
|
|
|
47
48
|
// src/db/backup.ts
|
|
@@ -103,7 +104,7 @@ function hasTable(db, table) {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// src/db/backup.ts
|
|
106
|
-
var BACKUP_FORMAT = "
|
|
107
|
+
var BACKUP_FORMAT = "agz-memory-backup/1";
|
|
107
108
|
function createVerifiedBackup(db, databasePath, sourceSchema, targetSchema, productVersion) {
|
|
108
109
|
const sourceHealth = assertHealthyDatabase(db);
|
|
109
110
|
const checkpoint = db.query("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
@@ -467,8 +468,77 @@ function processIsAlive(pid) {
|
|
|
467
468
|
// src/db/migrations/v009.ts
|
|
468
469
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
469
470
|
|
|
471
|
+
// src/capture/contract.ts
|
|
472
|
+
import * as z from "zod/v4";
|
|
473
|
+
var CAPTURE_SCHEMA = "agz-memory.capture/1";
|
|
474
|
+
var SUPPORTED_OPENCODE_VERSION = "0.0.0-beta-18743";
|
|
475
|
+
var CAPTURE_EVENT_MAX_BYTES = 16 * 1024;
|
|
476
|
+
var CAPTURE_CONTENT_MAX_CHARACTERS = 4800;
|
|
477
|
+
var CAPTURE_SUMMARY_MAX_CHARACTERS = 1200;
|
|
478
|
+
var CAPTURE_SUBJECT_MAX_CHARACTERS = 240;
|
|
479
|
+
var candidateSchema = z.object({
|
|
480
|
+
kind: z.enum(KINDS),
|
|
481
|
+
title: z.string().min(1).max(240),
|
|
482
|
+
summary: z.string().min(1).max(CAPTURE_SUMMARY_MAX_CHARACTERS),
|
|
483
|
+
content: z.string().min(1).max(CAPTURE_CONTENT_MAX_CHARACTERS),
|
|
484
|
+
subjectKey: z.string().min(1).max(CAPTURE_SUBJECT_MAX_CHARACTERS).optional(),
|
|
485
|
+
intent: z.enum(["create", "supersede", "ignore", "review"]),
|
|
486
|
+
targetNoteID: z.string().min(1).max(240).optional(),
|
|
487
|
+
confidence: z.number().finite().min(0).max(1),
|
|
488
|
+
evidence: z.enum(["explicit-user", "verified-outcome", "session-summary"])
|
|
489
|
+
}).strict();
|
|
490
|
+
var signalSchema = z.object({
|
|
491
|
+
tool: z.string().min(1).max(160),
|
|
492
|
+
status: z.enum(["completed", "error"]),
|
|
493
|
+
errorType: z.string().min(1).max(160).optional()
|
|
494
|
+
}).strict();
|
|
495
|
+
var captureEventSchema = z.object({
|
|
496
|
+
schema: z.literal(CAPTURE_SCHEMA),
|
|
497
|
+
idempotencyKey: z.string().regex(/^[0-9a-f]{64}$/),
|
|
498
|
+
projectID: z.uuid(),
|
|
499
|
+
bindingKey: z.string().regex(/^[0-9a-f]{64}$/),
|
|
500
|
+
kind: z.enum(["user-candidate", "assistant-candidate", "session-summary", "tool-signal"]),
|
|
501
|
+
source: z.object({
|
|
502
|
+
system: z.literal("opencode-v2"),
|
|
503
|
+
opencodeVersion: z.literal(SUPPORTED_OPENCODE_VERSION),
|
|
504
|
+
pluginVersion: z.string().min(1).max(80),
|
|
505
|
+
sessionID: z.string().min(1).max(240),
|
|
506
|
+
messageID: z.string().min(1).max(240).optional(),
|
|
507
|
+
ordinal: z.number().int().nonnegative().optional(),
|
|
508
|
+
toolCallID: z.string().min(1).max(240).optional(),
|
|
509
|
+
observedAt: z.number().int().nonnegative()
|
|
510
|
+
}).strict(),
|
|
511
|
+
candidate: candidateSchema.optional(),
|
|
512
|
+
signal: signalSchema.optional(),
|
|
513
|
+
redaction: z.object({
|
|
514
|
+
policyVersion: z.string().min(1).max(80),
|
|
515
|
+
replacements: z.number().int().nonnegative(),
|
|
516
|
+
truncated: z.boolean()
|
|
517
|
+
}).strict()
|
|
518
|
+
}).strict().superRefine((event, context) => {
|
|
519
|
+
if (event.kind === "tool-signal" && !event.signal) {
|
|
520
|
+
context.addIssue({ code: "custom", message: "tool-signal requires signal" });
|
|
521
|
+
}
|
|
522
|
+
if (event.kind !== "tool-signal" && !event.candidate) {
|
|
523
|
+
context.addIssue({ code: "custom", message: `${event.kind} requires candidate` });
|
|
524
|
+
}
|
|
525
|
+
if (event.kind === "tool-signal" && event.candidate) {
|
|
526
|
+
context.addIssue({ code: "custom", message: "tool-signal cannot carry candidate" });
|
|
527
|
+
}
|
|
528
|
+
if (event.kind !== "tool-signal" && event.signal) {
|
|
529
|
+
context.addIssue({ code: "custom", message: `${event.kind} cannot carry signal` });
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
function parseCaptureEvent(value) {
|
|
533
|
+
const event = captureEventSchema.parse(value);
|
|
534
|
+
if (Buffer.byteLength(JSON.stringify(event), "utf8") > CAPTURE_EVENT_MAX_BYTES) {
|
|
535
|
+
throw new Error(`capture event exceeds ${CAPTURE_EVENT_MAX_BYTES} UTF-8 bytes`);
|
|
536
|
+
}
|
|
537
|
+
return event;
|
|
538
|
+
}
|
|
539
|
+
|
|
470
540
|
// src/db/schema.ts
|
|
471
|
-
var
|
|
541
|
+
var SCHEMA_TABLES = `
|
|
472
542
|
CREATE TABLE IF NOT EXISTS projects (
|
|
473
543
|
id TEXT PRIMARY KEY,
|
|
474
544
|
name TEXT NOT NULL,
|
|
@@ -538,28 +608,7 @@ CREATE TABLE IF NOT EXISTS capture_checkpoints (
|
|
|
538
608
|
);
|
|
539
609
|
CREATE INDEX IF NOT EXISTS capture_checkpoints_due_idx
|
|
540
610
|
ON capture_checkpoints(state, next_reconcile_at);
|
|
541
|
-
|
|
542
|
-
idempotency_key TEXT PRIMARY KEY,
|
|
543
|
-
contract TEXT NOT NULL CHECK (contract = 'opencode2-memory.capture/1'),
|
|
544
|
-
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
545
|
-
binding_key TEXT NOT NULL REFERENCES project_bindings(binding_key) ON DELETE CASCADE,
|
|
546
|
-
event_kind TEXT NOT NULL CHECK (event_kind IN ('user-candidate','assistant-candidate','session-summary','tool-signal')),
|
|
547
|
-
source_session_id TEXT NOT NULL,
|
|
548
|
-
source_message_id TEXT,
|
|
549
|
-
source_ordinal INTEGER CHECK (source_ordinal IS NULL OR source_ordinal >= 0),
|
|
550
|
-
source_tool_call_id TEXT,
|
|
551
|
-
payload_json TEXT CHECK (payload_json IS NULL OR json_valid(payload_json)),
|
|
552
|
-
payload_hash TEXT,
|
|
553
|
-
redaction_version TEXT NOT NULL,
|
|
554
|
-
state TEXT NOT NULL CHECK (state IN ('pending','shadowed','review','materialized','duplicate','ignored','rejected','quarantined','failed','dead')),
|
|
555
|
-
attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
|
|
556
|
-
note_id TEXT,
|
|
557
|
-
last_error_code TEXT,
|
|
558
|
-
generation INTEGER NOT NULL DEFAULT 0 CHECK (generation >= 0),
|
|
559
|
-
created_at INTEGER NOT NULL,
|
|
560
|
-
updated_at INTEGER NOT NULL,
|
|
561
|
-
processed_at INTEGER
|
|
562
|
-
);
|
|
611
|
+
${captureEventsTable()}
|
|
563
612
|
CREATE INDEX IF NOT EXISTS capture_events_state_idx ON capture_events(state, updated_at);
|
|
564
613
|
CREATE INDEX IF NOT EXISTS capture_events_session_idx ON capture_events(project_id, source_session_id);
|
|
565
614
|
CREATE INDEX IF NOT EXISTS capture_events_note_idx ON capture_events(project_id, note_id);
|
|
@@ -643,15 +692,39 @@ CREATE TRIGGER IF NOT EXISTS notes_fts_au AFTER UPDATE OF title, summary, conten
|
|
|
643
692
|
VALUES (new.rowid, new.title, new.summary, new.content);
|
|
644
693
|
END;
|
|
645
694
|
`;
|
|
646
|
-
function
|
|
647
|
-
db.exec(
|
|
695
|
+
function createSchema(db) {
|
|
696
|
+
db.exec(SCHEMA_TABLES);
|
|
648
697
|
db.exec(FTS_V9);
|
|
649
698
|
db.query("DELETE FROM schema_state").run();
|
|
650
|
-
db.query("INSERT INTO schema_state(version) VALUES (
|
|
699
|
+
db.query("INSERT INTO schema_state(version) VALUES (10)").run();
|
|
651
700
|
}
|
|
652
701
|
function rebuildFts(db) {
|
|
653
702
|
db.query("INSERT INTO notes_fts(notes_fts) VALUES ('rebuild')").run();
|
|
654
703
|
}
|
|
704
|
+
function captureEventsTable(table = "capture_events") {
|
|
705
|
+
return `CREATE TABLE IF NOT EXISTS ${table} (
|
|
706
|
+
idempotency_key TEXT PRIMARY KEY,
|
|
707
|
+
contract TEXT NOT NULL CHECK (contract = '${CAPTURE_SCHEMA}'),
|
|
708
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
709
|
+
binding_key TEXT NOT NULL REFERENCES project_bindings(binding_key) ON DELETE CASCADE,
|
|
710
|
+
event_kind TEXT NOT NULL CHECK (event_kind IN ('user-candidate','assistant-candidate','session-summary','tool-signal')),
|
|
711
|
+
source_session_id TEXT NOT NULL,
|
|
712
|
+
source_message_id TEXT,
|
|
713
|
+
source_ordinal INTEGER CHECK (source_ordinal IS NULL OR source_ordinal >= 0),
|
|
714
|
+
source_tool_call_id TEXT,
|
|
715
|
+
payload_json TEXT CHECK (payload_json IS NULL OR json_valid(payload_json)),
|
|
716
|
+
payload_hash TEXT,
|
|
717
|
+
redaction_version TEXT NOT NULL,
|
|
718
|
+
state TEXT NOT NULL CHECK (state IN ('pending','shadowed','review','materialized','duplicate','ignored','rejected','quarantined','failed','dead')),
|
|
719
|
+
attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
|
|
720
|
+
note_id TEXT,
|
|
721
|
+
last_error_code TEXT,
|
|
722
|
+
generation INTEGER NOT NULL DEFAULT 0 CHECK (generation >= 0),
|
|
723
|
+
created_at INTEGER NOT NULL,
|
|
724
|
+
updated_at INTEGER NOT NULL,
|
|
725
|
+
processed_at INTEGER
|
|
726
|
+
);`;
|
|
727
|
+
}
|
|
655
728
|
|
|
656
729
|
// src/db/migrations/v009.ts
|
|
657
730
|
function migrateV8ToV9(db) {
|
|
@@ -720,7 +793,7 @@ function migrateV8ToV9(db) {
|
|
|
720
793
|
ALTER TABLE notes_v9 RENAME TO notes;
|
|
721
794
|
ALTER TABLE note_edges_v9 RENAME TO note_edges;
|
|
722
795
|
`);
|
|
723
|
-
db.exec(
|
|
796
|
+
db.exec(SCHEMA_TABLES);
|
|
724
797
|
for (const note of notes) {
|
|
725
798
|
const provenanceID = randomUUID3();
|
|
726
799
|
db.query(`
|
|
@@ -744,6 +817,57 @@ function noteContentHash(kind, title, summary, content) {
|
|
|
744
817
|
return createHash3("sha256").update(`${kind}\x00${title}\x00${summary}\x00${content}`, "utf8").digest("hex");
|
|
745
818
|
}
|
|
746
819
|
|
|
820
|
+
// src/db/migrations/v010.ts
|
|
821
|
+
import { createHash as createHash4 } from "crypto";
|
|
822
|
+
function migrateV9ToV10(db) {
|
|
823
|
+
const payloads = db.query("SELECT idempotency_key, payload_json FROM capture_events WHERE payload_json IS NOT NULL").all();
|
|
824
|
+
const migratedPayloads = payloads.map((row) => {
|
|
825
|
+
const event = JSON.parse(row.payload_json);
|
|
826
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) {
|
|
827
|
+
throw new Error(`capture event ${row.idempotency_key} payload is not an object`);
|
|
828
|
+
}
|
|
829
|
+
event.schema = CAPTURE_SCHEMA;
|
|
830
|
+
const payload = JSON.stringify(parseCaptureEvent(event));
|
|
831
|
+
return {
|
|
832
|
+
idempotencyKey: row.idempotency_key,
|
|
833
|
+
payload,
|
|
834
|
+
payloadHash: createHash4("sha256").update(payload, "utf8").digest("hex")
|
|
835
|
+
};
|
|
836
|
+
});
|
|
837
|
+
db.exec("DROP TABLE IF EXISTS capture_events_v10");
|
|
838
|
+
db.exec(captureEventsTable("capture_events_v10"));
|
|
839
|
+
db.query(`
|
|
840
|
+
INSERT INTO capture_events_v10
|
|
841
|
+
(idempotency_key, contract, project_id, binding_key, event_kind,
|
|
842
|
+
source_session_id, source_message_id, source_ordinal, source_tool_call_id,
|
|
843
|
+
payload_json, payload_hash, redaction_version, state, attempt_count,
|
|
844
|
+
note_id, last_error_code, generation, created_at, updated_at, processed_at)
|
|
845
|
+
SELECT idempotency_key, ?, project_id, binding_key, event_kind,
|
|
846
|
+
source_session_id, source_message_id, source_ordinal, source_tool_call_id,
|
|
847
|
+
payload_json, payload_hash, redaction_version, state, attempt_count,
|
|
848
|
+
note_id, last_error_code, generation, created_at, updated_at, processed_at
|
|
849
|
+
FROM capture_events
|
|
850
|
+
`).run(CAPTURE_SCHEMA);
|
|
851
|
+
const updatePayload = db.query(`
|
|
852
|
+
UPDATE capture_events_v10
|
|
853
|
+
SET payload_json = ?, payload_hash = ?
|
|
854
|
+
WHERE idempotency_key = ?
|
|
855
|
+
`);
|
|
856
|
+
for (const row of migratedPayloads) {
|
|
857
|
+
updatePayload.run(row.payload, row.payloadHash, row.idempotencyKey);
|
|
858
|
+
}
|
|
859
|
+
db.exec(`
|
|
860
|
+
DROP TABLE capture_events;
|
|
861
|
+
ALTER TABLE capture_events_v10 RENAME TO capture_events;
|
|
862
|
+
`);
|
|
863
|
+
db.exec(SCHEMA_TABLES);
|
|
864
|
+
db.query("DELETE FROM schema_state").run();
|
|
865
|
+
db.query("INSERT INTO schema_state(version) VALUES (10)").run();
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/version.ts
|
|
869
|
+
var PRODUCT_VERSION = "0.4.0";
|
|
870
|
+
|
|
747
871
|
// src/db.ts
|
|
748
872
|
var DDL = `
|
|
749
873
|
CREATE TABLE IF NOT EXISTS projects (
|
|
@@ -799,13 +923,13 @@ function openMemoryDatabase(path) {
|
|
|
799
923
|
const hasExistingData = hasLegacyV2(db) || hasTable2(db, "notes") || Boolean(existingVersion);
|
|
800
924
|
if (!hasExistingData) {
|
|
801
925
|
db.exec("PRAGMA foreign_keys=ON");
|
|
802
|
-
db.transaction(() =>
|
|
926
|
+
db.transaction(() => createSchema(db))();
|
|
803
927
|
assertHealthyDatabase(db);
|
|
804
928
|
return { db, close: () => db.close() };
|
|
805
929
|
}
|
|
806
930
|
if ((existingVersion?.version ?? 0) < SCHEMA_VERSION) {
|
|
807
931
|
lock = acquireMigrationLock(path, SCHEMA_VERSION);
|
|
808
|
-
backup = createVerifiedBackup(db, path, existingVersion?.version ?? 2, SCHEMA_VERSION,
|
|
932
|
+
backup = createVerifiedBackup(db, path, existingVersion?.version ?? 2, SCHEMA_VERSION, PRODUCT_VERSION);
|
|
809
933
|
db.exec("PRAGMA foreign_keys=OFF");
|
|
810
934
|
if (!existingVersion && hasLegacyV2(db)) {
|
|
811
935
|
db.exec(DDL);
|
|
@@ -824,9 +948,13 @@ function openMemoryDatabase(path) {
|
|
|
824
948
|
db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(id UNINDEXED, title, summary, content, tokenize='unicode61')");
|
|
825
949
|
migrateToV8(db);
|
|
826
950
|
}
|
|
827
|
-
|
|
828
|
-
if (version < 9)
|
|
951
|
+
let version = getSchemaVersion(db)?.version ?? 8;
|
|
952
|
+
if (version < 9) {
|
|
829
953
|
db.transaction(() => migrateV8ToV9(db))();
|
|
954
|
+
version = 9;
|
|
955
|
+
}
|
|
956
|
+
if (version < 10)
|
|
957
|
+
db.transaction(() => migrateV9ToV10(db))();
|
|
830
958
|
db.exec("PRAGMA foreign_keys=ON");
|
|
831
959
|
if (db.query("PRAGMA foreign_keys").get().foreign_keys !== 1) {
|
|
832
960
|
throw new Error("failed to enable database foreign keys");
|
|
@@ -838,7 +966,7 @@ function openMemoryDatabase(path) {
|
|
|
838
966
|
return { db, close: () => db.close() };
|
|
839
967
|
}
|
|
840
968
|
db.exec("PRAGMA foreign_keys=ON");
|
|
841
|
-
db.exec(
|
|
969
|
+
db.exec(SCHEMA_TABLES);
|
|
842
970
|
db.exec(FTS_V9);
|
|
843
971
|
assertHealthyDatabase(db);
|
|
844
972
|
db.exec("PRAGMA foreign_keys=ON");
|
|
@@ -1159,7 +1287,7 @@ function count(db, sql, ...bindings) {
|
|
|
1159
1287
|
}
|
|
1160
1288
|
|
|
1161
1289
|
// src/retrieval/derived.ts
|
|
1162
|
-
import { createHash as
|
|
1290
|
+
import { createHash as createHash5 } from "crypto";
|
|
1163
1291
|
|
|
1164
1292
|
// src/capture/redact.ts
|
|
1165
1293
|
var RULES = [
|
|
@@ -1190,15 +1318,15 @@ function redactText(value, options = {}) {
|
|
|
1190
1318
|
let replacements = 0;
|
|
1191
1319
|
let highRisk = 0;
|
|
1192
1320
|
const classes = {};
|
|
1193
|
-
for (const
|
|
1194
|
-
if (!
|
|
1321
|
+
for (const literal2 of options.denylist ?? []) {
|
|
1322
|
+
if (!literal2)
|
|
1195
1323
|
continue;
|
|
1196
|
-
const count2 = text.split(
|
|
1324
|
+
const count2 = text.split(literal2).length - 1;
|
|
1197
1325
|
if (count2 === 0)
|
|
1198
1326
|
continue;
|
|
1199
1327
|
replacements += count2;
|
|
1200
1328
|
classes.denylist = (classes.denylist ?? 0) + count2;
|
|
1201
|
-
text = text.replaceAll(
|
|
1329
|
+
text = text.replaceAll(literal2, "[REDACTED:denylist]");
|
|
1202
1330
|
}
|
|
1203
1331
|
for (const rule of RULES) {
|
|
1204
1332
|
text = text.replace(rule.pattern, () => {
|
|
@@ -1248,7 +1376,7 @@ function deriveDocument(source) {
|
|
|
1248
1376
|
const content = redactText(source.content);
|
|
1249
1377
|
if (title.quarantined || summary.quarantined || content.quarantined)
|
|
1250
1378
|
return;
|
|
1251
|
-
const contentHash =
|
|
1379
|
+
const contentHash = createHash5("sha256").update(`${source.kind}\x00${title.text}\x00${summary.text}\x00${content.text}`, "utf8").digest("hex");
|
|
1252
1380
|
return {
|
|
1253
1381
|
projectID: source.projectID,
|
|
1254
1382
|
noteID: source.noteID,
|
|
@@ -1282,7 +1410,7 @@ async function runAdmin(argv = process.argv.slice(2)) {
|
|
|
1282
1410
|
const db = new Database3(databasePath);
|
|
1283
1411
|
try {
|
|
1284
1412
|
const version = schemaVersion(db);
|
|
1285
|
-
return createVerifiedBackup(db, databasePath, version, version,
|
|
1413
|
+
return createVerifiedBackup(db, databasePath, version, version, PRODUCT_VERSION);
|
|
1286
1414
|
} finally {
|
|
1287
1415
|
db.close();
|
|
1288
1416
|
lock.release();
|
|
@@ -1390,7 +1518,7 @@ async function runAdmin(argv = process.argv.slice(2)) {
|
|
|
1390
1518
|
if (command === "backup" && subcommand === "prune") {
|
|
1391
1519
|
const entries = backupEntries(databasePath);
|
|
1392
1520
|
const root = resolve2(`${databasePath}.backup`);
|
|
1393
|
-
const digest =
|
|
1521
|
+
const digest = createHash6("sha256").update(`${resolve2(databasePath)}\x00${root}
|
|
1394
1522
|
${entries.map((entry) => `${basename2(entry.manifest)}\x00${basename2(entry.database)}\x00${entry.sha256}\x00${entry.size}\x00${entry.manifestHash}`).join(`
|
|
1395
1523
|
`)}`).digest("hex");
|
|
1396
1524
|
if (option(argv, "--confirm") !== "DELETE_VERIFIED_BACKUPS") {
|
|
@@ -1472,7 +1600,7 @@ function verifiedBackupEntry(root, manifest) {
|
|
|
1472
1600
|
database: verified.databasePath,
|
|
1473
1601
|
sha256: verified.manifest.sha256,
|
|
1474
1602
|
size: verified.manifest.size,
|
|
1475
|
-
manifestHash:
|
|
1603
|
+
manifestHash: createHash6("sha256").update(bytes).digest("hex")
|
|
1476
1604
|
};
|
|
1477
1605
|
}
|
|
1478
1606
|
if (import.meta.main) {
|