diff --git a/package-lock.json b/package-lock.json index c697f98..21c5986 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,322 +10,26 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.1.0", + "cross-spawn": "^7.0.6", "qrcode": "^1.5.4", "qrcode-terminal": "^0.12.0" }, "devDependencies": { + "@types/cross-spawn": "^6.0.6", "@types/node": "^22.0.0", "@types/qrcode": "^1.5.6", "@types/qrcode-terminal": "^0.12.0", "typescript": "^5.7.0" } }, - "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.1.77", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.1.77.tgz", - "integrity": "sha512-ZEjWQtkoB2MEY6K16DWMmF+8OhywAynH0m08V265cerbZ8xPD/2Ng2jPzbbO40mPeFSsMDJboShL+a3aObP0Jg==", - "license": "SEE LICENSE IN README.md", - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "^0.33.5", - "@img/sharp-darwin-x64": "^0.33.5", - "@img/sharp-linux-arm": "^0.33.5", - "@img/sharp-linux-arm64": "^0.33.5", - "@img/sharp-linux-x64": "^0.33.5", - "@img/sharp-linuxmusl-arm64": "^0.33.5", - "@img/sharp-linuxmusl-x64": "^0.33.5", - "@img/sharp-win32-x64": "^0.33.5" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@types/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, "node_modules/@types/node": { @@ -417,6 +121,20 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -469,6 +187,12 @@ "node": ">=8" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -526,6 +250,15 @@ "node": ">=8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", @@ -581,6 +314,27 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -628,6 +382,21 @@ "dev": true, "license": "MIT" }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", @@ -688,16 +457,6 @@ "engines": { "node": ">=6" } - }, - "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/package.json b/package.json index eace5c4..9fd6e9f 100644 --- a/package.json +++ b/package.json @@ -11,20 +11,31 @@ "dev": "tsc --watch", "test": "node --test dist/tests/*.test.js", "setup": "node dist/main.js setup", - "daemon": "bash scripts/daemon.sh", + "daemon": "node scripts/daemon.js", + "daemon:unix": "bash scripts/daemon.sh", + "daemon:win": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/daemon.ps1", "visualize": "npx tsx src/tools/visualize-logs.ts" }, "dependencies": { -"qrcode": "^1.5.4", + "cross-spawn": "^7.0.6", + "qrcode": "^1.5.4", "qrcode-terminal": "^0.12.0" }, "devDependencies": { + "@types/cross-spawn": "^6.0.6", "@types/node": "^22.0.0", "@types/qrcode": "^1.5.6", "@types/qrcode-terminal": "^0.12.0", "typescript": "^5.7.0" }, - "keywords": ["wechat", "claude-code", "claude", "bridge", "chat", "skill"], + "keywords": [ + "wechat", + "claude-code", + "claude", + "bridge", + "chat", + "skill" + ], "author": "Wechat-ggGitHub", "license": "MIT", "repository": { diff --git a/scripts/daemon.ps1 b/scripts/daemon.ps1 new file mode 100644 index 0000000..b049a2a --- /dev/null +++ b/scripts/daemon.ps1 @@ -0,0 +1,259 @@ +param( + [Parameter(Position = 0)] + [ValidateSet("setup", "start", "stop", "restart", "status", "logs")] + [string]$Command = "status", + + [Parameter(Position = 1)] + [ValidatePattern("^[A-Za-z0-9_-]+$")] + [string]$Instance = "default" +) + +$ErrorActionPreference = "Stop" + +$ProjectDir = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path + +# default 实例继续使用原来的数据目录,保留当前已登录账号 +$BaseDataDir = Join-Path $env:USERPROFILE ".wechat-claude-code" + +if ($Instance -eq "default") { + $DataDir = $BaseDataDir +} else { + $InstancesDir = Join-Path $BaseDataDir "instances" + $DataDir = Join-Path $InstancesDir $Instance +} + +# Node.js 子进程会继承这个环境变量 +$env:WCC_DATA_DIR = $DataDir + +$LogDir = Join-Path $DataDir "logs" +$PidFile = Join-Path $DataDir "wechat-claude-code.pid" +$StdoutLog = Join-Path $LogDir "stdout.log" +$StderrLog = Join-Path $LogDir "stderr.log" +$EntryFile = Join-Path $ProjectDir "dist\main.js" + +function Get-NodePath { + $nodeCommand = Get-Command node -ErrorAction Stop + return $nodeCommand.Source +} + +function Get-SavedProcessId { + if (-not (Test-Path $PidFile)) { + return $null + } + + try { + $value = (Get-Content $PidFile -Raw).Trim() + [int]$parsedId = 0 + + if ([int]::TryParse($value, [ref]$parsedId)) { + return $parsedId + } + } catch { + return $null + } + + return $null +} + +function Test-ProcessRunning { + param( + [int]$ProcessId + ) + + if ($ProcessId -le 0) { + return $false + } + + try { + Get-Process -Id $ProcessId -ErrorAction Stop | Out-Null + return $true + } catch { + return $false + } +} + +function Remove-PidFile { + if (Test-Path $PidFile) { + Remove-Item $PidFile -Force -ErrorAction SilentlyContinue + } +} + +function Invoke-InstanceSetup { + if (-not (Test-Path $EntryFile)) { + throw "dist/main.js not found. Run 'npm run build' first." + } + + New-Item -ItemType Directory -Force -Path $DataDir | Out-Null + + $nodePath = Get-NodePath + + Write-Host "Setting up instance: $Instance" + Write-Host "Data directory: $DataDir" + Write-Host "" + + Push-Location $ProjectDir + + try { + & $nodePath $EntryFile setup + + if ($LASTEXITCODE -ne 0) { + throw "Setup failed with exit code $LASTEXITCODE" + } + } finally { + Pop-Location + } +} + +function Start-Daemon { + $savedPid = Get-SavedProcessId + + if ($savedPid -and (Test-ProcessRunning -ProcessId $savedPid)) { + Write-Host "Instance '$Instance' is already running (PID: $savedPid)" + return + } + + Remove-PidFile + + if (-not (Test-Path $EntryFile)) { + throw "dist/main.js not found. Run 'npm run build' first." + } + + New-Item -ItemType Directory -Force -Path $LogDir | Out-Null + + $nodePath = Get-NodePath + + Write-Host "Starting instance '$Instance'..." + Write-Host "Data directory: $DataDir" + + $process = Start-Process ` + -FilePath $nodePath ` + -ArgumentList @("`"$EntryFile`"", "start") ` + -WorkingDirectory $ProjectDir ` + -WindowStyle Hidden ` + -RedirectStandardOutput $StdoutLog ` + -RedirectStandardError $StderrLog ` + -PassThru + + Set-Content -Path $PidFile -Value $process.Id -Encoding ASCII + + Start-Sleep -Milliseconds 800 + + if (-not (Test-ProcessRunning -ProcessId $process.Id)) { + Remove-PidFile + + Write-Host "" + Write-Host "Daemon exited immediately." + + if (Test-Path $StderrLog) { + Write-Host "=== stderr.log ===" + Get-Content $StderrLog -Encoding UTF8 -Tail 50 + } + + throw "Failed to start instance '$Instance'" + } + + Write-Host "Started instance '$Instance' (PID: $($process.Id))" + Write-Host "Logs: $StdoutLog" +} + +function Stop-Daemon { + $savedPid = Get-SavedProcessId + + if (-not $savedPid) { + Write-Host "Instance '$Instance' is not running (no PID file)" + Remove-PidFile + return + } + + if (-not (Test-ProcessRunning -ProcessId $savedPid)) { + Write-Host "Instance '$Instance' is not running (stale PID: $savedPid)" + Remove-PidFile + return + } + + Write-Host "Stopping instance '$Instance' (PID: $savedPid)..." + + & taskkill.exe /PID $savedPid /T /F | Out-Host + + Start-Sleep -Milliseconds 500 + Remove-PidFile + + Write-Host "Stopped instance '$Instance'" +} + +function Show-Status { + $savedPid = Get-SavedProcessId + + Write-Host "Instance: $Instance" + Write-Host "Data directory: $DataDir" + + if ($savedPid -and (Test-ProcessRunning -ProcessId $savedPid)) { + $process = Get-Process -Id $savedPid + $uptime = (Get-Date) - $process.StartTime + + Write-Host "Status: Running" + Write-Host "PID: $savedPid" + Write-Host "Started: $($process.StartTime)" + Write-Host "Uptime: $([math]::Floor($uptime.TotalHours))h $($uptime.Minutes)m" + return + } + + if ($savedPid) { + Remove-PidFile + } + + Write-Host "Status: Not running" +} + +function Show-Logs { + Write-Host "Instance: $Instance" + Write-Host "Data directory: $DataDir" + Write-Host "" + + $found = $false + + if (Test-Path $StdoutLog) { + $found = $true + Write-Host "=== stdout.log ===" + Get-Content $StdoutLog -Encoding UTF8 -Tail 100 + } + + if (Test-Path $StderrLog) { + $found = $true + Write-Host "" + Write-Host "=== stderr.log ===" + Get-Content $StderrLog -Encoding UTF8 -Tail 100 + } + + if (-not $found) { + Write-Host "No logs found" + } +} + +switch ($Command) { + "setup" { + Invoke-InstanceSetup + } + + "start" { + Start-Daemon + } + + "stop" { + Stop-Daemon + } + + "restart" { + Stop-Daemon + Start-Sleep -Seconds 1 + Start-Daemon + } + + "status" { + Show-Status + } + + "logs" { + Show-Logs + } +} \ No newline at end of file diff --git a/src/claude/provider.ts b/src/claude/provider.ts index fedb7a3..0f82f0c 100644 --- a/src/claude/provider.ts +++ b/src/claude/provider.ts @@ -1,4 +1,5 @@ -import { spawn, type ChildProcess } from 'node:child_process'; +import crossSpawn from 'cross-spawn'; +import type { ChildProcess } from 'node:child_process'; import { writeFileSync, unlinkSync, mkdirSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; @@ -121,10 +122,11 @@ export async function claudeQuery(options: QueryOptions): Promise { }; try { - child = spawn('claude', args, { + child = crossSpawn('claude', args, { cwd, stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env }, + windowsHide: true, }); } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); diff --git a/src/config.ts b/src/config.ts index 085d33c..094df56 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,6 @@ import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs"; import { join } from "node:path"; -import { homedir } from "node:os"; -import { DEFAULT_WORKING_DIR } from "./constants.js"; +import { DATA_DIR, DEFAULT_WORKING_DIR } from "./constants.js"; export interface Config { workingDirectory: string; @@ -9,7 +8,7 @@ export interface Config { systemPrompt?: string; } -const CONFIG_DIR = join(homedir(), ".wechat-claude-code"); +const CONFIG_DIR = DATA_DIR; const CONFIG_PATH = join(CONFIG_DIR, "config.json"); const DEFAULT_CONFIG: Config = { diff --git a/src/logger.ts b/src/logger.ts index 90ba24b..81ec109 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,8 +1,8 @@ import { mkdirSync, appendFileSync, readdirSync, unlinkSync } from "node:fs"; import { join } from "node:path"; -import { homedir } from "node:os"; +import { DATA_DIR } from "./constants.js"; -const LOG_DIR = join(homedir(), ".wechat-claude-code", "logs"); +const LOG_DIR = join(DATA_DIR, "logs"); const MAX_LOG_FILES = 30; // Keep at most 30 days of logs /** Clean up old log files beyond MAX_LOG_FILES retention. */ diff --git a/src/main.ts b/src/main.ts index e0bb69b..1878c88 100644 --- a/src/main.ts +++ b/src/main.ts @@ -379,7 +379,7 @@ async function handleMessage( accountId: account.accountId, session, updateSession, - clearSession: () => sessionStore.clear(account.accountId), + clearSession: () => sessionStore.clear(account.accountId, session), getChatHistoryText: (limit?: number) => sessionStore.getChatHistoryText(session, limit), text: userText, }; diff --git a/src/wechat/accounts.ts b/src/wechat/accounts.ts index aa057a8..6d50304 100644 --- a/src/wechat/accounts.ts +++ b/src/wechat/accounts.ts @@ -1,8 +1,8 @@ import { join } from 'node:path'; -import { homedir } from 'node:os'; import { readdirSync, statSync } from 'node:fs'; import { loadJson, saveJson, validateAccountId } from '../store.js'; import { logger } from '../logger.js'; +import { DATA_DIR } from '../constants.js'; export const DEFAULT_BASE_URL = 'https://ilinkai.weixin.qq.com'; @@ -14,7 +14,7 @@ export interface AccountData { createdAt: string; } -const ACCOUNTS_DIR = join(homedir(), '.wechat-claude-code', 'accounts'); +const ACCOUNTS_DIR = join(DATA_DIR, 'accounts'); function accountPath(accountId: string): string { validateAccountId(accountId);