-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiscovery.lua
More file actions
246 lines (199 loc) · 6.61 KB
/
Copy pathdiscovery.lua
File metadata and controls
246 lines (199 loc) · 6.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
-- Service discovery utilities for diffusion.nvim
-- Handles lock files and service detection for Claude and OpenCode
local Discovery = {}
function Discovery:new(service_type)
local instance = {
_service_type = service_type or "generic",
_logger = require('diffusion.utils.logger'):child("Discovery"),
_file_utils = require('diffusion.utils.file'),
_lock_files = {}
}
setmetatable(instance, { __index = self })
return instance
end
-- Create lock file for service discovery
function Discovery:create_lock_file(port, data, service_name)
service_name = service_name or self._service_type
local lock_dir = self:_get_lock_directory(service_name)
self._file_utils.ensure_dir(lock_dir)
local lock_file_name = string.format("%s_%d_%d.lock",
service_name, vim.fn.getpid(), port)
local lock_file_path = lock_dir .. "/" .. lock_file_name
local lock_data = vim.tbl_extend("force", {
pid = vim.fn.getpid(),
port = port,
service = service_name,
created_at = os.time(),
version = "1.0.0"
}, data or {})
local json_content = vim.fn.json_encode(lock_data)
local success, error = self._file_utils.write_file(lock_file_path, json_content)
if success then
self._lock_files[lock_file_path] = lock_data
self._logger:info("Created lock file", {
path = lock_file_path,
service = service_name
})
else
self._logger:error("Failed to create lock file", {
path = lock_file_path,
error = error
})
end
return success
end
-- Get lock directory for service
function Discovery:_get_lock_directory(service_name)
if service_name == "claude" then
return vim.fn.expand("~/.claude/ide")
elseif service_name == "opencode" then
return vim.fn.expand("~/.opencode/nvim")
elseif service_name == "codex" then
return vim.fn.expand("~/.codex/ide")
elseif service_name == "pi" then
return vim.fn.expand("~/.pi/ide")
else
return vim.fn.stdpath("data") .. "/diffusion/" .. service_name
end
end
-- Read lock file
function Discovery:read_lock_file(lock_file_path)
if not self._file_utils.file_exists(lock_file_path) then
return nil, "Lock file does not exist"
end
local content, error = self._file_utils.read_file(lock_file_path)
if not content then
return nil, error
end
local success, lock_data = pcall(vim.fn.json_decode, content)
if not success then
return nil, "Invalid JSON in lock file"
end
return lock_data
end
-- Find active lock files for service
function Discovery:find_active_lock_files(service_name)
service_name = service_name or self._service_type
local lock_dir = self:_get_lock_directory(service_name)
if not self._file_utils.is_directory(lock_dir) then
return {}
end
local files = self._file_utils.list_directory(lock_dir, ".*%.lock$")
if not files then
return {}
end
local active_locks = {}
for _, file_info in ipairs(files) do
local lock_data, error = self:read_lock_file(file_info.path)
if lock_data then
-- Check if process is still running
if self:_is_process_alive(lock_data.pid) then
table.insert(active_locks, {
path = file_info.path,
data = lock_data
})
else
-- Clean up stale lock file
self:cleanup_lock_file(file_info.path)
end
end
end
return active_locks
end
-- Check if process is still alive
function Discovery:_is_process_alive(pid)
if not pid or type(pid) ~= "number" or pid <= 0 then
return false
end
local utils = require('diffusion.utils')
return utils.is_process_alive(pid)
end
-- Cleanup specific lock file
function Discovery:cleanup_lock_file(lock_file_path)
if self._file_utils.file_exists(lock_file_path) then
local success = os.remove(lock_file_path)
if success then
self._lock_files[lock_file_path] = nil
self._logger:debug("Cleaned up lock file", { path = lock_file_path })
end
return success
end
return true
end
-- Cleanup all lock files for this instance
function Discovery:cleanup()
for lock_file_path, _ in pairs(self._lock_files) do
self:cleanup_lock_file(lock_file_path)
end
self._lock_files = {}
self._logger:info("Discovery cleanup completed")
end
-- Get service info from environment or lock files
function Discovery:get_service_info(service_name)
service_name = service_name or self._service_type
local info = {}
-- Check environment variables first
if service_name == "claude" then
info.port = vim.env.CLAUDE_CODE_SSE_PORT
info.enabled = vim.env.ENABLE_IDE_INTEGRATION == "true"
elseif service_name == "opencode" then
info.port = vim.env.OPENCODE_WEBSOCKET_PORT
info.secret = vim.env.OPENCODE_WEBSOCKET_SECRET
info.enabled = vim.env.ENABLE_IDE_INTEGRATION == "true"
end
-- Check for active lock files
local active_locks = self:find_active_lock_files(service_name)
if #active_locks > 0 then
-- Use the most recent lock file
table.sort(active_locks, function(a, b)
return (a.data.created_at or 0) > (b.data.created_at or 0)
end)
local latest_lock = active_locks[1].data
info.port = info.port or latest_lock.port
info.pid = latest_lock.pid
info.available = true
else
info.available = info.port ~= nil
end
return info
end
-- Check if service is available
function Discovery:is_service_available(service_name)
local info = self:get_service_info(service_name)
return info.available == true
end
-- Get all available services
function Discovery:get_available_services()
local services = {}
for _, service in ipairs({"claude", "opencode", "codex"}) do
if self:is_service_available(service) then
services[service] = self:get_service_info(service)
end
end
return services
end
-- Clean up stale lock files for all services
function Discovery:cleanup_stale_lock_files()
local cleaned_count = 0
for _, service in ipairs({"claude", "opencode", "codex"}) do
local lock_dir = self:_get_lock_directory(service)
if self._file_utils.is_directory(lock_dir) then
local files = self._file_utils.list_directory(lock_dir, ".*%.lock$")
if files then
for _, file_info in ipairs(files) do
local lock_data, _ = self:read_lock_file(file_info.path)
if lock_data and not self:_is_process_alive(lock_data.pid) then
if self:cleanup_lock_file(file_info.path) then
cleaned_count = cleaned_count + 1
end
end
end
end
end
end
if cleaned_count > 0 then
self._logger:info("Cleaned up stale lock files", { count = cleaned_count })
end
return cleaned_count
end
return Discovery