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 +1 -1
- package/scripts/global-postinstall.cjs +183 -50
package/package.json
CHANGED
|
@@ -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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
294
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
433
|
-
run(`
|
|
434
|
-
|
|
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
|
-
//
|
|
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
|
|
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');
|