refactor: require Bun globally, add auto-install, remove Windows executable approach (#243)
- Delete BinaryManager.ts - no longer needed - Simplify ProcessManager.ts - single Bun spawn path for all platforms - Update smart-install.js - auto-install Bun if missing (Windows/Unix) - Update documentation to reflect Bun requirement This simplifies the codebase by: - Using Bun consistently across all platforms (hooks + worker) - Eliminating binary download/hosting complexity - Zero native dependencies with bun:sqlite - Auto-installing Bun on first run if not present Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -76,8 +76,8 @@ Settings are managed in `~/.claude-mem/settings.json`. The file is auto-created
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- **Bun** >= 1.0 (Mac/Linux runtime)
|
- **Bun** >= 1.0 (all platforms - auto-installed if missing)
|
||||||
- Node.js >= 18 (build tools)
|
- Node.js >= 18 (build tools only)
|
||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ npx mintlify dev
|
|||||||
- **[Architecture Evolution](https://docs.claude-mem.ai/architecture-evolution)** - The journey from v3 to v5
|
- **[Architecture Evolution](https://docs.claude-mem.ai/architecture-evolution)** - The journey from v3 to v5
|
||||||
- **[Hooks Architecture](https://docs.claude-mem.ai/hooks-architecture)** - How Claude-Mem uses lifecycle hooks
|
- **[Hooks Architecture](https://docs.claude-mem.ai/hooks-architecture)** - How Claude-Mem uses lifecycle hooks
|
||||||
- **[Hooks Reference](https://docs.claude-mem.ai/architecture/hooks)** - 7 hook scripts explained
|
- **[Hooks Reference](https://docs.claude-mem.ai/architecture/hooks)** - 7 hook scripts explained
|
||||||
- **[Worker Service](https://docs.claude-mem.ai/architecture/worker-service)** - HTTP API & PM2 management
|
- **[Worker Service](https://docs.claude-mem.ai/architecture/worker-service)** - HTTP API & Bun process management
|
||||||
- **[Database](https://docs.claude-mem.ai/architecture/database)** - SQLite schema & FTS5 search
|
- **[Database](https://docs.claude-mem.ai/architecture/database)** - SQLite schema & FTS5 search
|
||||||
- **[Search Architecture](https://docs.claude-mem.ai/architecture/search-architecture)** - Hybrid search with Chroma vector database
|
- **[Search Architecture](https://docs.claude-mem.ai/architecture/search-architecture)** - Hybrid search with Chroma vector database
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ npx mintlify dev
|
|||||||
|
|
||||||
1. **5 Lifecycle Hooks** - SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd (6 hook scripts)
|
1. **5 Lifecycle Hooks** - SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd (6 hook scripts)
|
||||||
2. **Smart Install** - Cached dependency checker (pre-hook script, not a lifecycle hook)
|
2. **Smart Install** - Cached dependency checker (pre-hook script, not a lifecycle hook)
|
||||||
3. **Worker Service** - HTTP API on port 37777 with web viewer UI and 10 search endpoints, managed by PM2
|
3. **Worker Service** - HTTP API on port 37777 with web viewer UI and 10 search endpoints, managed by Bun
|
||||||
4. **SQLite Database** - Stores sessions, observations, summaries with FTS5 full-text search
|
4. **SQLite Database** - Stores sessions, observations, summaries with FTS5 full-text search
|
||||||
5. **mem-search Skill** - Natural language queries with progressive disclosure (~2,250 token savings vs MCP)
|
5. **mem-search Skill** - Natural language queries with progressive disclosure (~2,250 token savings vs MCP)
|
||||||
6. **Chroma Vector Database** - Hybrid semantic + keyword search for intelligent context retrieval
|
6. **Chroma Vector Database** - Hybrid semantic + keyword search for intelligent context retrieval
|
||||||
@@ -260,10 +260,10 @@ See [CHANGELOG.md](CHANGELOG.md) for complete version history.
|
|||||||
|
|
||||||
## System Requirements
|
## System Requirements
|
||||||
|
|
||||||
- **Node.js**: 18.0.0 or higher
|
- **Bun**: 1.0 or higher (auto-installed on first run if missing)
|
||||||
|
- **Node.js**: 18.0.0 or higher (for build tools)
|
||||||
- **Claude Code**: Latest version with plugin support
|
- **Claude Code**: Latest version with plugin support
|
||||||
- **PM2**: Process manager (bundled - no global install required)
|
- **SQLite 3**: For persistent storage (via bun:sqlite - zero native dependencies)
|
||||||
- **SQLite 3**: For persistent storage (bundled)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
---
|
---
|
||||||
title: "Worker Service"
|
title: "Worker Service"
|
||||||
description: "HTTP API and PM2 process management"
|
description: "HTTP API and Bun process management"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Worker Service
|
# Worker Service
|
||||||
|
|
||||||
The worker service is a long-running HTTP API built with Express.js and managed by PM2. It processes observations through the Claude Agent SDK separately from hook execution to prevent timeout issues.
|
The worker service is a long-running HTTP API built with Express.js and managed natively by Bun. It processes observations through the Claude Agent SDK separately from hook execution to prevent timeout issues.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
- **Technology**: Express.js HTTP server
|
- **Technology**: Express.js HTTP server
|
||||||
- **Process Manager**: PM2
|
- **Runtime**: Bun (auto-installed if missing)
|
||||||
|
- **Process Manager**: Native Bun process management via ProcessManager
|
||||||
- **Port**: Fixed port 37777 (configurable via `CLAUDE_MEM_WORKER_PORT`)
|
- **Port**: Fixed port 37777 (configurable via `CLAUDE_MEM_WORKER_PORT`)
|
||||||
- **Location**: `src/services/worker-service.ts`
|
- **Location**: `src/services/worker-service.ts`
|
||||||
- **Built Output**: `plugin/scripts/worker-service.cjs`
|
- **Built Output**: `plugin/scripts/worker-service.cjs`
|
||||||
@@ -322,28 +323,15 @@ DELETE /sessions/:sessionDbId
|
|||||||
|
|
||||||
**Note**: As of v4.1.0, the cleanup hook no longer calls this endpoint. Sessions are marked complete instead of deleted to allow graceful worker shutdown.
|
**Note**: As of v4.1.0, the cleanup hook no longer calls this endpoint. Sessions are marked complete instead of deleted to allow graceful worker shutdown.
|
||||||
|
|
||||||
## PM2 Management
|
## Bun Process Management
|
||||||
|
|
||||||
### Configuration
|
### Overview
|
||||||
|
|
||||||
The worker is configured via `ecosystem.config.cjs`:
|
The worker is managed by the native `ProcessManager` class which handles:
|
||||||
|
- Process spawning with Bun runtime
|
||||||
```javascript
|
- PID file tracking at `~/.claude-mem/worker.pid`
|
||||||
module.exports = {
|
- Health checks with automatic retry
|
||||||
apps: [{
|
- Graceful shutdown with SIGTERM/SIGKILL fallback
|
||||||
name: 'claude-mem-worker',
|
|
||||||
script: './plugin/scripts/worker-service.cjs',
|
|
||||||
instances: 1,
|
|
||||||
autorestart: true,
|
|
||||||
watch: false,
|
|
||||||
max_memory_restart: '1G',
|
|
||||||
env: {
|
|
||||||
NODE_ENV: 'production',
|
|
||||||
FORCE_COLOR: '1'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
@@ -366,7 +354,18 @@ npm run worker:status
|
|||||||
|
|
||||||
### Auto-Start Behavior
|
### Auto-Start Behavior
|
||||||
|
|
||||||
As of v4.0.0, the worker service auto-starts when the SessionStart hook fires. Manual start is optional.
|
The worker service auto-starts when the SessionStart hook fires. Manual start is optional.
|
||||||
|
|
||||||
|
### Bun Requirement
|
||||||
|
|
||||||
|
Bun is required to run the worker service. If Bun is not installed, the smart-install script will automatically install it on first run:
|
||||||
|
|
||||||
|
- **Windows**: `powershell -c "irm bun.sh/install.ps1 | iex"`
|
||||||
|
- **macOS/Linux**: `curl -fsSL https://bun.sh/install | bash`
|
||||||
|
|
||||||
|
You can also install manually via:
|
||||||
|
- `winget install Oven-sh.Bun` (Windows)
|
||||||
|
- `brew install oven-sh/bun/bun` (macOS)
|
||||||
|
|
||||||
## Claude Agent SDK Integration
|
## Claude Agent SDK Integration
|
||||||
|
|
||||||
@@ -410,15 +409,15 @@ If port 37777 is in use, the worker will fail to start. Set a custom port via en
|
|||||||
|
|
||||||
## Data Storage
|
## Data Storage
|
||||||
|
|
||||||
The worker service stores data in the plugin data directory:
|
The worker service stores data in the user data directory:
|
||||||
|
|
||||||
```
|
```
|
||||||
${CLAUDE_PLUGIN_ROOT}/data/
|
~/.claude-mem/
|
||||||
├── claude-mem.db # SQLite database
|
├── claude-mem.db # SQLite database (bun:sqlite)
|
||||||
├── worker.port # Current worker port file
|
├── worker.pid # PID file for process tracking
|
||||||
|
├── settings.json # User settings
|
||||||
└── logs/
|
└── logs/
|
||||||
├── worker-out.log # Worker stdout logs
|
└── worker-YYYY-MM-DD.log # Daily rotating logs
|
||||||
└── worker-error.log # Worker stderr logs
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Error Handling
|
## Error Handling
|
||||||
|
|||||||
+145
-14
@@ -1,40 +1,171 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Smart Install Script for claude-mem
|
||||||
|
*
|
||||||
|
* Ensures Bun runtime is installed (auto-installs if missing)
|
||||||
|
* and handles dependency installation when needed.
|
||||||
|
*/
|
||||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||||
import { execSync } from 'child_process';
|
import { execSync, spawnSync } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
|
|
||||||
const ROOT = join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack');
|
const ROOT = join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack');
|
||||||
const MARKER = join(ROOT, '.install-version');
|
const MARKER = join(ROOT, '.install-version');
|
||||||
|
const IS_WINDOWS = process.platform === 'win32';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Bun is installed and accessible
|
||||||
|
*/
|
||||||
|
function isBunInstalled() {
|
||||||
|
try {
|
||||||
|
const result = spawnSync('bun', ['--version'], {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
shell: IS_WINDOWS
|
||||||
|
});
|
||||||
|
return result.status === 0;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Bun version if installed
|
||||||
|
*/
|
||||||
|
function getBunVersion() {
|
||||||
|
try {
|
||||||
|
const result = spawnSync('bun', ['--version'], {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
shell: IS_WINDOWS
|
||||||
|
});
|
||||||
|
return result.status === 0 ? result.stdout.trim() : null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install Bun automatically based on platform
|
||||||
|
*/
|
||||||
|
function installBun() {
|
||||||
|
console.error('🔧 Bun not found. Installing Bun runtime...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
// Windows: Use PowerShell installer
|
||||||
|
console.error(' Installing via PowerShell...');
|
||||||
|
execSync('powershell -c "irm bun.sh/install.ps1 | iex"', {
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Unix/macOS: Use curl installer
|
||||||
|
console.error(' Installing via curl...');
|
||||||
|
execSync('curl -fsSL https://bun.sh/install | bash', {
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify installation
|
||||||
|
if (isBunInstalled()) {
|
||||||
|
const version = getBunVersion();
|
||||||
|
console.error(`✅ Bun ${version} installed successfully`);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Bun may be installed but not in PATH yet for this session
|
||||||
|
// Try common installation paths
|
||||||
|
const bunPaths = IS_WINDOWS
|
||||||
|
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
||||||
|
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun'];
|
||||||
|
|
||||||
|
for (const bunPath of bunPaths) {
|
||||||
|
if (existsSync(bunPath)) {
|
||||||
|
console.error(`✅ Bun installed at ${bunPath}`);
|
||||||
|
console.error('⚠️ Please restart your terminal or add Bun to PATH:');
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
console.error(` $env:Path += ";${join(homedir(), '.bun', 'bin')}"`);
|
||||||
|
} else {
|
||||||
|
console.error(` export PATH="$HOME/.bun/bin:$PATH"`);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Bun installation completed but binary not found');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to install Bun automatically');
|
||||||
|
console.error(' Please install manually:');
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
console.error(' - winget install Oven-sh.Bun');
|
||||||
|
console.error(' - Or: powershell -c "irm bun.sh/install.ps1 | iex"');
|
||||||
|
} else {
|
||||||
|
console.error(' - curl -fsSL https://bun.sh/install | bash');
|
||||||
|
console.error(' - Or: brew install oven-sh/bun/bun');
|
||||||
|
}
|
||||||
|
console.error(' Then restart your terminal and try again.');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if dependencies need to be installed
|
||||||
|
*/
|
||||||
function needsInstall() {
|
function needsInstall() {
|
||||||
if (!existsSync(join(ROOT, 'node_modules'))) return true;
|
if (!existsSync(join(ROOT, 'node_modules'))) return true;
|
||||||
try {
|
try {
|
||||||
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
|
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
|
||||||
const marker = JSON.parse(readFileSync(MARKER, 'utf-8'));
|
const marker = JSON.parse(readFileSync(MARKER, 'utf-8'));
|
||||||
return pkg.version !== marker.version || process.version !== marker.node;
|
return pkg.version !== marker.version || getBunVersion() !== marker.bun;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function install() {
|
/**
|
||||||
console.error('Installing dependencies...');
|
* Install dependencies using Bun
|
||||||
|
*/
|
||||||
|
function installDeps() {
|
||||||
|
console.error('📦 Installing dependencies with Bun...');
|
||||||
try {
|
try {
|
||||||
execSync('npm install', { cwd: ROOT, stdio: 'inherit' });
|
execSync('bun install', { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS });
|
||||||
} catch {
|
} catch {
|
||||||
execSync('npm install --force', { cwd: ROOT, stdio: 'inherit' });
|
// Retry with force flag
|
||||||
|
execSync('bun install --force', { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write version marker
|
||||||
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
|
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
|
||||||
writeFileSync(MARKER, JSON.stringify({ version: pkg.version, node: process.version }));
|
writeFileSync(MARKER, JSON.stringify({
|
||||||
|
version: pkg.version,
|
||||||
|
bun: getBunVersion(),
|
||||||
|
installedAt: new Date().toISOString()
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsInstall()) {
|
// Main execution
|
||||||
try {
|
try {
|
||||||
install();
|
// Step 1: Ensure Bun is installed
|
||||||
console.error('✅ Dependencies installed');
|
if (!isBunInstalled()) {
|
||||||
} catch (e) {
|
installBun();
|
||||||
console.error('❌ npm install failed:', e.message);
|
|
||||||
process.exit(1);
|
// Re-check after installation
|
||||||
|
if (!isBunInstalled()) {
|
||||||
|
console.error('❌ Bun is required but not available in PATH');
|
||||||
|
console.error(' Please restart your terminal after installation');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 2: Install dependencies if needed
|
||||||
|
if (needsInstall()) {
|
||||||
|
installDeps();
|
||||||
|
console.error('✅ Dependencies installed');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('❌ Installation failed:', e.message);
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
||||||
import { join } from 'path';
|
|
||||||
import { DATA_DIR } from '../../shared/paths.js';
|
|
||||||
|
|
||||||
const BIN_DIR = join(DATA_DIR, 'bin');
|
|
||||||
const VERSION_FILE = join(BIN_DIR, 'version.txt');
|
|
||||||
const GITHUB_RELEASES = 'https://github.com/thedotmack/claude-mem/releases/download';
|
|
||||||
|
|
||||||
export class BinaryManager {
|
|
||||||
static async getExecutablePath(): Promise<string> {
|
|
||||||
if (process.platform !== 'win32') {
|
|
||||||
throw new Error('BinaryManager only used on Windows');
|
|
||||||
}
|
|
||||||
|
|
||||||
const version = this.getCurrentVersion();
|
|
||||||
const binaryPath = join(BIN_DIR, 'worker-service.exe');
|
|
||||||
|
|
||||||
// Check if we have correct version
|
|
||||||
if (existsSync(binaryPath)) {
|
|
||||||
const installed = this.getInstalledVersion();
|
|
||||||
if (installed === version) return binaryPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download
|
|
||||||
await this.downloadBinary(version);
|
|
||||||
return binaryPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async downloadBinary(version: string): Promise<void> {
|
|
||||||
const url = `${GITHUB_RELEASES}/v${version}/worker-service-v${version}-win-x64.exe`;
|
|
||||||
console.log(`Downloading worker binary v${version}...`);
|
|
||||||
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(
|
|
||||||
`Download failed: ${response.status}\n` +
|
|
||||||
`URL: ${url}\n` +
|
|
||||||
`Make sure the release exists with a Windows binary attached.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const buffer = await response.arrayBuffer();
|
|
||||||
mkdirSync(BIN_DIR, { recursive: true });
|
|
||||||
|
|
||||||
const binaryPath = join(BIN_DIR, 'worker-service.exe');
|
|
||||||
writeFileSync(binaryPath, Buffer.from(buffer));
|
|
||||||
|
|
||||||
// Write version file
|
|
||||||
writeFileSync(VERSION_FILE, version);
|
|
||||||
|
|
||||||
console.log('Download complete');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static getCurrentVersion(): string {
|
|
||||||
// Read from package.json in the installed plugin location
|
|
||||||
// This ensures we get the correct version even when running from marketplace
|
|
||||||
try {
|
|
||||||
const packageJson = JSON.parse(
|
|
||||||
readFileSync(join(DATA_DIR, '..', '.claude', 'plugins', 'marketplaces', 'thedotmack', 'package.json'), 'utf-8')
|
|
||||||
);
|
|
||||||
return packageJson.version;
|
|
||||||
} catch {
|
|
||||||
// Fallback to environment variable
|
|
||||||
return process.env.npm_package_version || 'unknown';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static getInstalledVersion(): string | null {
|
|
||||||
try {
|
|
||||||
if (!existsSync(VERSION_FILE)) return null;
|
|
||||||
return readFileSync(VERSION_FILE, 'utf-8').trim();
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -43,21 +43,21 @@ export class ProcessManager {
|
|||||||
|
|
||||||
const logFile = this.getLogFilePath();
|
const logFile = this.getLogFilePath();
|
||||||
|
|
||||||
// Platform-specific spawn
|
// Use Bun on all platforms
|
||||||
if (process.platform === 'win32') {
|
return this.startWithBun(workerScript, logFile, port);
|
||||||
return this.startWindows(port);
|
|
||||||
} else {
|
|
||||||
return this.startUnix(workerScript, logFile, port);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async startUnix(script: string, logFile: string, port: number): Promise<{ success: boolean; pid?: number; error?: string }> {
|
private static async startWithBun(script: string, logFile: string, port: number): Promise<{ success: boolean; pid?: number; error?: string }> {
|
||||||
try {
|
try {
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
|
|
||||||
const child = spawn('bun', [script], {
|
const child = spawn('bun', [script], {
|
||||||
detached: true,
|
detached: true,
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
env: { ...process.env, CLAUDE_MEM_WORKER_PORT: String(port) },
|
env: { ...process.env, CLAUDE_MEM_WORKER_PORT: String(port) },
|
||||||
cwd: MARKETPLACE_ROOT
|
cwd: MARKETPLACE_ROOT,
|
||||||
|
// Hide console window on Windows
|
||||||
|
...(isWindows && { windowsHide: true })
|
||||||
});
|
});
|
||||||
|
|
||||||
// Write logs
|
// Write logs
|
||||||
@@ -89,48 +89,6 @@ export class ProcessManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async startWindows(port: number): Promise<{ success: boolean; pid?: number; error?: string }> {
|
|
||||||
// Windows: Use compiled exe from ~/.claude-mem/bin/
|
|
||||||
// Import dynamically to avoid loading on non-Windows platforms
|
|
||||||
const { BinaryManager } = await import('./BinaryManager.js');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const exePath = await BinaryManager.getExecutablePath();
|
|
||||||
|
|
||||||
const child = spawn(exePath, [], {
|
|
||||||
detached: true,
|
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
|
||||||
env: { ...process.env, CLAUDE_MEM_WORKER_PORT: String(port) },
|
|
||||||
windowsHide: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const logFile = this.getLogFilePath();
|
|
||||||
const logStream = createWriteStream(logFile, { flags: 'a' });
|
|
||||||
child.stdout?.pipe(logStream);
|
|
||||||
child.stderr?.pipe(logStream);
|
|
||||||
|
|
||||||
child.unref();
|
|
||||||
|
|
||||||
if (!child.pid) {
|
|
||||||
return { success: false, error: 'Failed to get PID from spawned process' };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.writePidFile({
|
|
||||||
pid: child.pid,
|
|
||||||
port,
|
|
||||||
startedAt: new Date().toISOString(),
|
|
||||||
version: process.env.npm_package_version || 'unknown'
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.waitForHealth(child.pid, port);
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : String(error)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async stop(timeout: number = PROCESS_STOP_TIMEOUT_MS): Promise<boolean> {
|
static async stop(timeout: number = PROCESS_STOP_TIMEOUT_MS): Promise<boolean> {
|
||||||
const info = this.getPidInfo();
|
const info = this.getPidInfo();
|
||||||
if (!info) return true;
|
if (!info) return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user