context-mode 1.0.128 → 1.0.130
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/adapters/detect.d.ts +30 -2
- package/build/adapters/detect.js +31 -0
- package/build/adapters/pi/mcp-bridge.js +19 -4
- package/build/db-base.d.ts +20 -0
- package/build/db-base.js +36 -53
- package/cli.bundle.mjs +126 -126
- package/hooks/session-db.bundle.mjs +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +86 -86
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.130"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "context-mode",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.130",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.130",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.130",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.130",
|
|
4
4
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -34,12 +34,20 @@ export declare function __seedClaudeCodePluginCacheMissForTests(): void;
|
|
|
34
34
|
* `resolveProjectDir({ strictPlatform })` to form the candidate list,
|
|
35
35
|
* and by Pi's bridge to scrub foreign workspace vars on child spawn.
|
|
36
36
|
* - `identification`: env var only signals which host is running; carries
|
|
37
|
-
* no project path.
|
|
38
|
-
*
|
|
37
|
+
* no project path. PRESERVED in normal operation (some are load-bearing
|
|
38
|
+
* for hook integrations on the host that owns them, e.g. CLAUDE_PLUGIN_ROOT
|
|
39
|
+
* for Claude Code's hook context).
|
|
39
40
|
*
|
|
40
41
|
* Issue #545 — algorithmic env-leak fix. The split allows resolveProjectDir
|
|
41
42
|
* to derive ALLOW (own workspace vars) and BAN (other platforms' workspace
|
|
42
43
|
* vars) sets from a single registry, satisfying MUST-3 (15 adapters equal).
|
|
44
|
+
*
|
|
45
|
+
* Issue #561 — FOREIGN identification vars MUST be scrubbed when spawning a
|
|
46
|
+
* child under a different host (e.g. Pi spawning context-mode child must
|
|
47
|
+
* scrub Claude Code identification vars CLAUDE_CODE_ENTRYPOINT /
|
|
48
|
+
* CLAUDE_PLUGIN_ROOT to prevent detectPlatform() in the child from
|
|
49
|
+
* misidentifying the host as claude-code and writing Pi's data into
|
|
50
|
+
* ~/.claude/context-mode/). See `foreignIdentificationEnv()` below.
|
|
43
51
|
*/
|
|
44
52
|
export type EnvVarRole = "workspace" | "identification";
|
|
45
53
|
export interface PlatformEnvEntry {
|
|
@@ -78,6 +86,26 @@ export declare function workspaceEnvVarsFor(platform: PlatformId): string[];
|
|
|
78
86
|
* workspace vars from spawned MCP child) and by the matrix regression test.
|
|
79
87
|
*/
|
|
80
88
|
export declare function foreignWorkspaceEnv(platform: PlatformId): Set<string>;
|
|
89
|
+
/**
|
|
90
|
+
* Issue #561 — return the union of identification env vars from ALL
|
|
91
|
+
* platforms EXCEPT the given one. Sibling of `foreignWorkspaceEnv`,
|
|
92
|
+
* filtered on `role === "identification"` instead of "workspace".
|
|
93
|
+
*
|
|
94
|
+
* Consumed by Pi's bridge env scrub: when Pi spawns the context-mode
|
|
95
|
+
* MCP child, the child inherits the host shell env including any
|
|
96
|
+
* identification vars set by a co-resident Claude Code session
|
|
97
|
+
* (CLAUDE_CODE_ENTRYPOINT / CLAUDE_PLUGIN_ROOT). Without scrubbing,
|
|
98
|
+
* `detectPlatform()` in the child falls through env priority order and
|
|
99
|
+
* resolves to claude-code first — Pi's session data then writes into
|
|
100
|
+
* `~/.claude/context-mode/` instead of Pi's own dir. Scrubbing FOREIGN
|
|
101
|
+
* identification vars (everyone else's) preserves Pi's OWN identification
|
|
102
|
+
* vars (PI_CONFIG_DIR / PI_SESSION_FILE / PI_COMPILED) so the child still
|
|
103
|
+
* detects pi correctly.
|
|
104
|
+
*
|
|
105
|
+
* Algorithmic, registry-driven — adding adapter #16 grows the scrub
|
|
106
|
+
* automatically (no edit to mcp-bridge.ts).
|
|
107
|
+
*/
|
|
108
|
+
export declare function foreignIdentificationEnv(platform: PlatformId): Set<string>;
|
|
81
109
|
/**
|
|
82
110
|
* Sync map from platform identifier → home-relative path segments where that
|
|
83
111
|
* platform stores its config. Mirrors the `super([...])` argument passed by
|
package/build/adapters/detect.js
CHANGED
|
@@ -222,6 +222,37 @@ export function foreignWorkspaceEnv(platform) {
|
|
|
222
222
|
}
|
|
223
223
|
return ban;
|
|
224
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Issue #561 — return the union of identification env vars from ALL
|
|
227
|
+
* platforms EXCEPT the given one. Sibling of `foreignWorkspaceEnv`,
|
|
228
|
+
* filtered on `role === "identification"` instead of "workspace".
|
|
229
|
+
*
|
|
230
|
+
* Consumed by Pi's bridge env scrub: when Pi spawns the context-mode
|
|
231
|
+
* MCP child, the child inherits the host shell env including any
|
|
232
|
+
* identification vars set by a co-resident Claude Code session
|
|
233
|
+
* (CLAUDE_CODE_ENTRYPOINT / CLAUDE_PLUGIN_ROOT). Without scrubbing,
|
|
234
|
+
* `detectPlatform()` in the child falls through env priority order and
|
|
235
|
+
* resolves to claude-code first — Pi's session data then writes into
|
|
236
|
+
* `~/.claude/context-mode/` instead of Pi's own dir. Scrubbing FOREIGN
|
|
237
|
+
* identification vars (everyone else's) preserves Pi's OWN identification
|
|
238
|
+
* vars (PI_CONFIG_DIR / PI_SESSION_FILE / PI_COMPILED) so the child still
|
|
239
|
+
* detects pi correctly.
|
|
240
|
+
*
|
|
241
|
+
* Algorithmic, registry-driven — adding adapter #16 grows the scrub
|
|
242
|
+
* automatically (no edit to mcp-bridge.ts).
|
|
243
|
+
*/
|
|
244
|
+
export function foreignIdentificationEnv(platform) {
|
|
245
|
+
const ban = new Set();
|
|
246
|
+
for (const [p, vars] of PLATFORM_ENV_VARS) {
|
|
247
|
+
if (p === platform)
|
|
248
|
+
continue;
|
|
249
|
+
for (const v of vars) {
|
|
250
|
+
if (v.role === "identification")
|
|
251
|
+
ban.add(v.name);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return ban;
|
|
255
|
+
}
|
|
225
256
|
/**
|
|
226
257
|
* Sync map from platform identifier → home-relative path segments where that
|
|
227
258
|
* platform stores its config. Mirrors the `super([...])` argument passed by
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { spawn, execSync } from "node:child_process";
|
|
24
24
|
import { detectRuntimes } from "../../runtime.js";
|
|
25
|
-
import { foreignWorkspaceEnv } from "../detect.js";
|
|
25
|
+
import { foreignWorkspaceEnv, foreignIdentificationEnv } from "../detect.js";
|
|
26
26
|
// ── Fork-bomb prevention (#516) ──────────────────────────────────────
|
|
27
27
|
//
|
|
28
28
|
// Original bug: `spawn(process.execPath, [serverScript])` recursively
|
|
@@ -154,12 +154,27 @@ export class MCPStdioClient {
|
|
|
154
154
|
// and Pi's sessions write into the wrong project. The ban list is
|
|
155
155
|
// derived ALGORITHMICALLY from PLATFORM_ENV_VARS (every other adapter's
|
|
156
156
|
// workspace-role vars), so adding adapter #16 grows the scrub
|
|
157
|
-
// automatically — no edit to this file.
|
|
158
|
-
//
|
|
159
|
-
//
|
|
157
|
+
// automatically — no edit to this file. Pi's own workspace vars and
|
|
158
|
+
// the universal escape hatch (CONTEXT_MODE_PROJECT_DIR) are NEVER
|
|
159
|
+
// scrubbed.
|
|
160
160
|
for (const banned of foreignWorkspaceEnv("pi")) {
|
|
161
161
|
delete childEnv[banned];
|
|
162
162
|
}
|
|
163
|
+
// Issue #561 — scrub foreign IDENTIFICATION env vars before spawn.
|
|
164
|
+
//
|
|
165
|
+
// Foreign identification vars hijack detectPlatform() — must scrub
|
|
166
|
+
// when spawning child under a different host (#561). When Pi runs
|
|
167
|
+
// co-resident with Claude Code, the inherited shell env carries
|
|
168
|
+
// CLAUDE_CODE_ENTRYPOINT and CLAUDE_PLUGIN_ROOT; the spawned MCP
|
|
169
|
+
// child's detectPlatform() then walks PLATFORM_ENV_VARS in priority
|
|
170
|
+
// order (claude-code first), returns claude-code, and Pi's session
|
|
171
|
+
// data lands in ~/.claude/context-mode/ instead of Pi's own dir.
|
|
172
|
+
// Pi's OWN identification vars (PI_CONFIG_DIR / PI_SESSION_FILE /
|
|
173
|
+
// PI_COMPILED) are excluded from the ban set so the child still
|
|
174
|
+
// detects pi correctly.
|
|
175
|
+
for (const banned of foreignIdentificationEnv("pi")) {
|
|
176
|
+
delete childEnv[banned];
|
|
177
|
+
}
|
|
163
178
|
this._spawnEnv = childEnv;
|
|
164
179
|
this.child = spawn(runtime, [this.serverScript], {
|
|
165
180
|
// Pipe stderr (#472 round-3): swallowing it via "ignore" hides
|
package/build/db-base.d.ts
CHANGED
|
@@ -139,6 +139,26 @@ export declare function isSQLiteCorruptionError(msg: string): boolean;
|
|
|
139
139
|
export declare function renameCorruptDB(dbPath: string): void;
|
|
140
140
|
export declare abstract class SQLiteBase {
|
|
141
141
|
#private;
|
|
142
|
+
/**
|
|
143
|
+
* Open (or create) a SQLite DB at `dbPath`.
|
|
144
|
+
*
|
|
145
|
+
* v1.0.130 — multi-writer is the contract. ALL SQLiteBase consumers
|
|
146
|
+
* (SessionDB, ContentStore) may open the same on-disk dbPath from
|
|
147
|
+
* multiple processes simultaneously — that is the legitimate multi-
|
|
148
|
+
* window UX shape and the WAL handles it natively. SQLITE_BUSY on
|
|
149
|
+
* write contention is absorbed by `withRetry()` below (busy_timeout
|
|
150
|
+
* = 30000ms inside `new Database(...)`).
|
|
151
|
+
*
|
|
152
|
+
* v1.0.128 introduced a single-writer guard here as a defense against
|
|
153
|
+
* #560. That defense was an over-correction — the actual root causes
|
|
154
|
+
* of #560 were #559 (zombie MCP child accumulation) and #561 (Pi
|
|
155
|
+
* misdetection writing to the wrong DB path), both fixed in v1.0.128
|
|
156
|
+
* + v1.0.129. The single-writer guard broke legitimate multi-window
|
|
157
|
+
* users; v1.0.130 rolls it out. See
|
|
158
|
+
* docs/adr/0001-sessiondb-multi-writer.md and the v1.0.130 INVARIANT
|
|
159
|
+
* block in tests/util/db-base-platform-gate.test.ts for the
|
|
160
|
+
* regression-proof anchor (source-pin + behavioural).
|
|
161
|
+
*/
|
|
142
162
|
constructor(dbPath: string);
|
|
143
163
|
/** Called once after WAL pragmas are applied. Subclasses run CREATE TABLE/VIRTUAL TABLE here. */
|
|
144
164
|
protected abstract initSchema(): void;
|
package/build/db-base.js
CHANGED
|
@@ -9,12 +9,6 @@ import { createRequire } from "node:module";
|
|
|
9
9
|
import { existsSync, unlinkSync, renameSync } from "node:fs";
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
11
|
import { join } from "node:path";
|
|
12
|
-
// v1.0.128 — Issue #560 single-writer enforcement.
|
|
13
|
-
// Lockfile is the PRIMARY defense (clean UX with conflicting PID),
|
|
14
|
-
// `locking_mode = EXCLUSIVE` (applied in applyWALPragmas below) is the
|
|
15
|
-
// SECONDARY defense for the narrow race window between lockfile claim
|
|
16
|
-
// and the actual `new Database(...)` open. Both skip-gate on tmpdir paths.
|
|
17
|
-
import { acquireDbLock, releaseDbLock } from "./util/db-lock.js";
|
|
18
12
|
// ─────────────────────────────────────────────────────────
|
|
19
13
|
// bun:sqlite adapter (#45)
|
|
20
14
|
// ─────────────────────────────────────────────────────────
|
|
@@ -316,18 +310,13 @@ export function applyWALPragmas(db) {
|
|
|
316
310
|
db.pragma("mmap_size = 268435456");
|
|
317
311
|
}
|
|
318
312
|
catch { /* unsupported runtime */ }
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
// locking_mode (or pragma at all) still get the lockfile floor.
|
|
327
|
-
try {
|
|
328
|
-
db.pragma("locking_mode = EXCLUSIVE");
|
|
329
|
-
}
|
|
330
|
-
catch { /* unsupported runtime */ }
|
|
313
|
+
// NOTE: `locking_mode = EXCLUSIVE` is intentionally NOT applied here.
|
|
314
|
+
// ALL DBs built on this helper — ContentStore (FTS5 shared knowledge
|
|
315
|
+
// base) AND SessionDB (per-project events) — are multi-writer-safe by
|
|
316
|
+
// contract. WAL + busy_timeout + the withRetry() wrapper below handle
|
|
317
|
+
// SQLITE_BUSY natively. EXCLUSIVE locking is opt-out, never opt-in
|
|
318
|
+
// from a base class shared by multi-writer consumers.
|
|
319
|
+
// See docs/adr/0001-sessiondb-multi-writer.md for the v1.0.130 ADR.
|
|
331
320
|
}
|
|
332
321
|
// ─────────────────────────────────────────────────────────
|
|
333
322
|
// DB file helpers
|
|
@@ -468,26 +457,19 @@ export function renameCorruptDB(dbPath) {
|
|
|
468
457
|
* re-imports within the same fork process (ESM isolate mode clears
|
|
469
458
|
* module-level state but globalThis persists).
|
|
470
459
|
*/
|
|
471
|
-
// v1.0.
|
|
472
|
-
//
|
|
473
|
-
// A persistent global slot from a
|
|
474
|
-
// the wrong shape and crash the
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
// owns its lockfile. The exit hook needs both — closeDB(db) handles the
|
|
478
|
-
// WAL checkpoint, releaseDbLock(dbPath) drops the .lock file. We use a
|
|
479
|
-
// Map keyed by DatabaseInstance to keep API call sites unchanged.
|
|
460
|
+
// v1.0.130 — symbol name bumped because the value type reverted from
|
|
461
|
+
// Map<DatabaseInstance, string> (v1.0.128 lockfile pairing) back to
|
|
462
|
+
// Set<DatabaseInstance>. A persistent global slot from a v1.0.128 or
|
|
463
|
+
// v1.0.129 module would deserialize as the wrong shape and crash the
|
|
464
|
+
// exit hook iteration.
|
|
465
|
+
const _kLiveDBs = Symbol.for("__context_mode_live_dbs_v3__");
|
|
480
466
|
const _liveDBs = (() => {
|
|
481
467
|
const g = globalThis;
|
|
482
468
|
if (!g[_kLiveDBs]) {
|
|
483
|
-
g[_kLiveDBs] = new
|
|
469
|
+
g[_kLiveDBs] = new Set();
|
|
484
470
|
process.on("exit", () => {
|
|
485
|
-
for (const
|
|
471
|
+
for (const db of g[_kLiveDBs]) {
|
|
486
472
|
closeDB(db);
|
|
487
|
-
// Release lock AFTER close so the WAL checkpoint inside closeDB
|
|
488
|
-
// runs while we still own the writer slot (no second-opener can
|
|
489
|
-
// race in mid-checkpoint).
|
|
490
|
-
releaseDbLock({ dbPath });
|
|
491
473
|
}
|
|
492
474
|
g[_kLiveDBs].clear();
|
|
493
475
|
});
|
|
@@ -497,16 +479,29 @@ const _liveDBs = (() => {
|
|
|
497
479
|
export class SQLiteBase {
|
|
498
480
|
#dbPath;
|
|
499
481
|
#db;
|
|
482
|
+
/**
|
|
483
|
+
* Open (or create) a SQLite DB at `dbPath`.
|
|
484
|
+
*
|
|
485
|
+
* v1.0.130 — multi-writer is the contract. ALL SQLiteBase consumers
|
|
486
|
+
* (SessionDB, ContentStore) may open the same on-disk dbPath from
|
|
487
|
+
* multiple processes simultaneously — that is the legitimate multi-
|
|
488
|
+
* window UX shape and the WAL handles it natively. SQLITE_BUSY on
|
|
489
|
+
* write contention is absorbed by `withRetry()` below (busy_timeout
|
|
490
|
+
* = 30000ms inside `new Database(...)`).
|
|
491
|
+
*
|
|
492
|
+
* v1.0.128 introduced a single-writer guard here as a defense against
|
|
493
|
+
* #560. That defense was an over-correction — the actual root causes
|
|
494
|
+
* of #560 were #559 (zombie MCP child accumulation) and #561 (Pi
|
|
495
|
+
* misdetection writing to the wrong DB path), both fixed in v1.0.128
|
|
496
|
+
* + v1.0.129. The single-writer guard broke legitimate multi-window
|
|
497
|
+
* users; v1.0.130 rolls it out. See
|
|
498
|
+
* docs/adr/0001-sessiondb-multi-writer.md and the v1.0.130 INVARIANT
|
|
499
|
+
* block in tests/util/db-base-platform-gate.test.ts for the
|
|
500
|
+
* regression-proof anchor (source-pin + behavioural).
|
|
501
|
+
*/
|
|
500
502
|
constructor(dbPath) {
|
|
501
503
|
const Database = loadDatabase();
|
|
502
504
|
this.#dbPath = dbPath;
|
|
503
|
-
// v1.0.128 — Issue #560 PRIMARY single-writer guard. Must claim
|
|
504
|
-
// BEFORE `new Database(...)` so a contending opener gets the clean
|
|
505
|
-
// DatabaseLockedError UX (PID + verbatim message) instead of the
|
|
506
|
-
// SQLITE_BUSY surfaced by EXCLUSIVE locking. Skip-gate via
|
|
507
|
-
// tmpdir-prefix check inside the helper — defaultDBPath() output
|
|
508
|
-
// (per-process tmp DBs) does not contend, so it never claims a lock.
|
|
509
|
-
acquireDbLock({ dbPath });
|
|
510
505
|
cleanOrphanedWALFiles(dbPath);
|
|
511
506
|
let db;
|
|
512
507
|
try {
|
|
@@ -523,19 +518,15 @@ export class SQLiteBase {
|
|
|
523
518
|
applyWALPragmas(db);
|
|
524
519
|
}
|
|
525
520
|
catch (retryErr) {
|
|
526
|
-
// Free the lock before bubbling — caller can never reach
|
|
527
|
-
// close()/cleanup() if the ctor throws.
|
|
528
|
-
releaseDbLock({ dbPath });
|
|
529
521
|
throw new Error(`Failed to create fresh DB after renaming corrupt file: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`);
|
|
530
522
|
}
|
|
531
523
|
}
|
|
532
524
|
else {
|
|
533
|
-
releaseDbLock({ dbPath });
|
|
534
525
|
throw err;
|
|
535
526
|
}
|
|
536
527
|
}
|
|
537
528
|
this.#db = db;
|
|
538
|
-
_liveDBs.
|
|
529
|
+
_liveDBs.add(this.#db);
|
|
539
530
|
this.initSchema();
|
|
540
531
|
this.prepareStatements();
|
|
541
532
|
}
|
|
@@ -551,10 +542,6 @@ export class SQLiteBase {
|
|
|
551
542
|
close() {
|
|
552
543
|
_liveDBs.delete(this.#db);
|
|
553
544
|
closeDB(this.#db);
|
|
554
|
-
// v1.0.128 — Issue #560: drop the .lock file AFTER closeDB so the
|
|
555
|
-
// WAL checkpoint inside closeDB completes while we still own the
|
|
556
|
-
// writer slot. releaseDbLock is no-op for tmpdir paths (skip-gate).
|
|
557
|
-
releaseDbLock({ dbPath: this.#dbPath });
|
|
558
545
|
}
|
|
559
546
|
withRetry(fn) {
|
|
560
547
|
return withRetry(fn);
|
|
@@ -567,9 +554,5 @@ export class SQLiteBase {
|
|
|
567
554
|
_liveDBs.delete(this.#db);
|
|
568
555
|
closeDB(this.#db);
|
|
569
556
|
deleteDBFiles(this.#dbPath);
|
|
570
|
-
// v1.0.128 — Issue #560: also drop the lockfile during cleanup. Per-
|
|
571
|
-
// process tmp DBs (defaultDBPath()) skip-gate inside the helper, so
|
|
572
|
-
// this is only a side-effect for shared on-disk content stores.
|
|
573
|
-
releaseDbLock({ dbPath: this.#dbPath });
|
|
574
557
|
}
|
|
575
558
|
}
|