specmem-hardwicksoftware 3.7.2 → 3.7.4
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/package.json +1 -1
- package/scripts/specmem-init.cjs +89 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specmem-hardwicksoftware",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Persistent memory system for coding sessions - semantic search with pgvector, token compression, team coordination, file watching. Needs root: installs system-wide hooks, manages docker/PostgreSQL, writes global configs, handles screen sessions. justcalljon.pro",
|
|
6
6
|
"main": "dist/index.js",
|
package/scripts/specmem-init.cjs
CHANGED
|
@@ -3596,7 +3596,8 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3596
3596
|
// Revalidate socket path after failures (may have been restarted externally)
|
|
3597
3597
|
async function revalidateSocket() {
|
|
3598
3598
|
const projectSocketPath = path.join(projectPath, 'specmem', 'sockets', 'embeddings.sock');
|
|
3599
|
-
const
|
|
3599
|
+
const projDirName = path.basename(projectPath).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
3600
|
+
const sharedSocketPath = path.join(os.homedir(), '.specmem', projDirName, 'sockets', 'embeddings.sock');
|
|
3600
3601
|
|
|
3601
3602
|
// Check project socket first (preferred)
|
|
3602
3603
|
if (fs.existsSync(projectSocketPath)) {
|
|
@@ -3609,7 +3610,7 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3609
3610
|
}
|
|
3610
3611
|
}
|
|
3611
3612
|
|
|
3612
|
-
// Fallback to shared socket
|
|
3613
|
+
// Fallback to shared socket (home dir)
|
|
3613
3614
|
if (fs.existsSync(sharedSocketPath)) {
|
|
3614
3615
|
activeSocketPath = sharedSocketPath;
|
|
3615
3616
|
const healthy = await checkSocketHealth();
|
|
@@ -3620,6 +3621,70 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3620
3621
|
}
|
|
3621
3622
|
}
|
|
3622
3623
|
|
|
3624
|
+
// Fallback to /tmp shared sockets
|
|
3625
|
+
for (let i = 0; i < 5; i++) {
|
|
3626
|
+
const tmpSocket = `/tmp/specmem-embed-${i}.sock`;
|
|
3627
|
+
if (fs.existsSync(tmpSocket)) {
|
|
3628
|
+
activeSocketPath = tmpSocket;
|
|
3629
|
+
const healthy = await checkSocketHealth();
|
|
3630
|
+
if (healthy) {
|
|
3631
|
+
initLog(`Revalidated socket: using /tmp shared socket at ${tmpSocket}`);
|
|
3632
|
+
consecutiveEmbeddingFailures = 0;
|
|
3633
|
+
return true;
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
// AUTO-RESTART: If no socket found, try restarting the embedding server
|
|
3639
|
+
initLog('Socket revalidation: no socket found, attempting auto-restart of embedding server...');
|
|
3640
|
+
try {
|
|
3641
|
+
const { spawn } = require('child_process');
|
|
3642
|
+
const possiblePythonPaths = [
|
|
3643
|
+
path.join(__dirname, '..', 'embedding-sandbox', 'frankenstein-embeddings.py'),
|
|
3644
|
+
path.join(projectPath, 'embedding-sandbox', 'frankenstein-embeddings.py'),
|
|
3645
|
+
'/opt/specmem/embedding-sandbox/frankenstein-embeddings.py'
|
|
3646
|
+
];
|
|
3647
|
+
let embeddingScript = null;
|
|
3648
|
+
for (const p of possiblePythonPaths) {
|
|
3649
|
+
if (fs.existsSync(p)) { embeddingScript = p; break; }
|
|
3650
|
+
}
|
|
3651
|
+
if (embeddingScript) {
|
|
3652
|
+
const socketsDir = path.join(projectPath, 'specmem', 'sockets');
|
|
3653
|
+
if (!fs.existsSync(socketsDir)) fs.mkdirSync(socketsDir, { recursive: true });
|
|
3654
|
+
// Clean stale socket
|
|
3655
|
+
if (fs.existsSync(projectSocketPath)) {
|
|
3656
|
+
try { fs.unlinkSync(projectSocketPath); } catch { /* ignore */ }
|
|
3657
|
+
}
|
|
3658
|
+
const pythonPath = getPythonPath();
|
|
3659
|
+
const proc = spawn(pythonPath, [embeddingScript], {
|
|
3660
|
+
cwd: path.dirname(embeddingScript),
|
|
3661
|
+
env: { ...process.env, SPECMEM_SOCKET_PATH: projectSocketPath, SPECMEM_PROJECT_PATH: projectPath },
|
|
3662
|
+
detached: true,
|
|
3663
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
3664
|
+
});
|
|
3665
|
+
proc.stdout.on('data', () => {});
|
|
3666
|
+
proc.stderr.on('data', () => {});
|
|
3667
|
+
proc.on('error', () => {});
|
|
3668
|
+
proc.unref();
|
|
3669
|
+
// Wait up to 15s for socket to appear
|
|
3670
|
+
for (let i = 0; i < 30; i++) {
|
|
3671
|
+
await new Promise(r => setTimeout(r, 500));
|
|
3672
|
+
if (fs.existsSync(projectSocketPath)) {
|
|
3673
|
+
activeSocketPath = projectSocketPath;
|
|
3674
|
+
const healthy = await checkSocketHealth();
|
|
3675
|
+
if (healthy) {
|
|
3676
|
+
initLog(`Embedding server auto-restarted successfully, socket at ${projectSocketPath}`);
|
|
3677
|
+
consecutiveEmbeddingFailures = 0;
|
|
3678
|
+
return true;
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
initLog('Embedding server auto-restart: socket did not appear within 15s');
|
|
3683
|
+
}
|
|
3684
|
+
} catch (restartErr) {
|
|
3685
|
+
initLog(`Embedding server auto-restart failed: ${restartErr.message || restartErr}`);
|
|
3686
|
+
}
|
|
3687
|
+
|
|
3623
3688
|
initLog('Socket revalidation failed: no healthy socket found');
|
|
3624
3689
|
return false;
|
|
3625
3690
|
}
|
|
@@ -3666,8 +3731,20 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3666
3731
|
|
|
3667
3732
|
client.setTimeout(TIMEOUTS.EMBEDDING_REQUEST * 2); // Longer timeout for batch
|
|
3668
3733
|
|
|
3734
|
+
// HARD WALL-CLOCK TIMEOUT: Cannot be reset by data/heartbeats
|
|
3735
|
+
// Prevents infinite hangs on large files where server keeps sending heartbeats
|
|
3736
|
+
const HARD_TIMEOUT_MS = TIMEOUTS.EMBEDDING_REQUEST * 3; // 270s absolute max
|
|
3737
|
+
const hardTimeout = setTimeout(() => {
|
|
3738
|
+
if (!settled) {
|
|
3739
|
+
settled = true;
|
|
3740
|
+
if (!client.destroyed) client.destroy();
|
|
3741
|
+
reject(new Error(`Hard wall-clock timeout after ${HARD_TIMEOUT_MS}ms - embedding took too long (heartbeats: ${heartbeatCount})`));
|
|
3742
|
+
}
|
|
3743
|
+
}, HARD_TIMEOUT_MS);
|
|
3744
|
+
|
|
3669
3745
|
// Cleanup helper to avoid leaked resources
|
|
3670
3746
|
const cleanup = () => {
|
|
3747
|
+
clearTimeout(hardTimeout);
|
|
3671
3748
|
if (!client.destroyed) client.destroy();
|
|
3672
3749
|
};
|
|
3673
3750
|
|
|
@@ -4188,6 +4265,16 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
4188
4265
|
setPhase('Embedding');
|
|
4189
4266
|
|
|
4190
4267
|
let fileEmbeddings = [];
|
|
4268
|
+
// FIX: If activeSocketPath is gone (socket died), try to revalidate before skipping
|
|
4269
|
+
if (fileEmbedTexts.length > 0 && !activeSocketPath) {
|
|
4270
|
+
initLog('No active socket for embedding phase - attempting revalidation...');
|
|
4271
|
+
const revalidated = await revalidateSocket();
|
|
4272
|
+
if (revalidated) {
|
|
4273
|
+
initLog(`Socket revalidated successfully: ${activeSocketPath}`);
|
|
4274
|
+
} else {
|
|
4275
|
+
initLog('Socket revalidation failed - embeddings will be skipped for this batch');
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4191
4278
|
if (fileEmbedTexts.length > 0 && activeSocketPath) {
|
|
4192
4279
|
try {
|
|
4193
4280
|
ui.setSubStatus(`Generating ${fileEmbedTexts.length} embeddings in batch...`);
|