specmem-hardwicksoftware 3.5.20 → 3.5.22

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.

Potentially problematic release.


This version of specmem-hardwicksoftware might be problematic. Click here for more details.

package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specmem-hardwicksoftware",
3
- "version": "3.5.20",
3
+ "version": "3.5.22",
4
4
  "type": "module",
5
5
  "description": "SpecMem - Speculative Memory for Claude Code. Auto-configures hooks, docker, embeddings. https://justcalljon.pro",
6
6
  "main": "dist/index.js",
@@ -126,6 +126,15 @@ const IS_MAC = PLATFORM === 'darwin';
126
126
  const IS_LINUX = PLATFORM === 'linux';
127
127
  const IS_WINDOWS = PLATFORM === 'win32';
128
128
 
129
+ // Detect if running inside Docker container
130
+ const IS_DOCKER = fs.existsSync('/.dockerenv') ||
131
+ (fs.existsSync('/proc/1/cgroup') &&
132
+ fs.readFileSync('/proc/1/cgroup', 'utf8').includes('docker'));
133
+
134
+ // Skip embeddings flag (for low-resource or containerized environments)
135
+ const SKIP_EMBEDDINGS = process.env.SPECMEM_SKIP_EMBEDDINGS === 'true' ||
136
+ process.env.SPECMEM_LITE === 'true';
137
+
129
138
  /**
130
139
  * Run a command and return success/output
131
140
  */
