oh-my-claude-sisyphus 3.2.5 → 3.3.1
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/README.md +37 -2
- package/agents/scientist-high.md +1003 -0
- package/agents/scientist-low.md +232 -0
- package/agents/scientist.md +1180 -0
- package/bridge/__pycache__/gyoshu_bridge.cpython-310.pyc +0 -0
- package/bridge/gyoshu_bridge.py +846 -0
- package/commands/research.md +511 -0
- package/dist/agents/definitions.d.ts +9 -0
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +25 -0
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/index.d.ts +2 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/scientist.d.ts +16 -0
- package/dist/agents/scientist.d.ts.map +1 -0
- package/dist/agents/scientist.js +370 -0
- package/dist/agents/scientist.js.map +1 -0
- package/dist/lib/atomic-write.d.ts +29 -0
- package/dist/lib/atomic-write.d.ts.map +1 -0
- package/dist/lib/atomic-write.js +111 -0
- package/dist/lib/atomic-write.js.map +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/python-repl/bridge-manager.d.ts +65 -0
- package/dist/tools/python-repl/bridge-manager.d.ts.map +1 -0
- package/dist/tools/python-repl/bridge-manager.js +478 -0
- package/dist/tools/python-repl/bridge-manager.js.map +1 -0
- package/dist/tools/python-repl/index.d.ts +40 -0
- package/dist/tools/python-repl/index.d.ts.map +1 -0
- package/dist/tools/python-repl/index.js +36 -0
- package/dist/tools/python-repl/index.js.map +1 -0
- package/dist/tools/python-repl/paths.d.ts +84 -0
- package/dist/tools/python-repl/paths.d.ts.map +1 -0
- package/dist/tools/python-repl/paths.js +213 -0
- package/dist/tools/python-repl/paths.js.map +1 -0
- package/dist/tools/python-repl/session-lock.d.ts +111 -0
- package/dist/tools/python-repl/session-lock.d.ts.map +1 -0
- package/dist/tools/python-repl/session-lock.js +510 -0
- package/dist/tools/python-repl/session-lock.js.map +1 -0
- package/dist/tools/python-repl/socket-client.d.ts +42 -0
- package/dist/tools/python-repl/socket-client.d.ts.map +1 -0
- package/dist/tools/python-repl/socket-client.js +157 -0
- package/dist/tools/python-repl/socket-client.js.map +1 -0
- package/dist/tools/python-repl/tool.d.ts +100 -0
- package/dist/tools/python-repl/tool.d.ts.map +1 -0
- package/dist/tools/python-repl/tool.js +575 -0
- package/dist/tools/python-repl/tool.js.map +1 -0
- package/dist/tools/python-repl/types.d.ts +95 -0
- package/dist/tools/python-repl/types.d.ts.map +1 -0
- package/dist/tools/python-repl/types.js +2 -0
- package/dist/tools/python-repl/types.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Lock - Cross-platform file-based session locking
|
|
3
|
+
*
|
|
4
|
+
* Provides single-writer enforcement per session with:
|
|
5
|
+
* - PID-reuse safety via process start time verification
|
|
6
|
+
* - Cross-platform support (Linux, macOS)
|
|
7
|
+
* - Stale lock detection and safe breaking
|
|
8
|
+
* - Request queuing with timeout
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from 'fs/promises';
|
|
11
|
+
import * as fsSync from 'fs';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import * as os from 'os';
|
|
14
|
+
import * as crypto from 'crypto';
|
|
15
|
+
import { execFile } from 'child_process';
|
|
16
|
+
import { promisify } from 'util';
|
|
17
|
+
import { ensureDirSync } from '../../lib/atomic-write.js';
|
|
18
|
+
import { getSessionLockPath } from './paths.js';
|
|
19
|
+
const execFileAsync = promisify(execFile);
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// CONSTANTS
|
|
22
|
+
// =============================================================================
|
|
23
|
+
const STALE_LOCK_AGE_MS = 60000; // 60 seconds
|
|
24
|
+
const DEFAULT_ACQUIRE_TIMEOUT_MS = 30000; // 30 seconds
|
|
25
|
+
const LOCK_RETRY_INTERVAL_MS = 100; // 100ms between retries
|
|
26
|
+
const REMOTE_LOCK_STALE_AGE_MS = 300000; // 5 minutes for remote locks
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// ERRORS
|
|
29
|
+
// =============================================================================
|
|
30
|
+
export class LockTimeoutError extends Error {
|
|
31
|
+
lockPath;
|
|
32
|
+
timeout;
|
|
33
|
+
lastHolder;
|
|
34
|
+
constructor(lockPath, timeout, lastHolder) {
|
|
35
|
+
super(`Failed to acquire lock within ${timeout}ms. ` +
|
|
36
|
+
(lastHolder
|
|
37
|
+
? `Held by PID ${lastHolder.pid} on ${lastHolder.hostname} since ${lastHolder.acquiredAt}`
|
|
38
|
+
: 'Unknown holder') +
|
|
39
|
+
`. Lock path: ${lockPath}`);
|
|
40
|
+
this.lockPath = lockPath;
|
|
41
|
+
this.timeout = timeout;
|
|
42
|
+
this.lastHolder = lastHolder;
|
|
43
|
+
this.name = 'LockTimeoutError';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export class LockError extends Error {
|
|
47
|
+
constructor(message) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = 'LockError';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// PID VALIDATION
|
|
54
|
+
// =============================================================================
|
|
55
|
+
/**
|
|
56
|
+
* Validate that a PID is a positive integer.
|
|
57
|
+
* Defense in depth against command injection via poisoned lock files.
|
|
58
|
+
*/
|
|
59
|
+
function isValidPid(pid) {
|
|
60
|
+
return typeof pid === 'number' && Number.isInteger(pid) && pid > 0;
|
|
61
|
+
}
|
|
62
|
+
// =============================================================================
|
|
63
|
+
// PROCESS START TIME DETECTION
|
|
64
|
+
// =============================================================================
|
|
65
|
+
/**
|
|
66
|
+
* Get process start time on Linux via /proc/{pid}/stat field 22.
|
|
67
|
+
* The stat format is: pid (comm) state ppid... with field 22 being starttime.
|
|
68
|
+
* Command names can contain spaces/parens, so we parse from the last ')'.
|
|
69
|
+
*
|
|
70
|
+
* Returns clock ticks since boot (jiffies), or undefined if unavailable.
|
|
71
|
+
*/
|
|
72
|
+
async function getProcessStartTimeLinux(pid) {
|
|
73
|
+
try {
|
|
74
|
+
const stat = await fs.readFile(`/proc/${pid}/stat`, 'utf8');
|
|
75
|
+
const closeParen = stat.lastIndexOf(')');
|
|
76
|
+
if (closeParen === -1)
|
|
77
|
+
return undefined;
|
|
78
|
+
// Fields after comm: state(1) ppid(2) ... starttime(20) - 0-indexed after split
|
|
79
|
+
const fieldsAfterComm = stat.substring(closeParen + 2).split(' ');
|
|
80
|
+
// starttime is field 22 in /proc/pid/stat, which is index 19 after removing pid and comm
|
|
81
|
+
const startTimeField = fieldsAfterComm[19];
|
|
82
|
+
if (!startTimeField)
|
|
83
|
+
return undefined;
|
|
84
|
+
const startTime = parseInt(startTimeField, 10);
|
|
85
|
+
return isNaN(startTime) ? undefined : startTime;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get process start time on macOS via `ps -p {pid} -o lstart=`.
|
|
93
|
+
* Returns Unix timestamp in ms, or undefined if unavailable.
|
|
94
|
+
*/
|
|
95
|
+
async function getProcessStartTimeMacOS(pid) {
|
|
96
|
+
if (!isValidPid(pid))
|
|
97
|
+
return undefined;
|
|
98
|
+
try {
|
|
99
|
+
const { stdout } = await execFileAsync('ps', ['-p', String(pid), '-o', 'lstart='], {
|
|
100
|
+
env: { ...process.env, LC_ALL: 'C' },
|
|
101
|
+
});
|
|
102
|
+
const lstart = stdout.trim();
|
|
103
|
+
if (!lstart)
|
|
104
|
+
return undefined;
|
|
105
|
+
const date = new Date(lstart);
|
|
106
|
+
if (isNaN(date.getTime()))
|
|
107
|
+
return undefined;
|
|
108
|
+
return date.getTime();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the start time of the current process.
|
|
116
|
+
* Used when creating lock files to enable PID reuse detection.
|
|
117
|
+
*/
|
|
118
|
+
export async function getCurrentProcessStartTime() {
|
|
119
|
+
const pid = process.pid;
|
|
120
|
+
if (process.platform === 'linux') {
|
|
121
|
+
return getProcessStartTimeLinux(pid);
|
|
122
|
+
}
|
|
123
|
+
else if (process.platform === 'darwin') {
|
|
124
|
+
return getProcessStartTimeMacOS(pid);
|
|
125
|
+
}
|
|
126
|
+
// Unsupported platform - return undefined
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
// =============================================================================
|
|
130
|
+
// PROCESS LIVENESS DETECTION
|
|
131
|
+
// =============================================================================
|
|
132
|
+
/**
|
|
133
|
+
* Check if a process is alive with PID-reuse detection via start time comparison.
|
|
134
|
+
*
|
|
135
|
+
* @param pid - Process ID to check
|
|
136
|
+
* @param recordedStartTime - Start time recorded when lock was acquired
|
|
137
|
+
* @returns true if process is alive AND start time matches (or wasn't recorded)
|
|
138
|
+
*/
|
|
139
|
+
export async function isProcessAlive(pid, recordedStartTime) {
|
|
140
|
+
if (!isValidPid(pid))
|
|
141
|
+
return false;
|
|
142
|
+
if (process.platform === 'linux') {
|
|
143
|
+
const currentStartTime = await getProcessStartTimeLinux(pid);
|
|
144
|
+
if (currentStartTime === undefined)
|
|
145
|
+
return false;
|
|
146
|
+
// If we have a recorded start time, verify it matches
|
|
147
|
+
if (recordedStartTime !== undefined && currentStartTime !== recordedStartTime) {
|
|
148
|
+
return false; // PID reuse detected
|
|
149
|
+
}
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
else if (process.platform === 'darwin') {
|
|
153
|
+
try {
|
|
154
|
+
// First check if process exists
|
|
155
|
+
const { stdout } = await execFileAsync('ps', ['-p', String(pid), '-o', 'pid='], {
|
|
156
|
+
env: { ...process.env, LC_ALL: 'C' },
|
|
157
|
+
});
|
|
158
|
+
if (stdout.trim() === '')
|
|
159
|
+
return false;
|
|
160
|
+
// If we have a recorded start time, verify it matches
|
|
161
|
+
if (recordedStartTime !== undefined) {
|
|
162
|
+
const currentStartTime = await getProcessStartTimeMacOS(pid);
|
|
163
|
+
// Fail-closed: if we can't get current start time but we have a recorded one,
|
|
164
|
+
// assume PID reuse has occurred (safer than assuming same process)
|
|
165
|
+
if (currentStartTime === undefined) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
if (currentStartTime !== recordedStartTime) {
|
|
169
|
+
return false; // PID reuse detected
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Unknown platform: conservative assumption that process is alive
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
// =============================================================================
|
|
182
|
+
// SYMLINK-SAFE FILE OPERATIONS
|
|
183
|
+
// =============================================================================
|
|
184
|
+
/**
|
|
185
|
+
* Open a file with O_NOFOLLOW to prevent symlink attacks.
|
|
186
|
+
* Falls back to lstat check on platforms that don't support O_NOFOLLOW.
|
|
187
|
+
*/
|
|
188
|
+
async function openNoFollow(filePath, flags, mode) {
|
|
189
|
+
// Add O_NOFOLLOW if available (Linux, macOS)
|
|
190
|
+
const O_NOFOLLOW = fsSync.constants.O_NOFOLLOW ?? 0x20000;
|
|
191
|
+
const flagsWithNoFollow = flags | O_NOFOLLOW;
|
|
192
|
+
try {
|
|
193
|
+
return await fs.open(filePath, flagsWithNoFollow, mode);
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
// ELOOP means it's a symlink - reject it
|
|
197
|
+
if (err.code === 'ELOOP') {
|
|
198
|
+
throw new LockError(`Lock file is a symlink: ${filePath}`);
|
|
199
|
+
}
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Read a file safely, rejecting symlinks.
|
|
205
|
+
*/
|
|
206
|
+
async function readFileNoFollow(filePath) {
|
|
207
|
+
// First check if it's a symlink via lstat
|
|
208
|
+
try {
|
|
209
|
+
const stat = await fs.lstat(filePath);
|
|
210
|
+
if (stat.isSymbolicLink()) {
|
|
211
|
+
throw new LockError(`Lock file is a symlink: ${filePath}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
if (err.code === 'ENOENT') {
|
|
216
|
+
throw err; // File doesn't exist - propagate
|
|
217
|
+
}
|
|
218
|
+
if (err instanceof LockError) {
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
// Other errors - let readFile handle them
|
|
222
|
+
}
|
|
223
|
+
return fs.readFile(filePath, 'utf8');
|
|
224
|
+
}
|
|
225
|
+
// =============================================================================
|
|
226
|
+
// LOCK FILE OPERATIONS
|
|
227
|
+
// =============================================================================
|
|
228
|
+
/**
|
|
229
|
+
* Read and validate a lock file.
|
|
230
|
+
* Returns null if file doesn't exist, is invalid, or is a symlink.
|
|
231
|
+
*/
|
|
232
|
+
async function readLockFile(lockPath) {
|
|
233
|
+
try {
|
|
234
|
+
const content = await readFileNoFollow(lockPath);
|
|
235
|
+
const lockInfo = JSON.parse(content);
|
|
236
|
+
// Validate required fields
|
|
237
|
+
if (!lockInfo.lockId ||
|
|
238
|
+
!isValidPid(lockInfo.pid) ||
|
|
239
|
+
!lockInfo.hostname ||
|
|
240
|
+
!lockInfo.acquiredAt) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return lockInfo;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// ENOENT = doesn't exist, ELOOP = symlink rejected, or parse error
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Create a new LockInfo for the current process.
|
|
252
|
+
*/
|
|
253
|
+
async function createLockInfo(lockId) {
|
|
254
|
+
return {
|
|
255
|
+
lockId,
|
|
256
|
+
pid: process.pid,
|
|
257
|
+
processStartTime: await getCurrentProcessStartTime(),
|
|
258
|
+
hostname: os.hostname(),
|
|
259
|
+
acquiredAt: new Date().toISOString(),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Check if a lock can be safely broken. A lock is breakable if:
|
|
264
|
+
* - Age > 60 seconds AND owning process is dead OR start time differs (PID reuse)
|
|
265
|
+
* - For remote hosts: Only breaks if age > 5 minutes
|
|
266
|
+
*/
|
|
267
|
+
async function canBreakLock(lockInfo) {
|
|
268
|
+
const age = Date.now() - new Date(lockInfo.acquiredAt).getTime();
|
|
269
|
+
// Lock is too fresh to break
|
|
270
|
+
if (age < STALE_LOCK_AGE_MS) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
// For remote hosts, require much longer timeout
|
|
274
|
+
if (lockInfo.hostname !== os.hostname()) {
|
|
275
|
+
return age > REMOTE_LOCK_STALE_AGE_MS;
|
|
276
|
+
}
|
|
277
|
+
// Check if owning process is still alive with same start time
|
|
278
|
+
const alive = await isProcessAlive(lockInfo.pid, lockInfo.processStartTime);
|
|
279
|
+
return !alive;
|
|
280
|
+
}
|
|
281
|
+
// =============================================================================
|
|
282
|
+
// SESSION LOCK CLASS
|
|
283
|
+
// =============================================================================
|
|
284
|
+
/**
|
|
285
|
+
* SessionLock manages a single lock file for session coordination.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* const lock = new SessionLock('my-session-id');
|
|
289
|
+
* try {
|
|
290
|
+
* await lock.acquire();
|
|
291
|
+
* // ... do work ...
|
|
292
|
+
* } finally {
|
|
293
|
+
* await lock.release();
|
|
294
|
+
* }
|
|
295
|
+
*/
|
|
296
|
+
export class SessionLock {
|
|
297
|
+
lockPath;
|
|
298
|
+
lockId;
|
|
299
|
+
held = false;
|
|
300
|
+
lockInfo = null;
|
|
301
|
+
constructor(sessionId) {
|
|
302
|
+
this.lockPath = getSessionLockPath(sessionId);
|
|
303
|
+
this.lockId = crypto.randomUUID();
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Acquire lock with timeout (default 30s).
|
|
307
|
+
* Blocks until lock is acquired or timeout is reached.
|
|
308
|
+
*
|
|
309
|
+
* @param timeout - Maximum time to wait in milliseconds
|
|
310
|
+
* @throws LockTimeoutError if lock cannot be acquired within timeout
|
|
311
|
+
*/
|
|
312
|
+
async acquire(timeout = DEFAULT_ACQUIRE_TIMEOUT_MS) {
|
|
313
|
+
if (this.held) {
|
|
314
|
+
throw new LockError('Lock already held by this instance');
|
|
315
|
+
}
|
|
316
|
+
const startTime = Date.now();
|
|
317
|
+
let lastHolder;
|
|
318
|
+
while (Date.now() - startTime < timeout) {
|
|
319
|
+
const result = await this.tryAcquire();
|
|
320
|
+
if (result.acquired) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (result.holder) {
|
|
324
|
+
lastHolder = result.holder;
|
|
325
|
+
}
|
|
326
|
+
await sleep(LOCK_RETRY_INTERVAL_MS);
|
|
327
|
+
}
|
|
328
|
+
throw new LockTimeoutError(this.lockPath, timeout, lastHolder);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Try to acquire lock (non-blocking).
|
|
332
|
+
* Returns immediately with result indicating success or failure.
|
|
333
|
+
*/
|
|
334
|
+
async tryAcquire() {
|
|
335
|
+
try {
|
|
336
|
+
const existingLock = await readLockFile(this.lockPath);
|
|
337
|
+
if (existingLock) {
|
|
338
|
+
// Check if we can break the stale lock
|
|
339
|
+
if (await canBreakLock(existingLock)) {
|
|
340
|
+
try {
|
|
341
|
+
await fs.unlink(this.lockPath);
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
// Lock might have been removed by another process
|
|
345
|
+
}
|
|
346
|
+
// Fall through to acquire
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
return {
|
|
350
|
+
acquired: false,
|
|
351
|
+
reason: 'held_by_other',
|
|
352
|
+
holder: existingLock,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Create new lock info
|
|
357
|
+
const newLockInfo = await createLockInfo(this.lockId);
|
|
358
|
+
try {
|
|
359
|
+
// Ensure directory exists
|
|
360
|
+
ensureDirSync(path.dirname(this.lockPath));
|
|
361
|
+
// Atomic exclusive create with O_NOFOLLOW
|
|
362
|
+
const flags = fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL;
|
|
363
|
+
const lockFile = await openNoFollow(this.lockPath, flags, 0o644);
|
|
364
|
+
try {
|
|
365
|
+
await lockFile.writeFile(JSON.stringify(newLockInfo, null, 2), { encoding: 'utf8' });
|
|
366
|
+
await lockFile.sync();
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
await lockFile.close();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
catch (err) {
|
|
373
|
+
if (err.code === 'EEXIST') {
|
|
374
|
+
// Another process created the lock file first
|
|
375
|
+
return {
|
|
376
|
+
acquired: false,
|
|
377
|
+
reason: 'held_by_other',
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
throw err;
|
|
381
|
+
}
|
|
382
|
+
// Verify our lock wasn't overwritten (race condition check)
|
|
383
|
+
const verifyLock = await readLockFile(this.lockPath);
|
|
384
|
+
if (!verifyLock || verifyLock.lockId !== this.lockId) {
|
|
385
|
+
return {
|
|
386
|
+
acquired: false,
|
|
387
|
+
reason: 'error',
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
this.held = true;
|
|
391
|
+
this.lockInfo = newLockInfo;
|
|
392
|
+
return {
|
|
393
|
+
acquired: true,
|
|
394
|
+
reason: existingLock ? 'stale_broken' : 'success',
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
catch (err) {
|
|
398
|
+
return {
|
|
399
|
+
acquired: false,
|
|
400
|
+
reason: 'error',
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Release held lock.
|
|
406
|
+
* Safe to call multiple times - subsequent calls are no-ops.
|
|
407
|
+
*/
|
|
408
|
+
async release() {
|
|
409
|
+
if (!this.held) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
// Verify we still own the lock before deleting
|
|
414
|
+
const currentLock = await readLockFile(this.lockPath);
|
|
415
|
+
if (currentLock && currentLock.lockId === this.lockId) {
|
|
416
|
+
await fs.unlink(this.lockPath);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// Ignore errors (lock might already be gone)
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
this.held = false;
|
|
424
|
+
this.lockInfo = null;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Force break a stale lock.
|
|
429
|
+
* USE WITH CAUTION: This will break the lock regardless of who holds it.
|
|
430
|
+
* Should only be used for recovery from known stale states.
|
|
431
|
+
*/
|
|
432
|
+
async forceBreak() {
|
|
433
|
+
try {
|
|
434
|
+
await fs.unlink(this.lockPath);
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
if (err.code !== 'ENOENT') {
|
|
438
|
+
throw err;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
this.held = false;
|
|
442
|
+
this.lockInfo = null;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Check if lock is held by us.
|
|
446
|
+
*/
|
|
447
|
+
isHeld() {
|
|
448
|
+
return this.held;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Get the lock file path.
|
|
452
|
+
*/
|
|
453
|
+
getLockPath() {
|
|
454
|
+
return this.lockPath;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Get current lock info (if held).
|
|
458
|
+
*/
|
|
459
|
+
getLockInfo() {
|
|
460
|
+
return this.lockInfo;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// =============================================================================
|
|
464
|
+
// UTILITY FUNCTIONS
|
|
465
|
+
// =============================================================================
|
|
466
|
+
function sleep(ms) {
|
|
467
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Execute a function while holding a lock, releasing automatically on completion.
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* await withLock('session-id', async () => {
|
|
474
|
+
* // ... critical section ...
|
|
475
|
+
* });
|
|
476
|
+
*/
|
|
477
|
+
export async function withLock(sessionId, fn, timeout = DEFAULT_ACQUIRE_TIMEOUT_MS) {
|
|
478
|
+
const lock = new SessionLock(sessionId);
|
|
479
|
+
await lock.acquire(timeout);
|
|
480
|
+
try {
|
|
481
|
+
return await fn();
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
await lock.release();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Get the current status of a session lock.
|
|
489
|
+
*/
|
|
490
|
+
export async function getLockStatus(sessionId) {
|
|
491
|
+
const lockPath = getSessionLockPath(sessionId);
|
|
492
|
+
const lockInfo = await readLockFile(lockPath);
|
|
493
|
+
if (!lockInfo) {
|
|
494
|
+
return {
|
|
495
|
+
locked: false,
|
|
496
|
+
lockInfo: null,
|
|
497
|
+
canBreak: false,
|
|
498
|
+
ownedByUs: false,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
const canBreakResult = await canBreakLock(lockInfo);
|
|
502
|
+
const ownedByUs = lockInfo.pid === process.pid && lockInfo.hostname === os.hostname();
|
|
503
|
+
return {
|
|
504
|
+
locked: true,
|
|
505
|
+
lockInfo,
|
|
506
|
+
canBreak: canBreakResult,
|
|
507
|
+
ownedByUs,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
//# sourceMappingURL=session-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lock.js","sourceRoot":"","sources":["../../../src/tools/python-repl/session-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error types for socket communication
|
|
3
|
+
*/
|
|
4
|
+
export declare class SocketConnectionError extends Error {
|
|
5
|
+
readonly socketPath: string;
|
|
6
|
+
readonly originalError?: Error | undefined;
|
|
7
|
+
constructor(message: string, socketPath: string, originalError?: Error | undefined);
|
|
8
|
+
}
|
|
9
|
+
export declare class SocketTimeoutError extends Error {
|
|
10
|
+
readonly timeoutMs: number;
|
|
11
|
+
constructor(message: string, timeoutMs: number);
|
|
12
|
+
}
|
|
13
|
+
export declare class JsonRpcError extends Error {
|
|
14
|
+
readonly code: number;
|
|
15
|
+
readonly data?: unknown | undefined;
|
|
16
|
+
constructor(message: string, code: number, data?: unknown | undefined);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Send a JSON-RPC 2.0 request over Unix socket
|
|
20
|
+
*
|
|
21
|
+
* @param socketPath - Path to the Unix socket
|
|
22
|
+
* @param method - JSON-RPC method name
|
|
23
|
+
* @param params - Optional parameters object
|
|
24
|
+
* @param timeout - Request timeout in milliseconds (default: 60000ms / 1 min)
|
|
25
|
+
* @returns Promise resolving to the result typed as T
|
|
26
|
+
*
|
|
27
|
+
* @throws {SocketConnectionError} If socket connection fails
|
|
28
|
+
* @throws {SocketTimeoutError} If request times out
|
|
29
|
+
* @throws {JsonRpcError} If server returns an error response
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const result = await sendSocketRequest<ExecuteResult>(
|
|
34
|
+
* '/tmp/omc/abc123/bridge.sock',
|
|
35
|
+
* 'execute',
|
|
36
|
+
* { code: 'print("hello")' },
|
|
37
|
+
* 60000
|
|
38
|
+
* );
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function sendSocketRequest<T>(socketPath: string, method: string, params?: Record<string, unknown>, timeout?: number): Promise<T>;
|
|
42
|
+
//# sourceMappingURL=socket-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-client.d.ts","sourceRoot":"","sources":["../../../src/tools/python-repl/socket-client.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;aACD,UAAU,EAAE,MAAM;aAAkB,aAAa,CAAC,EAAE,KAAK;gBAA1F,OAAO,EAAE,MAAM,EAAkB,UAAU,EAAE,MAAM,EAAkB,aAAa,CAAC,EAAE,KAAK,YAAA;CAIvG;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aACE,SAAS,EAAE,MAAM;gBAAlD,OAAO,EAAE,MAAM,EAAkB,SAAS,EAAE,MAAM;CAI/D;AAED,qBAAa,YAAa,SAAQ,KAAK;aAGnB,IAAI,EAAE,MAAM;aACZ,IAAI,CAAC,EAAE,OAAO;gBAF9B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,YAAA;CAKjC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,GAAE,MAAc,GACtB,OAAO,CAAC,CAAC,CAAC,CA+IZ"}
|