msnav 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +10 -4
- data/lib/msnav/server.rb +8 -2
- data/lib/msnav/store.rb +88 -19
- data/lib/msnav/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6d6d26b0049b7aa5df6c60df61e989ebd2402db9912cfb97ab30f176b6eacd6
|
|
4
|
+
data.tar.gz: 84ba3e939ece29c631b8fd17649888a6f4ef6a7ee1fa4be29d971a497682e40d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 594621248be0da1c37217e7286b08480dd7f9f76d48f2ad767fa1641c428867a9fa5ea4344f2b57dc0f1cae2f130fc9814563204645b0ed702d7923b5abc27ff
|
|
7
|
+
data.tar.gz: a71963f36a2aece2d7e7f4322f2a207c1e1a1efea2f1d4e366ec0d19b37a72e4739a1eaee592896812102b455e96d0f4826caca4584c9f7539a5c4ac864eb7f7
|
data/README.md
CHANGED
|
@@ -110,13 +110,19 @@ nothing (msnav never indexes).
|
|
|
110
110
|
|
|
111
111
|
## Compatibility
|
|
112
112
|
|
|
113
|
-
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
- Opens `coderag.db` **strictly read-only**. Compatibility is checked via
|
|
114
|
+
the index's `read_contract` meta key (accepts 1.x) when present; older
|
|
115
|
+
indexes are accepted from schema v4 up.
|
|
116
|
+
- The editor-window registry and open-command queue are **msnav-owned**
|
|
117
|
+
(coderag dropped this responsibility entirely): a sidecar
|
|
118
|
+
`msnav-windows.db` next to the index in the hub's storage dir, shared
|
|
119
|
+
through the same bind mount, so cross-container open-routing between msnav
|
|
120
|
+
daemons keeps working. msnav versions it itself (`PRAGMA user_version`).
|
|
121
|
+
It is ephemeral liveness state; deleting it is safe.
|
|
116
122
|
- Ruby ≥ 2.6; gems: `sqlite3`, `webrick`.
|
|
117
123
|
- The Docker-Desktop caveat from coderag applies here too: SQLite (WAL) over
|
|
118
124
|
bind mounts with several concurrent writers is the sensitive spot; msnav
|
|
119
|
-
|
|
125
|
+
writes only to its small sidecar, never to the index.
|
|
120
126
|
|
|
121
127
|
## Troubleshooting
|
|
122
128
|
|
data/lib/msnav/server.rb
CHANGED
|
@@ -11,7 +11,8 @@ module Msnav
|
|
|
11
11
|
# host-side `coderag index` or a peer daemon's rebuild hot-reloads every
|
|
12
12
|
# msnav on the hub within a second, requests always see a consistent index.
|
|
13
13
|
class Holder
|
|
14
|
-
attr_reader :cfg, :store, :windows, :
|
|
14
|
+
attr_reader :cfg, :store, :windows, :windows_store, :service_root,
|
|
15
|
+
:service_name
|
|
15
16
|
|
|
16
17
|
def initialize(cfg, service_root: nil, service_name: nil)
|
|
17
18
|
@cfg = cfg
|
|
@@ -21,7 +22,12 @@ module Msnav
|
|
|
21
22
|
@service_root = service_root
|
|
22
23
|
@service_name = service_name
|
|
23
24
|
@store = Store.new(cfg.db_path)
|
|
24
|
-
|
|
25
|
+
# coderag no longer carries window state at all — the registry is
|
|
26
|
+
# msnav-owned, in a sidecar DB inside the same shared storage dir, so
|
|
27
|
+
# every msnav daemon on the hub (one per container) routes opens
|
|
28
|
+
# through the mount exactly like before
|
|
29
|
+
@windows_store = WindowsStore.new(cfg.storage_dir + "msnav-windows.db")
|
|
30
|
+
@windows = WindowRegistry.new(@windows_store)
|
|
25
31
|
@mutex = Mutex.new
|
|
26
32
|
@generation = 0
|
|
27
33
|
@db_generation = @store.index_generation
|
data/lib/msnav/store.rb
CHANGED
|
@@ -4,17 +4,18 @@ require "pathname"
|
|
|
4
4
|
require "sqlite3"
|
|
5
5
|
|
|
6
6
|
module Msnav
|
|
7
|
-
#
|
|
7
|
+
# Read-only access to the shared coderag SQLite index.
|
|
8
8
|
#
|
|
9
|
-
# coderag (Python) owns the schema
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
9
|
+
# coderag (Python) owns the index; since its schema v5 the DB is a pure
|
|
10
|
+
# reader contract (meta key `read_contract`, currently 1.x) and holds no
|
|
11
|
+
# mutable daemon state — msnav therefore opens it strictly read-only and
|
|
12
|
+
# never writes to it. The window registry lives elsewhere (WindowsStore).
|
|
13
|
+
#
|
|
14
|
+
# One connection guarded by a Mutex: navigation runs on the in-memory
|
|
15
|
+
# graph, so the DB sees only generation polls and graph reloads.
|
|
16
16
|
class Store
|
|
17
|
-
SCHEMA_VERSION = 4
|
|
17
|
+
SCHEMA_VERSION = 4 # oldest index schema msnav understands
|
|
18
|
+
READ_CONTRACT_MAJOR = 1 # newest reader contract msnav is written against
|
|
18
19
|
|
|
19
20
|
attr_reader :path
|
|
20
21
|
|
|
@@ -62,36 +63,104 @@ module Msnav
|
|
|
62
63
|
end
|
|
63
64
|
end
|
|
64
65
|
|
|
66
|
+
def has_table?(name)
|
|
67
|
+
with_conn do |db|
|
|
68
|
+
!db.get_first_row(
|
|
69
|
+
"SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?",
|
|
70
|
+
[name]).nil?
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
65
74
|
private
|
|
66
75
|
|
|
67
76
|
def connect
|
|
68
|
-
db = SQLite3::Database.new(@path.to_s)
|
|
77
|
+
db = SQLite3::Database.new(@path.to_s, readonly: true)
|
|
69
78
|
db.busy_timeout = 5000
|
|
70
|
-
db.execute("PRAGMA foreign_keys=ON")
|
|
71
79
|
db
|
|
72
80
|
rescue SQLite3::Exception => e
|
|
73
81
|
raise IndexStaleError, "#{@path} could not be opened (#{e.message})"
|
|
74
82
|
end
|
|
75
83
|
|
|
84
|
+
# Compatibility gate. Newer coderag publishes `read_contract` (the
|
|
85
|
+
# reader-facing surface version) — that is the authoritative check.
|
|
86
|
+
# Older indexes are accepted from schema v4 up.
|
|
76
87
|
def check_version
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
contract = begin
|
|
89
|
+
get_meta("read_contract")
|
|
79
90
|
rescue SQLite3::Exception => e
|
|
80
91
|
raise IndexStaleError,
|
|
81
92
|
"#{@path} is not a coderag database (#{e.message}) — " \
|
|
82
93
|
"delete it and run `coderag index` on the host"
|
|
83
94
|
end
|
|
84
|
-
|
|
95
|
+
if contract
|
|
96
|
+
major = contract.split(".").first.to_i
|
|
97
|
+
return if major == READ_CONTRACT_MAJOR
|
|
98
|
+
raise IndexStaleError,
|
|
99
|
+
"#{@path} publishes read contract #{contract}, msnav is written " \
|
|
100
|
+
"against #{READ_CONTRACT_MAJOR}.x — upgrade msnav"
|
|
101
|
+
end
|
|
102
|
+
version = (get_meta("schema_version") || 0).to_i
|
|
85
103
|
if version < SCHEMA_VERSION
|
|
86
104
|
raise IndexStaleError,
|
|
87
|
-
"#{@path} is schema v#{version}, msnav needs v#{SCHEMA_VERSION} — " \
|
|
105
|
+
"#{@path} is schema v#{version}, msnav needs v#{SCHEMA_VERSION}+ — " \
|
|
88
106
|
"run `coderag index` on the host to migrate"
|
|
89
107
|
end
|
|
90
108
|
return if version == SCHEMA_VERSION
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
warn "msnav: #{@path} is schema v#{version} without a read_contract " \
|
|
110
|
+
"(msnav is written against v#{SCHEMA_VERSION}) — continuing, " \
|
|
111
|
+
"but upgrade msnav if navigation misbehaves"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Writable sidecar for msnav's own mutable state: the editor-window
|
|
116
|
+
# registry and open-command queue. coderag dropped this responsibility
|
|
117
|
+
# entirely (its index is a read-only contract now), so msnav owns the
|
|
118
|
+
# storage — and versions it itself via PRAGMA user_version. Lives next to
|
|
119
|
+
# the index in the hub's storage dir, shared through the same bind mount,
|
|
120
|
+
# so every msnav daemon on the hub sees the same windows. Created on
|
|
121
|
+
# demand; safe to delete (ephemeral liveness state).
|
|
122
|
+
class WindowsStore
|
|
123
|
+
USER_VERSION = 1
|
|
124
|
+
|
|
125
|
+
DDL = [
|
|
126
|
+
"CREATE TABLE IF NOT EXISTS windows (" \
|
|
127
|
+
"window_id TEXT PRIMARY KEY, authority TEXT NOT NULL DEFAULT ''," \
|
|
128
|
+
"roots TEXT NOT NULL, path_mappings TEXT NOT NULL," \
|
|
129
|
+
"host_roots TEXT NOT NULL, seq INTEGER NOT NULL, last_poll REAL NOT NULL)",
|
|
130
|
+
"CREATE TABLE IF NOT EXISTS window_commands (" \
|
|
131
|
+
"id INTEGER PRIMARY KEY AUTOINCREMENT, window_id TEXT NOT NULL," \
|
|
132
|
+
"payload TEXT NOT NULL, created_at REAL NOT NULL)",
|
|
133
|
+
"CREATE INDEX IF NOT EXISTS idx_window_commands_window " \
|
|
134
|
+
"ON window_commands(window_id)",
|
|
135
|
+
].freeze
|
|
136
|
+
|
|
137
|
+
attr_reader :path
|
|
138
|
+
|
|
139
|
+
def initialize(path)
|
|
140
|
+
@path = Pathname.new(path.to_s)
|
|
141
|
+
@path.parent.mkpath
|
|
142
|
+
@mutex = Mutex.new
|
|
143
|
+
@conn = SQLite3::Database.new(@path.to_s)
|
|
144
|
+
@conn.busy_timeout = 5000
|
|
145
|
+
@conn.execute("PRAGMA journal_mode=WAL")
|
|
146
|
+
version = @conn.get_first_value("PRAGMA user_version").to_i
|
|
147
|
+
if version > USER_VERSION
|
|
148
|
+
raise IndexStaleError,
|
|
149
|
+
"#{@path} was created by a newer msnav (registry v#{version}, " \
|
|
150
|
+
"this msnav understands v#{USER_VERSION}) — upgrade msnav"
|
|
151
|
+
end
|
|
152
|
+
DDL.each { |stmt| @conn.execute(stmt) }
|
|
153
|
+
@conn.execute("PRAGMA user_version = #{USER_VERSION}") if version.zero?
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def with_conn
|
|
157
|
+
@mutex.synchronize { yield @conn }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def close
|
|
161
|
+
@mutex.synchronize do
|
|
162
|
+
@conn.close unless @conn.closed?
|
|
163
|
+
end
|
|
95
164
|
end
|
|
96
165
|
end
|
|
97
166
|
end
|
data/lib/msnav/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: msnav
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Zahoruiko
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sqlite3
|