@@ -265,56 +274,77 @@ function checkPostgres() {
265
274
  function installPostgres() {
266
275
  log.header('Installing PostgreSQL');
267
276
 
277
+ const PG_TIMEOUT = 180000; // 3 minute timeout for PG install
278
+
268
279
  if (IS_MAC) {
269
280
  log.step(1, 'Installing via Homebrew...');
270
281
 
271
282
  // Check if Homebrew exists
272
283
  if (!commandExists('brew')) {
273
284
  log.info('Installing Homebrew first...');
274
- run('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"');
285
+ run('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { timeout: 300000 });
275
286
  }
276
287
 
277
- run('brew install postgresql@16');
278
- run('brew services start postgresql@16');
288
+ run('brew install postgresql@16', { timeout: PG_TIMEOUT });
289
+ run('brew services start postgresql@16', { timeout: 30000 });
279
290
 
280
291
  // Add to PATH
281
- const brewPrefix = execSync('brew --prefix', { encoding: 'utf8' }).trim();
282
- const pgPath = `${brewPrefix}/opt/postgresql@16/bin`;
283
- log.info(`Add to PATH: export PATH="${pgPath}:$PATH"`);
292
+ try {
293
+ const brewPrefix = execSync('brew --prefix', { encoding: 'utf8', timeout: 10000 }).trim();
294
+ const pgPath = `${brewPrefix}/opt/postgresql@16/bin`;
295
+ log.info(`Add to PATH: export PATH="${pgPath}:$PATH"`);
296
+ } catch (e) {}
284
297
 
285
298
  } else if (IS_LINUX) {
286
299
  log.step(1, 'Detecting Linux distribution...');
287
300
 
288
301
  // Check for apt (Debian/Ubuntu)
289
302
  if (commandExists('apt-get')) {
290
- log.step(2, 'Installing via apt...');
291
- run('sudo apt-get update');
292
- run('sudo apt-get install -y postgresql postgresql-contrib');
293
- run('sudo systemctl start postgresql');
294
- run('sudo systemctl enable postgresql');
303
+ log.step(2, 'Installing via apt (timeout: 3 min)...');
304
+ run('sudo apt-get update', { timeout: 60000 });
305
+ const result = run('sudo apt-get install -y postgresql postgresql-contrib', { timeout: PG_TIMEOUT });
306
+ if (!result.success) {
307
+ log.warn('PostgreSQL install timed out or failed');
308
+ log.info('Try manually: sudo apt-get install -y postgresql postgresql-contrib');
309
+ return false;
310
+ }
311
+ run('sudo systemctl start postgresql', { timeout: 30000 });
312
+ run('sudo systemctl enable postgresql', { timeout: 30000 });
295
313
  }
296
314
  // Check for yum/dnf (RHEL/CentOS/Fedora)
297
315
  else if (commandExists('dnf')) {
298
- log.step(2, 'Installing via dnf...');
299
- run('sudo dnf install -y postgresql-server postgresql-contrib');
300
- run('sudo postgresql-setup --initdb');
301
- run('sudo systemctl start postgresql');
302
- run('sudo systemctl enable postgresql');
316
+ log.step(2, 'Installing via dnf (timeout: 3 min)...');
317
+ const result = run('sudo dnf install -y postgresql-server postgresql-contrib', { timeout: PG_TIMEOUT });
318
+ if (!result.success) {
319
+ log.warn('PostgreSQL install timed out');
320
+ return false;
321
+ }
322
+ run('sudo postgresql-setup --initdb', { timeout: 60000 });
323
+ run('sudo systemctl start postgresql', { timeout: 30000 });
324
+ run('sudo systemctl enable postgresql', { timeout: 30000 });
303
325
  }
304
326
  else if (commandExists('yum')) {
305
- log.step(2, 'Installing via yum...');
306
- run('sudo yum install -y postgresql-server postgresql-contrib');
307
- run('sudo postgresql-setup initdb');
308
- run('sudo systemctl start postgresql');
309
- run('sudo systemctl enable postgresql');
327
+ log.step(2, 'Installing via yum (timeout: 3 min)...');
328
+ const result = run('sudo yum install -y postgresql-server postgresql-contrib', { timeout: PG_TIMEOUT });
329
+ if (!result.success) {
330
+ log.warn('PostgreSQL install timed out');
331
+ return false;
332
+ }
333
+ run('sudo postgresql-setup initdb', { timeout: 60000 });
334
+ run('sudo systemctl start postgresql', { timeout: 30000 });
335
+ run('sudo systemctl enable postgresql', { timeout: 30000 });
310
336
  }
311
337
  // Check for pacman (Arch)
312
338
  else if (commandExists('pacman')) {
313
- log.step(2, 'Installing via pacman...');
314
- run('sudo pacman -S --noconfirm postgresql');
315
- run('sudo -u postgres initdb -D /var/lib/postgres/data');
316
- run('sudo systemctl start postgresql');
317
- run('sudo systemctl enable postgresql');
339
+ log.step(2, 'Installing via pacman (timeout: 3 min)...');
340
+ const result = run('sudo pacman -S --noconfirm postgresql', { timeout: PG_TIMEOUT });
341
+ if (!result.success) {
342
+ log.warn('PostgreSQL install timed out');
343
+ return false;
344
+ }
345
+ run('sudo -u postgres initdb -D /var/lib/postgres/data', { timeout: 60000 });
346
+ run('sudo systemctl start postgresql', { timeout: 30000 });
347
+ run('sudo systemctl enable postgresql', { timeout: 30000 });
318
348
  }
319
349
  else {
320
350
  log.error('Could not detect package manager. Please install PostgreSQL manually.');
@@ -328,8 +358,9 @@ function installPostgres() {
328
358
  return false;
329
359
  }
330
360
 
331
- // Verify installation
332
- const check = run('pg_isready', { silent: true });
361
+ // Verify installation with timeout
362
+ log.info('Verifying PostgreSQL installation...');
363
+ const check = run('pg_isready', { silent: true, timeout: 10000 });
333
364
  if (check.success) {
334
365
  log.success('PostgreSQL installed and running!');
335
366
  return true;
@@ -338,12 +369,23 @@ function installPostgres() {
338
369
  // Try starting the service
339
370
  log.info('Trying to start PostgreSQL service...');
340
371
  if (IS_MAC) {
341
- run('brew services start postgresql@16');
372
+ run('brew services start postgresql@16', { timeout: 30000 });
342
373
  } else if (IS_LINUX) {
343
- run('sudo systemctl start postgresql');
374
+ run('sudo systemctl start postgresql', { timeout: 30000 });
375
+ }
376
+
377
+ // Wait up to 30 seconds for PG to be ready
378
+ for (let i = 0; i < 6; i++) {
379
+ if (run('pg_isready', { silent: true, timeout: 5000 }).success) {
380
+ log.success('PostgreSQL is ready');
381
+ return true;
382
+ }
383
+ log.info('Waiting for PostgreSQL to start...');
384
+ run('sleep 5', { silent: true });
344
385
  }
345
386
 
346
- return run('pg_isready', { silent: true }).success;
387
+ log.warn('PostgreSQL may not be fully started - continuing anyway');
388
+ return true; // Continue setup, might work
347
389
  }
348
390
 
349
391
  // ============================================================================
@@ -358,7 +400,7 @@ function checkPgvector() {
358
400
 
359
401
  const result = run(
360
402
  `sudo -u postgres psql -c "SELECT 1 FROM pg_available_extensions WHERE name = 'vector';" 2>/dev/null`,
361
- { silent: true }
403
+ { silent: true, timeout: 15000 }
362
404
  );
363
405
 
364
406
  if (result.success && result.output && result.output.includes('1')) {
@@ -376,30 +418,33 @@ function checkPgvector() {
376
418
  function installPgvector() {
377
419
  log.header('Installing pgvector Extension');
378
420
 
421
+ const PGVECTOR_TIMEOUT = 120000; // 2 minute timeout
422
+
379
423
  if (IS_MAC) {
380
424
  log.step(1, 'Installing pgvector via Homebrew...');
381
- run('brew install pgvector');
425
+ run('brew install pgvector', { timeout: PGVECTOR_TIMEOUT });
382
426
 
383
427
  } else if (IS_LINUX) {
384
428
  // Try apt first (Ubuntu/Debian have pgvector in repos now)
385
429
  if (commandExists('apt-get')) {
386
- log.step(1, 'Trying apt install...');
387
- const aptResult = run('sudo apt-get install -y postgresql-16-pgvector 2>/dev/null || sudo apt-get install -y postgresql-pgvector 2>/dev/null', { silent: true });
430
+ log.step(1, 'Trying apt install (timeout: 2 min)...');
431
+ const aptResult = run('sudo apt-get install -y postgresql-16-pgvector 2>/dev/null || sudo apt-get install -y postgresql-pgvector 2>/dev/null', { silent: true, timeout: PGVECTOR_TIMEOUT });
388
432
 
389
433
  if (!aptResult.success) {
390
- log.step(2, 'Building pgvector from source...');
434
+ log.step(2, 'Building pgvector from source (timeout: 3 min)...');
391
435
  buildPgvectorFromSource();
392
436
  }
393
437
  } else {
394
- log.step(1, 'Building pgvector from source...');
438
+ log.step(1, 'Building pgvector from source (timeout: 3 min)...');
395
439
  buildPgvectorFromSource();
396
440
  }
397
441
  }
398
442
 
399
- // Verify
443
+ // Verify with timeout
444
+ log.info('Verifying pgvector installation...');
400
445
  const check = run(
401
446
  `sudo -u postgres psql -c "SELECT 1 FROM pg_available_extensions WHERE name = 'vector';" 2>/dev/null`,
402
- { silent: true }
447
+ { silent: true, timeout: 15000 }
403
448
  );
404
449
 
405
450
  if (check.success && check.output && check.output.includes('1')) {
@@ -407,7 +452,8 @@ function installPgvector() {
407
452
  return true;
408
453
  }
409
454
 
410
- log.warn('pgvector may need manual installation');
455
+ log.warn('pgvector install may have timed out - continuing anyway');
456
+ log.info('SpecMem can work without pgvector (reduced functionality)');
411
457
  return false;
412
458
  }
413
459
 
@@ -417,21 +463,33 @@ function installPgvector() {
417
463
  function buildPgvectorFromSource() {
418
464
  log.info('Building pgvector from source...');
419
465
 
466
+ const BUILD_TIMEOUT = 180000; // 3 minute timeout for build
467
+
420
468
  // Install build dependencies
421
469
  if (commandExists('apt-get')) {
422
- run('sudo apt-get install -y build-essential git postgresql-server-dev-all');
470
+ run('sudo apt-get install -y build-essential git postgresql-server-dev-all', { timeout: 120000 });
423
471
  } else if (commandExists('dnf')) {
424
- run('sudo dnf install -y gcc make git postgresql-devel');
472
+ run('sudo dnf install -y gcc make git postgresql-devel', { timeout: 120000 });
425
473
  } else if (commandExists('yum')) {
426
- run('sudo yum install -y gcc make git postgresql-devel');
474
+ run('sudo yum install -y gcc make git postgresql-devel', { timeout: 120000 });
427
475
  }
428
476
 
429
- // Clone and build
477
+ // Clone and build with timeout
430
478
  const tmpDir = '/tmp/pgvector-build';
431
- run(`rm -rf ${tmpDir}`);
432
- run(`git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git ${tmpDir}`);
433
- run(`cd ${tmpDir} && make && sudo make install`);
434
- run(`rm -rf ${tmpDir}`);
479
+ run(`rm -rf ${tmpDir}`, { timeout: 10000 });
480
+
481
+ const cloneResult = run(`git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git ${tmpDir}`, { timeout: 60000 });
482
+ if (!cloneResult.success) {
483
+ log.warn('Failed to clone pgvector repo - skipping');
484
+ return;
485
+ }
486
+
487
+ const buildResult = run(`cd ${tmpDir} && make && sudo make install`, { timeout: BUILD_TIMEOUT });
488
+ if (!buildResult.success) {
489
+ log.warn('pgvector build timed out or failed');
490
+ }
491
+
492
+ run(`rm -rf ${tmpDir}`, { timeout: 10000 });
435
493
  }
436
494
 
437
495
  // ============================================================================
@@ -541,6 +599,25 @@ function adjustPgAuth() {
541
599
  function setupDocker() {
542
600
  log.header('Checking Docker');
543
601
 
602
+ // Skip if requested or in lite mode
603
+ if (SKIP_EMBEDDINGS) {
604
+ log.info('Skipping Docker/embeddings setup (SPECMEM_SKIP_EMBEDDINGS=true)');
605
+ log.info('SpecMem will use API-based embeddings as fallback');
606
+ return true;
607
+ }
608
+
609
+ // Warn if running inside Docker (Docker-in-Docker is complex)
610
+ if (IS_DOCKER) {
611
+ log.warn('Running inside Docker container detected!');
612
+ log.info('Docker-in-Docker requires special setup.');
613
+ log.info('Options:');
614
+ log.info(' 1. Mount host Docker socket: -v /var/run/docker.sock:/var/run/docker.sock');
615
+ log.info(' 2. Use privileged mode: --privileged');
616
+ log.info(' 3. Skip local embeddings: export SPECMEM_SKIP_EMBEDDINGS=true');
617
+ log.info('');
618
+ log.info('Attempting to continue anyway...');
619
+ }
620
+
544
621
  // Check if Docker is already installed
545
622
  if (commandExists('docker')) {
546
623
  log.success('Docker is installed');
@@ -608,7 +685,25 @@ function startDockerDaemon() {
608
685
  return false;
609
686
 
610
687
  } else if (IS_LINUX) {
611
- // Try systemctl first
688
+ // In Docker container, systemctl won't work
689
+ if (IS_DOCKER) {
690
+ log.warn('Inside Docker container - systemctl unavailable');
691
+ log.info('Checking if Docker socket is mounted...');
692
+
693
+ if (fs.existsSync('/var/run/docker.sock')) {
694
+ if (run('docker info', { silent: true }).success) {
695
+ log.success('Docker socket available (mounted from host)');
696
+ return true;
697
+ }
698
+ }
699
+
700
+ log.warn('Docker socket not available in container');
701
+ log.info('SpecMem will work without local embeddings');
702
+ log.info('To enable: run container with -v /var/run/docker.sock:/var/run/docker.sock');
703
+ return false;
704
+ }
705
+
706
+ // Try systemctl first (only on non-containerized Linux)
612
707
  log.step(1, 'Starting Docker via systemctl...');
613
708
  run('sudo systemctl start docker');
614
709
  run('sudo systemctl enable docker');
@@ -775,6 +870,7 @@ function installDockerLinux() {
775
870
 
776
871
  /**
777
872
  * Configure Docker for non-root usage on Linux
873
+ * SECURITY: Binds Docker to 127.0.0.1 only (no external access)
778
874
  */
779
875
  function configureDockerLinux() {
780
876
  log.step('→', 'Configuring Docker for non-root usage...');
@@ -784,9 +880,46 @@ function configureDockerLinux() {
784
880
  // Add user to docker group
785
881
  run(`sudo usermod -aG docker ${user}`);
786
882
 
883
+ // SECURITY: Configure Docker to bind to localhost only
884
+ log.step('→', 'Securing Docker to localhost only (127.0.0.1)...');
885
+ const daemonConfig = {
886
+ "iptables": true,
887
+ "ip": "127.0.0.1",
888
+ "ip-forward": false,
889
+ "userland-proxy": true,
890
+ "live-restore": true,
891
+ "log-driver": "json-file",
892
+ "log-opts": {
893
+ "max-size": "10m",
894
+ "max-file": "3"
895
+ }
896
+ };
897
+
898
+ try {
899
+ const daemonJsonPath = '/etc/docker/daemon.json';
900
+ run('sudo mkdir -p /etc/docker');
901
+
902
+ // Check if config exists and merge
903
+ let existingConfig = {};
904
+ if (fs.existsSync(daemonJsonPath)) {
905
+ try {
906
+ existingConfig = JSON.parse(fs.readFileSync(daemonJsonPath, 'utf8'));
907
+ } catch (e) {}
908
+ }
909
+
910
+ const mergedConfig = { ...existingConfig, ...daemonConfig };
911
+ const configJson = JSON.stringify(mergedConfig, null, 2);
912
+
913
+ // Write via sudo
914
+ run(`echo '${configJson}' | sudo tee ${daemonJsonPath}`);
915
+ log.success('Docker configured for localhost-only access (127.0.0.1)');
916
+ } catch (e) {
917
+ log.warn('Could not configure Docker daemon.json - manual security review recommended');
918
+ }
919
+
787
920
  // Enable and start Docker service
788
921
  run('sudo systemctl enable docker');
789
- run('sudo systemctl start docker');
922
+ run('sudo systemctl restart docker'); // Restart to apply new config
790
923
 
791
924
  // Set up Docker to start on boot
792
925
  run('sudo systemctl enable containerd');