@origintrail-official/dkg-node-ui 10.0.13 → 10.0.14
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/dist/chat-memory.d.ts +43 -2
- package/dist/chat-memory.d.ts.map +1 -1
- package/dist/chat-memory.js +31 -7
- package/dist/chat-memory.js.map +1 -1
- package/dist/db.d.ts +18 -10
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +181 -51
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/db.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { RESPONSE_CACHE_BYTES, parseContextGraphJoinPolicyRecord, } from '@origintrail-official/dkg-core';
|
|
3
|
+
import { RESPONSE_CACHE_BYTES, parseContextGraphJoinPolicyRecord, DEFAULT_SYNC_CHECKPOINT_TTL_MS, isValidSyncCheckpointEntry, transitionSyncCheckpointManifestOffset, transitionSyncCheckpointOffset, transitionSyncCheckpointResponderSession, withoutSyncCheckpointResponderSession, } from '@origintrail-official/dkg-core';
|
|
4
4
|
export { SqliteChainEventCursorStore, SqliteContextGraphRegistryScanCursorStore, } from './chain-cursor-stores.js';
|
|
5
|
-
const SCHEMA_VERSION =
|
|
5
|
+
export const SCHEMA_VERSION = 34;
|
|
6
6
|
// Default operator retention. Lowered from 90 → 14 days on V15 (2026-05) after
|
|
7
7
|
// a production incident in which the `logs` table + its FTS5 shadow tables
|
|
8
8
|
// grew to ~9 GB on a 12-day-old node and corrupted the SQLite page (header
|
|
@@ -172,7 +172,7 @@ export class DashboardDB {
|
|
|
172
172
|
);
|
|
173
173
|
`);
|
|
174
174
|
};
|
|
175
|
-
const
|
|
175
|
+
const ensureSyncCheckpointResumeColumns = () => {
|
|
176
176
|
const table = this.db.prepare(`
|
|
177
177
|
SELECT 1 AS found FROM sqlite_master
|
|
178
178
|
WHERE type = 'table' AND name = 'sync_checkpoints'
|
|
@@ -187,6 +187,18 @@ export class DashboardDB {
|
|
|
187
187
|
if (!columns.has('responder_session_expires_at')) {
|
|
188
188
|
this.db.exec(`ALTER TABLE sync_checkpoints ADD COLUMN responder_session_expires_at INTEGER;`);
|
|
189
189
|
}
|
|
190
|
+
if (!columns.has('responder_session_offset')) {
|
|
191
|
+
this.db.exec(`ALTER TABLE sync_checkpoints ADD COLUMN responder_session_offset INTEGER;`);
|
|
192
|
+
}
|
|
193
|
+
if (!columns.has('manifest_digest')) {
|
|
194
|
+
this.db.exec(`ALTER TABLE sync_checkpoints ADD COLUMN manifest_digest TEXT;`);
|
|
195
|
+
}
|
|
196
|
+
if (!columns.has('manifest_prefix_digest')) {
|
|
197
|
+
this.db.exec(`ALTER TABLE sync_checkpoints ADD COLUMN manifest_prefix_digest TEXT;`);
|
|
198
|
+
}
|
|
199
|
+
if (!columns.has('terminal')) {
|
|
200
|
+
this.db.exec(`ALTER TABLE sync_checkpoints ADD COLUMN terminal INTEGER NOT NULL DEFAULT 0 CHECK (terminal IN (0, 1));`);
|
|
201
|
+
}
|
|
190
202
|
};
|
|
191
203
|
if (version > SCHEMA_VERSION)
|
|
192
204
|
return;
|
|
@@ -194,7 +206,7 @@ export class DashboardDB {
|
|
|
194
206
|
// Repair restored/development databases that carry the current version
|
|
195
207
|
// but lost an idempotent schema adjunct.
|
|
196
208
|
ensureJoinApprovalRepairMarker();
|
|
197
|
-
|
|
209
|
+
ensureSyncCheckpointResumeColumns();
|
|
198
210
|
ensureJoinPolicyAuditCapTrigger();
|
|
199
211
|
return;
|
|
200
212
|
}
|
|
@@ -789,7 +801,13 @@ export class DashboardDB {
|
|
|
789
801
|
key TEXT PRIMARY KEY,
|
|
790
802
|
offset INTEGER NOT NULL CHECK (offset >= 0),
|
|
791
803
|
updated_at INTEGER NOT NULL,
|
|
792
|
-
expires_at INTEGER NOT NULL
|
|
804
|
+
expires_at INTEGER NOT NULL,
|
|
805
|
+
responder_session_id TEXT,
|
|
806
|
+
responder_session_expires_at INTEGER,
|
|
807
|
+
responder_session_offset INTEGER CHECK (responder_session_offset >= 0),
|
|
808
|
+
manifest_digest TEXT,
|
|
809
|
+
manifest_prefix_digest TEXT,
|
|
810
|
+
terminal INTEGER NOT NULL DEFAULT 0 CHECK (terminal IN (0, 1))
|
|
793
811
|
);
|
|
794
812
|
CREATE INDEX IF NOT EXISTS idx_sync_checkpoints_expires_at
|
|
795
813
|
ON sync_checkpoints(expires_at);
|
|
@@ -1023,7 +1041,7 @@ export class DashboardDB {
|
|
|
1023
1041
|
// OFFSET pagination is scoped to an immutable responder row list. Keep
|
|
1024
1042
|
// the opaque responder token beside the verified offset so a requester
|
|
1025
1043
|
// restart can resume that same list instead of discarding durable work.
|
|
1026
|
-
|
|
1044
|
+
ensureSyncCheckpointResumeColumns();
|
|
1027
1045
|
}
|
|
1028
1046
|
if (version < 31) {
|
|
1029
1047
|
this.db.exec(`
|
|
@@ -1042,6 +1060,41 @@ export class DashboardDB {
|
|
|
1042
1060
|
CHECK (length(checksum) > 0)
|
|
1043
1061
|
);
|
|
1044
1062
|
`);
|
|
1063
|
+
}
|
|
1064
|
+
if (version < 32) {
|
|
1065
|
+
this.db.exec(`
|
|
1066
|
+
CREATE TABLE IF NOT EXISTS selected_vm_reconcile_cursors (
|
|
1067
|
+
deployment_id TEXT NOT NULL CHECK (length(trim(deployment_id)) > 0),
|
|
1068
|
+
context_graph_id TEXT NOT NULL,
|
|
1069
|
+
on_chain_context_graph_id TEXT NOT NULL,
|
|
1070
|
+
name_hash TEXT NOT NULL,
|
|
1071
|
+
watermark INTEGER NOT NULL CHECK (watermark >= 0),
|
|
1072
|
+
updated_at INTEGER NOT NULL,
|
|
1073
|
+
PRIMARY KEY (deployment_id, context_graph_id, on_chain_context_graph_id)
|
|
1074
|
+
);
|
|
1075
|
+
CREATE INDEX IF NOT EXISTS idx_selected_vm_reconcile_cursor_cg
|
|
1076
|
+
ON selected_vm_reconcile_cursors(deployment_id, context_graph_id);
|
|
1077
|
+
`);
|
|
1078
|
+
}
|
|
1079
|
+
if (version < 33) {
|
|
1080
|
+
// A durable DATA offset is reusable only with cryptographic proof of the
|
|
1081
|
+
// canonical META generation and verified descriptor prefix that gave it
|
|
1082
|
+
// meaning. Persist both digests beside the offset/session so production
|
|
1083
|
+
// SQLite checkpoints have the same safety contract as the in-memory
|
|
1084
|
+
// requester store and survive daemon restarts.
|
|
1085
|
+
ensureSyncCheckpointResumeColumns();
|
|
1086
|
+
}
|
|
1087
|
+
if (version < 34) {
|
|
1088
|
+
// The V33 manifest-bound continuation must not share a key with V32,
|
|
1089
|
+
// which interpreted OFFSET as an unverified raw responder coordinate.
|
|
1090
|
+
// Drop every pre-versioned durable DATA row before new code starts using
|
|
1091
|
+
// the `|checkpoint:v2` namespace, so a later rollback also fails closed.
|
|
1092
|
+
ensureSyncCheckpointResumeColumns();
|
|
1093
|
+
this.db.prepare(`
|
|
1094
|
+
DELETE FROM sync_checkpoints
|
|
1095
|
+
WHERE key LIKE '%|durable|data%'
|
|
1096
|
+
AND key NOT LIKE '%|durable|data|checkpoint:v2%'
|
|
1097
|
+
`).run();
|
|
1045
1098
|
}
|
|
1046
1099
|
this.db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
1047
1100
|
if (upgradedExistingDb && !this.explicitRetentionDays) {
|
|
@@ -1640,6 +1693,29 @@ export class DashboardDB {
|
|
|
1640
1693
|
deleteVmReconcileNegativesForContextGraph(contextGraphId) {
|
|
1641
1694
|
this.stmt('deleteVmReconcileNegativesForContextGraph', 'DELETE FROM vm_reconcile_negative_cache WHERE context_graph_id = ?').run(contextGraphId);
|
|
1642
1695
|
}
|
|
1696
|
+
upsertSelectedVmReconcileCursor(record) {
|
|
1697
|
+
this.stmt('upsertSelectedVmReconcileCursor', `
|
|
1698
|
+
INSERT INTO selected_vm_reconcile_cursors (
|
|
1699
|
+
deployment_id, context_graph_id, on_chain_context_graph_id,
|
|
1700
|
+
name_hash, watermark, updated_at
|
|
1701
|
+
) VALUES (
|
|
1702
|
+
@deployment_id, @context_graph_id, @on_chain_context_graph_id,
|
|
1703
|
+
@name_hash, @watermark, @updated_at
|
|
1704
|
+
)
|
|
1705
|
+
ON CONFLICT(deployment_id, context_graph_id, on_chain_context_graph_id) DO UPDATE SET
|
|
1706
|
+
name_hash = excluded.name_hash,
|
|
1707
|
+
watermark = excluded.watermark,
|
|
1708
|
+
updated_at = excluded.updated_at
|
|
1709
|
+
`).run(record);
|
|
1710
|
+
}
|
|
1711
|
+
getSelectedVmReconcileCursor(deploymentId, contextGraphId, onChainContextGraphId) {
|
|
1712
|
+
return this.stmt('getSelectedVmReconcileCursor', `
|
|
1713
|
+
SELECT * FROM selected_vm_reconcile_cursors
|
|
1714
|
+
WHERE deployment_id = ?
|
|
1715
|
+
AND context_graph_id = ?
|
|
1716
|
+
AND on_chain_context_graph_id = ?
|
|
1717
|
+
`).get(deploymentId, contextGraphId, onChainContextGraphId);
|
|
1718
|
+
}
|
|
1643
1719
|
// --- Phase F: chain-driven VM reconciliation telemetry ---
|
|
1644
1720
|
/**
|
|
1645
1721
|
* Persist one {@link ReplicationEvent} (best-effort sink target). `ordinal`,
|
|
@@ -2691,7 +2767,6 @@ export class DashboardDB {
|
|
|
2691
2767
|
}
|
|
2692
2768
|
}
|
|
2693
2769
|
// --- Sync requester checkpoints (issue #1138 A3) ---
|
|
2694
|
-
const DEFAULT_SYNC_CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
2695
2770
|
export class SqliteSyncCheckpointStore {
|
|
2696
2771
|
db;
|
|
2697
2772
|
clock;
|
|
@@ -2701,73 +2776,128 @@ export class SqliteSyncCheckpointStore {
|
|
|
2701
2776
|
this.clock = options.clock ?? (() => Date.now());
|
|
2702
2777
|
this.ttlMs = options.ttlMs ?? DEFAULT_SYNC_CHECKPOINT_TTL_MS;
|
|
2703
2778
|
}
|
|
2704
|
-
|
|
2705
|
-
const now = this.clock();
|
|
2779
|
+
readRow(key) {
|
|
2706
2780
|
const row = this.db.prepare(`SELECT offset, updated_at, expires_at,
|
|
2707
|
-
responder_session_id, responder_session_expires_at
|
|
2781
|
+
responder_session_id, responder_session_expires_at, responder_session_offset,
|
|
2782
|
+
manifest_digest, manifest_prefix_digest, terminal
|
|
2708
2783
|
FROM sync_checkpoints WHERE key = ?`).get(key);
|
|
2709
2784
|
if (!row)
|
|
2710
2785
|
return undefined;
|
|
2711
|
-
if (row.expires_at < now) {
|
|
2712
|
-
this.delete(key);
|
|
2713
|
-
return undefined;
|
|
2714
|
-
}
|
|
2715
|
-
const hasFreshResponderSession = Boolean(row.responder_session_id)
|
|
2716
|
-
&& Number.isSafeInteger(row.responder_session_expires_at)
|
|
2717
|
-
&& (row.responder_session_expires_at ?? 0) > now;
|
|
2718
|
-
if (row.responder_session_id && !hasFreshResponderSession) {
|
|
2719
|
-
this.clearResponderSession(key);
|
|
2720
|
-
}
|
|
2721
2786
|
return {
|
|
2722
2787
|
offset: row.offset,
|
|
2723
2788
|
updatedAtMs: row.updated_at,
|
|
2724
2789
|
expiresAtMs: row.expires_at,
|
|
2725
|
-
...(
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
}
|
|
2790
|
+
...(row.terminal === 1 ? { terminal: true } : {}),
|
|
2791
|
+
...(row.manifest_digest ? { manifestDigest: row.manifest_digest } : {}),
|
|
2792
|
+
...(row.manifest_prefix_digest
|
|
2793
|
+
? { manifestPrefixDigest: row.manifest_prefix_digest }
|
|
2794
|
+
: {}),
|
|
2795
|
+
// Preserve each nullable session column independently so the shared
|
|
2796
|
+
// validator can distinguish an absent session from a torn/malformed
|
|
2797
|
+
// persisted session. Collapsing a partial row to no session fields would
|
|
2798
|
+
// turn corrupt durable state into an apparently valid ordinary offset.
|
|
2799
|
+
...(row.responder_session_id !== null
|
|
2800
|
+
? { responderSessionId: row.responder_session_id }
|
|
2801
|
+
: {}),
|
|
2802
|
+
...(row.responder_session_expires_at !== null
|
|
2803
|
+
? { responderSessionExpiresAtMs: row.responder_session_expires_at }
|
|
2804
|
+
: {}),
|
|
2805
|
+
...(row.responder_session_offset !== null
|
|
2806
|
+
? { responderSessionOffset: row.responder_session_offset }
|
|
2730
2807
|
: {}),
|
|
2731
2808
|
};
|
|
2732
2809
|
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2810
|
+
get(key, now = this.clock()) {
|
|
2811
|
+
const entry = this.readRow(key);
|
|
2812
|
+
if (!entry)
|
|
2813
|
+
return undefined;
|
|
2814
|
+
if (!isValidSyncCheckpointEntry(entry) || entry.expiresAtMs < now) {
|
|
2815
|
+
this.delete(key);
|
|
2816
|
+
return undefined;
|
|
2817
|
+
}
|
|
2818
|
+
if (entry.responderSessionId
|
|
2819
|
+
&& (entry.responderSessionExpiresAtMs ?? 0) <= now) {
|
|
2820
|
+
const withoutExpiredSession = withoutSyncCheckpointResponderSession(entry);
|
|
2821
|
+
this.writeEntry(key, withoutExpiredSession);
|
|
2822
|
+
return withoutExpiredSession;
|
|
2736
2823
|
}
|
|
2824
|
+
return entry;
|
|
2825
|
+
}
|
|
2826
|
+
writeEntry(key, entry) {
|
|
2737
2827
|
this.db.prepare(`
|
|
2738
|
-
INSERT INTO sync_checkpoints (
|
|
2739
|
-
|
|
2828
|
+
INSERT INTO sync_checkpoints (
|
|
2829
|
+
key, offset, updated_at, expires_at,
|
|
2830
|
+
responder_session_id, responder_session_expires_at, responder_session_offset,
|
|
2831
|
+
manifest_digest, manifest_prefix_digest, terminal
|
|
2832
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2740
2833
|
ON CONFLICT(key) DO UPDATE SET
|
|
2741
2834
|
offset = excluded.offset,
|
|
2742
2835
|
updated_at = excluded.updated_at,
|
|
2743
|
-
expires_at = excluded.expires_at
|
|
2744
|
-
|
|
2836
|
+
expires_at = excluded.expires_at,
|
|
2837
|
+
responder_session_id = excluded.responder_session_id,
|
|
2838
|
+
responder_session_expires_at = excluded.responder_session_expires_at,
|
|
2839
|
+
responder_session_offset = excluded.responder_session_offset,
|
|
2840
|
+
manifest_digest = excluded.manifest_digest,
|
|
2841
|
+
manifest_prefix_digest = excluded.manifest_prefix_digest,
|
|
2842
|
+
terminal = excluded.terminal
|
|
2843
|
+
`).run(key, entry.offset, entry.updatedAtMs, entry.expiresAtMs, entry.responderSessionId ?? null, entry.responderSessionExpiresAtMs ?? null, entry.responderSessionOffset ?? null, entry.manifestDigest ?? null, entry.manifestPrefixDigest ?? null, entry.terminal ? 1 : 0);
|
|
2844
|
+
}
|
|
2845
|
+
set(key, value, nowMs = this.clock(), responderSessionOffset) {
|
|
2846
|
+
const transition = this.db.transaction(() => {
|
|
2847
|
+
this.writeEntry(key, transitionSyncCheckpointOffset({
|
|
2848
|
+
key,
|
|
2849
|
+
existing: this.get(key, nowMs),
|
|
2850
|
+
value,
|
|
2851
|
+
nowMs,
|
|
2852
|
+
ttlMs: this.ttlMs,
|
|
2853
|
+
responderSessionOffset,
|
|
2854
|
+
}));
|
|
2855
|
+
});
|
|
2856
|
+
transition();
|
|
2745
2857
|
}
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2858
|
+
setManifestBoundOffset(key, value, manifestDigest, nowMs = this.clock(), manifestPrefixDigest, terminal = false, responderSessionOffset) {
|
|
2859
|
+
const transition = this.db.transaction(() => {
|
|
2860
|
+
this.writeEntry(key, transitionSyncCheckpointManifestOffset({
|
|
2861
|
+
key,
|
|
2862
|
+
existing: this.get(key, nowMs),
|
|
2863
|
+
value,
|
|
2864
|
+
manifestDigest,
|
|
2865
|
+
nowMs,
|
|
2866
|
+
ttlMs: this.ttlMs,
|
|
2867
|
+
manifestPrefixDigest,
|
|
2868
|
+
terminal,
|
|
2869
|
+
responderSessionOffset,
|
|
2870
|
+
}));
|
|
2871
|
+
});
|
|
2872
|
+
transition();
|
|
2873
|
+
}
|
|
2874
|
+
setResponderSession(key, sessionId, expiresAtMs, nowMs = this.clock(), manifestDigest, manifestPrefixDigest, responderSessionOffset) {
|
|
2750
2875
|
if (expiresAtMs <= nowMs) {
|
|
2751
2876
|
this.clearResponderSession(key);
|
|
2752
2877
|
return;
|
|
2753
2878
|
}
|
|
2754
|
-
this.db.
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2879
|
+
const transition = this.db.transaction(() => {
|
|
2880
|
+
this.writeEntry(key, transitionSyncCheckpointResponderSession({
|
|
2881
|
+
key,
|
|
2882
|
+
existing: this.get(key, nowMs),
|
|
2883
|
+
sessionId,
|
|
2884
|
+
expiresAtMs,
|
|
2885
|
+
nowMs,
|
|
2886
|
+
ttlMs: this.ttlMs,
|
|
2887
|
+
manifestDigest,
|
|
2888
|
+
manifestPrefixDigest,
|
|
2889
|
+
responderSessionOffset,
|
|
2890
|
+
}));
|
|
2891
|
+
});
|
|
2892
|
+
transition();
|
|
2763
2893
|
}
|
|
2764
2894
|
clearResponderSession(key) {
|
|
2765
|
-
this.db.
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2895
|
+
const transition = this.db.transaction(() => {
|
|
2896
|
+
const existing = this.readRow(key);
|
|
2897
|
+
if (existing)
|
|
2898
|
+
this.writeEntry(key, withoutSyncCheckpointResponderSession(existing));
|
|
2899
|
+
});
|
|
2900
|
+
transition();
|
|
2771
2901
|
}
|
|
2772
2902
|
delete(key) {
|
|
2773
2903
|
this.db.prepare(`DELETE FROM sync_checkpoints WHERE key = ?`).run(key);
|