fix(python): use venv Python for all services to fix dotenv errors (#311)
* fix(python): use venv Python for all services to fix dotenv errors Services were spawning Python processes using findPythonCommand() which returns the bundled Python directly. However, dependencies like python-dotenv are only installed in the venv created from the bundled Python. Changes: - Add getConfiguredPythonPath() helper that returns venv Python when ready - Update all services to use venv Python instead of bundled Python directly: - memory-service.ts - memory-handlers.ts - agent-process.ts - changelog-service.ts - title-generator.ts - insights/config.ts - project-context-handlers.ts - worktree-handlers.ts - Fix availability checks to use findPythonCommand() (can return null) - Add python:verify script for bundling verification The flow now works correctly: 1. App starts → findPythonCommand() finds bundled Python 2. pythonEnvManager creates venv using bundled Python 3. pip installs dependencies (dotenv, claude-agent-sdk, etc.) 4. All services use venv Python → has all dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix lintin and test --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Generated
+28
-26
@@ -195,6 +195,7 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -579,6 +580,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -622,6 +624,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -661,6 +664,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
@@ -1067,7 +1071,6 @@
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cross-dirname": "^0.1.0",
|
||||
"debug": "^4.3.4",
|
||||
@@ -1089,7 +1092,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
@@ -4212,8 +4214,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
@@ -4400,6 +4401,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -4410,6 +4412,7 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -4501,6 +4504,7 @@
|
||||
"integrity": "sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.50.1",
|
||||
"@typescript-eslint/types": "8.50.1",
|
||||
@@ -4913,6 +4917,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -4973,6 +4978,7 @@
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -5145,7 +5151,6 @@
|
||||
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
}
|
||||
@@ -5540,6 +5545,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -6210,8 +6216,7 @@
|
||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
@@ -6561,6 +6566,7 @@
|
||||
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.0.12",
|
||||
"builder-util": "26.0.11",
|
||||
@@ -6618,8 +6624,7 @@
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
@@ -6695,6 +6700,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
"@types/node": "^22.7.7",
|
||||
@@ -6823,7 +6829,6 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.2.1",
|
||||
"debug": "^4.1.1",
|
||||
@@ -6844,7 +6849,6 @@
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
@@ -6860,7 +6864,6 @@
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
@@ -6871,7 +6874,6 @@
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
@@ -7241,6 +7243,7 @@
|
||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -8486,6 +8489,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4"
|
||||
},
|
||||
@@ -9276,6 +9280,7 @@
|
||||
"integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@acemir/cssom": "^0.9.28",
|
||||
"@asamuzakjp/dom-selector": "^6.7.6",
|
||||
@@ -10207,7 +10212,6 @@
|
||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"lz-string": "bin/bin.js"
|
||||
}
|
||||
@@ -12398,6 +12402,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -12495,6 +12500,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -12531,7 +12537,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^9.4.0"
|
||||
},
|
||||
@@ -12549,7 +12554,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
@@ -12570,7 +12574,6 @@
|
||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
"ansi-styles": "^5.0.0",
|
||||
@@ -12586,7 +12589,6 @@
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -12599,8 +12601,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proc-log": {
|
||||
"version": "2.0.1",
|
||||
@@ -12704,6 +12705,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -12713,6 +12715,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -14032,7 +14035,8 @@
|
||||
"version": "4.1.18",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
|
||||
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
@@ -14089,7 +14093,6 @@
|
||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "~2.6.2"
|
||||
@@ -14116,7 +14119,6 @@
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@@ -14138,7 +14140,6 @@
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -14152,7 +14153,6 @@
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -14167,7 +14167,6 @@
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
@@ -14484,6 +14483,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -14833,6 +14833,7 @@
|
||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -15875,6 +15876,7 @@
|
||||
"integrity": "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"rebuild": "electron-rebuild",
|
||||
"python:download": "node scripts/download-python.cjs",
|
||||
"python:download:all": "node scripts/download-python.cjs --all",
|
||||
"python:verify": "node scripts/verify-python-bundling.cjs",
|
||||
"package": "npm run python:download && electron-vite build && electron-builder",
|
||||
"package:mac": "npm run python:download && electron-vite build && electron-builder --mac",
|
||||
"package:win": "npm run python:download && electron-vite build && electron-builder --win",
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Verify Python bundling configuration is correct.
|
||||
* Run this before packaging to ensure Python will be properly bundled.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync, spawnSync } = require('child_process');
|
||||
|
||||
const FRONTEND_DIR = path.resolve(__dirname, '..');
|
||||
const PYTHON_RUNTIME_DIR = path.join(FRONTEND_DIR, 'python-runtime');
|
||||
|
||||
console.log('=== Python Bundling Verification ===\n');
|
||||
|
||||
// Check 1: Python runtime downloaded?
|
||||
console.log('1. Checking if Python runtime is downloaded...');
|
||||
const platform = process.platform === 'win32' ? 'win' : process.platform === 'darwin' ? 'mac' : 'linux';
|
||||
const arch = process.arch;
|
||||
const runtimePath = path.join(PYTHON_RUNTIME_DIR, `${platform}-${arch}`, 'python');
|
||||
|
||||
if (fs.existsSync(runtimePath)) {
|
||||
const pythonExe = process.platform === 'win32'
|
||||
? path.join(runtimePath, 'python.exe')
|
||||
: path.join(runtimePath, 'bin', 'python3');
|
||||
|
||||
if (fs.existsSync(pythonExe)) {
|
||||
console.log(` ✓ Found bundled Python at: ${pythonExe}`);
|
||||
|
||||
// Test version
|
||||
try {
|
||||
const version = execSync(`"${pythonExe}" --version`, { encoding: 'utf8' }).trim();
|
||||
console.log(` ✓ Version: ${version}`);
|
||||
} catch (e) {
|
||||
console.log(` ✗ Failed to get version: ${e.message}`);
|
||||
}
|
||||
} else {
|
||||
console.log(` ✗ Python executable not found at: ${pythonExe}`);
|
||||
}
|
||||
} else {
|
||||
console.log(` ✗ Python runtime not downloaded. Run: npm run python:download`);
|
||||
}
|
||||
|
||||
// Check 2: package.json extraResources configured?
|
||||
console.log('\n2. Checking package.json extraResources configuration...');
|
||||
const packageJson = require(path.join(FRONTEND_DIR, 'package.json'));
|
||||
const extraResources = packageJson.build?.extraResources || [];
|
||||
|
||||
const pythonResource = extraResources.find(r =>
|
||||
(typeof r === 'string' && r.includes('python')) ||
|
||||
(typeof r === 'object' && r.from?.includes('python'))
|
||||
);
|
||||
|
||||
if (pythonResource) {
|
||||
console.log(' ✓ Python is configured in extraResources:');
|
||||
console.log(` ${JSON.stringify(pythonResource)}`);
|
||||
} else {
|
||||
console.log(' ✗ Python not found in extraResources configuration');
|
||||
}
|
||||
|
||||
// Check 3: Test venv creation simulation
|
||||
console.log('\n3. Checking venv creation capability...');
|
||||
try {
|
||||
// Find system Python for testing
|
||||
let pythonCmd = process.platform === 'win32' ? 'python' : 'python3';
|
||||
|
||||
const result = spawnSync(pythonCmd, ['-m', 'venv', '--help'], { encoding: 'utf8' });
|
||||
if (result.status === 0) {
|
||||
console.log(` ✓ venv module is available`);
|
||||
} else {
|
||||
console.log(` ✗ venv module not available: ${result.stderr}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` ✗ Failed to check venv: ${e.message}`);
|
||||
}
|
||||
|
||||
// Check 4: Verify requirements.txt exists
|
||||
console.log('\n4. Checking requirements.txt...');
|
||||
const backendDir = path.join(FRONTEND_DIR, '..', 'backend');
|
||||
const requirementsPath = path.join(backendDir, 'requirements.txt');
|
||||
|
||||
if (fs.existsSync(requirementsPath)) {
|
||||
const content = fs.readFileSync(requirementsPath, 'utf8');
|
||||
const hasDotenv = content.includes('python-dotenv');
|
||||
const hasSDK = content.includes('claude-agent-sdk');
|
||||
|
||||
console.log(` ✓ requirements.txt found`);
|
||||
console.log(` ${hasDotenv ? '✓' : '✗'} python-dotenv: ${hasDotenv ? 'present' : 'MISSING!'}`);
|
||||
console.log(` ${hasSDK ? '✓' : '✗'} claude-agent-sdk: ${hasSDK ? 'present' : 'MISSING!'}`);
|
||||
} else {
|
||||
console.log(` ✗ requirements.txt not found at: ${requirementsPath}`);
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log('\n=== Summary ===');
|
||||
console.log('To fully test Python bundling:');
|
||||
console.log('1. Run: npm run python:download');
|
||||
console.log('2. Run: npm run package:win (or :mac/:linux)');
|
||||
console.log('3. Launch the packaged app and check Dev Tools console for:');
|
||||
console.log(' - "[Python] Found bundled Python at: ..."');
|
||||
console.log(' - "[PythonEnvManager] Ready with Python path: ..."');
|
||||
console.log('4. Try creating and running a task - should work without dotenv errors');
|
||||
@@ -9,7 +9,8 @@ import { ProcessType, ExecutionProgressData } from './types';
|
||||
import { detectRateLimit, createSDKRateLimitInfo, getProfileEnv, detectAuthFailure } from '../rate-limit-detector';
|
||||
import { projectStore } from '../project-store';
|
||||
import { getClaudeProfileManager } from '../claude-profile-manager';
|
||||
import { findPythonCommand, parsePythonCommand } from '../python-detector';
|
||||
import { parsePythonCommand } from '../python-detector';
|
||||
import { getConfiguredPythonPath } from '../python-env-manager';
|
||||
|
||||
/**
|
||||
* Process spawning and lifecycle management
|
||||
@@ -18,8 +19,9 @@ export class AgentProcessManager {
|
||||
private state: AgentState;
|
||||
private events: AgentEvents;
|
||||
private emitter: EventEmitter;
|
||||
// Auto-detect Python command on initialization
|
||||
private pythonPath: string = findPythonCommand() || 'python';
|
||||
// Python path will be configured by pythonEnvManager after venv is ready
|
||||
// Use null to indicate not yet configured - getPythonPath() will use fallback
|
||||
private _pythonPath: string | null = null;
|
||||
private autoBuildSourcePath: string = '';
|
||||
|
||||
constructor(state: AgentState, events: AgentEvents, emitter: EventEmitter) {
|
||||
@@ -33,7 +35,7 @@ export class AgentProcessManager {
|
||||
*/
|
||||
configure(pythonPath?: string, autoBuildSourcePath?: string): void {
|
||||
if (pythonPath) {
|
||||
this.pythonPath = pythonPath;
|
||||
this._pythonPath = pythonPath;
|
||||
}
|
||||
if (autoBuildSourcePath) {
|
||||
this.autoBuildSourcePath = autoBuildSourcePath;
|
||||
@@ -41,10 +43,17 @@ export class AgentProcessManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured Python path
|
||||
* Get the configured Python path.
|
||||
* Returns explicitly configured path, or falls back to getConfiguredPythonPath()
|
||||
* which uses the venv Python if ready.
|
||||
*/
|
||||
getPythonPath(): string {
|
||||
return this.pythonPath;
|
||||
// If explicitly configured (by pythonEnvManager), use that
|
||||
if (this._pythonPath) {
|
||||
return this._pythonPath;
|
||||
}
|
||||
// Otherwise use the global configured path (venv if ready, else bundled/system)
|
||||
return getConfiguredPythonPath();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +177,7 @@ export class AgentProcessManager {
|
||||
const profileEnv = getProfileEnv();
|
||||
|
||||
// Parse Python command to handle space-separated commands like "py -3"
|
||||
const [pythonCommand, pythonBaseArgs] = parsePythonCommand(this.pythonPath);
|
||||
const [pythonCommand, pythonBaseArgs] = parsePythonCommand(this.getPythonPath());
|
||||
const childProcess = spawn(pythonCommand, [...pythonBaseArgs, ...args], {
|
||||
cwd,
|
||||
env: {
|
||||
|
||||
@@ -27,15 +27,15 @@ import {
|
||||
getCommits,
|
||||
getBranchDiffCommits
|
||||
} from './git-integration';
|
||||
import { findPythonCommand } from '../python-detector';
|
||||
import { getConfiguredPythonPath } from '../python-env-manager';
|
||||
|
||||
/**
|
||||
* Main changelog service - orchestrates all changelog operations
|
||||
* Delegates to specialized modules for specific concerns
|
||||
*/
|
||||
export class ChangelogService extends EventEmitter {
|
||||
// Auto-detect Python command on initialization
|
||||
private pythonPath: string = findPythonCommand() || 'python';
|
||||
// Python path will be configured by pythonEnvManager after venv is ready
|
||||
private _pythonPath: string | null = null;
|
||||
private claudePath: string = 'claude';
|
||||
private autoBuildSourcePath: string = '';
|
||||
private cachedEnv: Record<string, string> | null = null;
|
||||
@@ -130,13 +130,25 @@ export class ChangelogService extends EventEmitter {
|
||||
*/
|
||||
configure(pythonPath?: string, autoBuildSourcePath?: string): void {
|
||||
if (pythonPath) {
|
||||
this.pythonPath = pythonPath;
|
||||
this._pythonPath = pythonPath;
|
||||
}
|
||||
if (autoBuildSourcePath) {
|
||||
this.autoBuildSourcePath = autoBuildSourcePath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured Python path.
|
||||
* Returns explicitly configured path, or falls back to getConfiguredPythonPath()
|
||||
* which uses the venv Python if ready.
|
||||
*/
|
||||
private get pythonPath(): string {
|
||||
if (this._pythonPath) {
|
||||
return this._pythonPath;
|
||||
}
|
||||
return getConfiguredPythonPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the auto-claude source path (detects automatically if not configured)
|
||||
*/
|
||||
|
||||
@@ -2,15 +2,16 @@ import path from 'path';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { app } from 'electron';
|
||||
import { getProfileEnv } from '../rate-limit-detector';
|
||||
import { findPythonCommand } from '../python-detector';
|
||||
import { getConfiguredPythonPath } from '../python-env-manager';
|
||||
|
||||
/**
|
||||
* Configuration manager for insights service
|
||||
* Handles path detection and environment variable loading
|
||||
*/
|
||||
export class InsightsConfig {
|
||||
// Auto-detect Python command on initialization
|
||||
private pythonPath: string = findPythonCommand() || 'python';
|
||||
// Python path will be configured by pythonEnvManager after venv is ready
|
||||
// Use getter to always get current configured path
|
||||
private _pythonPath: string | null = null;
|
||||
private autoBuildSourcePath: string = '';
|
||||
|
||||
/**
|
||||
@@ -18,7 +19,7 @@ export class InsightsConfig {
|
||||
*/
|
||||
configure(pythonPath?: string, autoBuildSourcePath?: string): void {
|
||||
if (pythonPath) {
|
||||
this.pythonPath = pythonPath;
|
||||
this._pythonPath = pythonPath;
|
||||
}
|
||||
if (autoBuildSourcePath) {
|
||||
this.autoBuildSourcePath = autoBuildSourcePath;
|
||||
@@ -26,10 +27,17 @@ export class InsightsConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured Python path
|
||||
* Get configured Python path.
|
||||
* Returns explicitly configured path, or falls back to getConfiguredPythonPath()
|
||||
* which uses the venv Python if ready.
|
||||
*/
|
||||
getPythonPath(): string {
|
||||
return this.pythonPath;
|
||||
// If explicitly configured (by pythonEnvManager), use that
|
||||
if (this._pythonPath) {
|
||||
return this._pythonPath;
|
||||
}
|
||||
// Otherwise use the global configured path (venv if ready, else bundled/system)
|
||||
return getConfiguredPythonPath();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ipcMain, app } from 'electron';
|
||||
import { ipcMain } from 'electron';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import path from 'path';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
buildMemoryStatus
|
||||
} from './memory-status-handlers';
|
||||
import { loadFileBasedMemories } from './memory-data-handlers';
|
||||
import { findPythonCommand, parsePythonCommand } from '../../python-detector';
|
||||
import { parsePythonCommand } from '../../python-detector';
|
||||
import { getConfiguredPythonPath } from '../../python-env-manager';
|
||||
import { getAugmentedEnv } from '../../env-utils';
|
||||
|
||||
/**
|
||||
@@ -159,20 +160,10 @@ export function registerProjectContextHandlers(
|
||||
const analyzerPath = path.join(autoBuildSource, 'analyzer.py');
|
||||
const indexOutputPath = path.join(project.path, AUTO_BUILD_PATHS.PROJECT_INDEX);
|
||||
|
||||
// Get Python command directly from settings file (not pythonEnvManager which creates NEW venv)
|
||||
let pythonCmd = findPythonCommand() || 'python3';
|
||||
try {
|
||||
const settingsPath = path.join(app.getPath('userData'), 'settings.json');
|
||||
if (existsSync(settingsPath)) {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
||||
if (settings.pythonPath && existsSync(settings.pythonPath)) {
|
||||
pythonCmd = settings.pythonPath;
|
||||
console.log('[project-context] Using Python from settings:', pythonCmd);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[project-context] Could not read Python from settings:', err);
|
||||
}
|
||||
// Get configured Python path (venv if ready, otherwise bundled/system)
|
||||
// This ensures we use the venv Python which has dependencies installed
|
||||
const pythonCmd = getConfiguredPythonPath();
|
||||
console.log('[project-context] Using Python:', pythonCmd);
|
||||
|
||||
const [pythonCommand, pythonBaseArgs] = parsePythonCommand(pythonCmd);
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ import {
|
||||
isKuzuAvailable,
|
||||
} from '../memory-service';
|
||||
import { validateOpenAIApiKey } from '../api-validation-service';
|
||||
import { findPythonCommand, parsePythonCommand } from '../python-detector';
|
||||
import { parsePythonCommand } from '../python-detector';
|
||||
import { getConfiguredPythonPath } from '../python-env-manager';
|
||||
|
||||
/**
|
||||
* Ollama Service Status
|
||||
@@ -104,10 +105,9 @@ async function executeOllamaDetector(
|
||||
command: string,
|
||||
baseUrl?: string
|
||||
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
||||
const pythonCmd = findPythonCommand();
|
||||
if (!pythonCmd) {
|
||||
return { success: false, error: 'Python not found' };
|
||||
}
|
||||
// Use configured Python path (venv if ready, otherwise bundled/system)
|
||||
// Note: ollama_model_detector.py doesn't require dotenv, but using venv is safer
|
||||
const pythonCmd = getConfiguredPythonPath();
|
||||
|
||||
// Find the ollama_model_detector.py script
|
||||
const possiblePaths = [
|
||||
@@ -543,10 +543,8 @@ export function registerMemoryHandlers(): void {
|
||||
baseUrl?: string
|
||||
): Promise<IPCResult<OllamaPullResult>> => {
|
||||
try {
|
||||
const pythonCmd = findPythonCommand();
|
||||
if (!pythonCmd) {
|
||||
return { success: false, error: 'Python not found' };
|
||||
}
|
||||
// Use configured Python path (venv if ready, otherwise bundled/system)
|
||||
const pythonCmd = getConfiguredPythonPath();
|
||||
|
||||
// Find the ollama_model_detector.py script
|
||||
const possiblePaths = [
|
||||
|
||||
@@ -5,11 +5,11 @@ import path from 'path';
|
||||
import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
|
||||
import { execSync, spawn, spawnSync } from 'child_process';
|
||||
import { projectStore } from '../../project-store';
|
||||
import { PythonEnvManager } from '../../python-env-manager';
|
||||
import { getConfiguredPythonPath, PythonEnvManager } from '../../python-env-manager';
|
||||
import { getEffectiveSourcePath } from '../../auto-claude-updater';
|
||||
import { getProfileEnv } from '../../rate-limit-detector';
|
||||
import { findTaskAndProject } from './shared';
|
||||
import { findPythonCommand, parsePythonCommand } from '../../python-detector';
|
||||
import { parsePythonCommand } from '../../python-detector';
|
||||
|
||||
/**
|
||||
* Read the stored base branch from task_metadata.json
|
||||
@@ -354,7 +354,8 @@ export function registerWorktreeHandlers(
|
||||
debug('Using stored base branch:', taskBaseBranch);
|
||||
}
|
||||
|
||||
const pythonPath = pythonEnvManager.getPythonPath() || findPythonCommand() || 'python';
|
||||
// Use configured Python path (venv if ready, otherwise bundled/system)
|
||||
const pythonPath = getConfiguredPythonPath();
|
||||
debug('Running command:', pythonPath, args.join(' '));
|
||||
debug('Working directory:', sourcePath);
|
||||
|
||||
@@ -711,7 +712,8 @@ export function registerWorktreeHandlers(
|
||||
console.warn('[IPC] Using stored base branch for preview:', taskBaseBranch);
|
||||
}
|
||||
|
||||
const pythonPath = pythonEnvManager.getPythonPath() || findPythonCommand() || 'python';
|
||||
// Use configured Python path (venv if ready, otherwise bundled/system)
|
||||
const pythonPath = getConfiguredPythonPath();
|
||||
console.warn('[IPC] Running merge preview:', pythonPath, args.join(' '));
|
||||
|
||||
// Get profile environment for consistency
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { app } from 'electron';
|
||||
import { findPythonCommand, parsePythonCommand } from './python-detector';
|
||||
import { getConfiguredPythonPath } from './python-env-manager';
|
||||
import { getMemoriesDir } from './config-paths';
|
||||
import type { MemoryEpisode } from '../shared/types';
|
||||
|
||||
@@ -120,10 +120,7 @@ async function executeQuery(
|
||||
args: string[],
|
||||
timeout: number = 10000
|
||||
): Promise<QueryResult> {
|
||||
const pythonCmd = findPythonCommand();
|
||||
if (!pythonCmd) {
|
||||
return { success: false, error: 'Python not found' };
|
||||
}
|
||||
const pythonCmd = getConfiguredPythonPath();
|
||||
|
||||
const scriptPath = getQueryScriptPath();
|
||||
if (!scriptPath) {
|
||||
@@ -186,10 +183,7 @@ async function executeSemanticQuery(
|
||||
embedderConfig: EmbedderConfig,
|
||||
timeout: number = 30000 // Longer timeout for embedding operations
|
||||
): Promise<QueryResult> {
|
||||
const pythonCmd = findPythonCommand();
|
||||
if (!pythonCmd) {
|
||||
return { success: false, error: 'Python not found' };
|
||||
}
|
||||
const pythonCmd = getConfiguredPythonPath();
|
||||
|
||||
const scriptPath = getQueryScriptPath();
|
||||
if (!scriptPath) {
|
||||
@@ -591,7 +585,7 @@ export async function closeMemoryService(): Promise<void> {
|
||||
* Check if Python with LadybugDB is available
|
||||
*/
|
||||
export function isKuzuAvailable(): boolean {
|
||||
// Check if Python is available
|
||||
// Check if Python is available (findPythonCommand can return null)
|
||||
const pythonCmd = findPythonCommand();
|
||||
if (!pythonCmd) {
|
||||
return false;
|
||||
@@ -619,7 +613,7 @@ export function getMemoryServiceStatus(dbPath?: string): MemoryServiceStatus {
|
||||
? fs.readdirSync(basePath).filter((name) => !name.startsWith('.'))
|
||||
: [];
|
||||
|
||||
// Check if Python and script are available
|
||||
// Check if Python and script are available (findPythonCommand can return null)
|
||||
const pythonAvailable = findPythonCommand() !== null;
|
||||
const scriptAvailable = getQueryScriptPath() !== null;
|
||||
|
||||
|
||||
@@ -432,3 +432,28 @@ export class PythonEnvManager extends EventEmitter {
|
||||
|
||||
// Singleton instance
|
||||
export const pythonEnvManager = new PythonEnvManager();
|
||||
|
||||
/**
|
||||
* Get the configured venv Python path if ready, otherwise fall back to system Python.
|
||||
* This should be used by ALL services that need to spawn Python processes.
|
||||
*
|
||||
* Priority:
|
||||
* 1. If venv is ready -> return venv Python (has all dependencies installed)
|
||||
* 2. Fall back to findPythonCommand() -> bundled or system Python
|
||||
*
|
||||
* Note: For scripts that require dependencies (dotenv, claude-agent-sdk, etc.),
|
||||
* the venv Python MUST be used. Only use this fallback for scripts that
|
||||
* don't have external dependencies (like ollama_model_detector.py).
|
||||
*/
|
||||
export function getConfiguredPythonPath(): string {
|
||||
// If venv is ready, always prefer it (has dependencies installed)
|
||||
if (pythonEnvManager.isEnvReady()) {
|
||||
const venvPath = pythonEnvManager.getPythonPath();
|
||||
if (venvPath) {
|
||||
return venvPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to system/bundled Python
|
||||
return findPythonCommand() || 'python';
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { spawn } from 'child_process';
|
||||
import { app } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
import { detectRateLimit, createSDKRateLimitInfo, getProfileEnv } from './rate-limit-detector';
|
||||
import { findPythonCommand, parsePythonCommand } from './python-detector';
|
||||
import { parsePythonCommand } from './python-detector';
|
||||
import { getConfiguredPythonPath } from './python-env-manager';
|
||||
|
||||
/**
|
||||
* Debug logging - only logs when DEBUG=true or in development mode
|
||||
@@ -21,8 +22,8 @@ function debug(...args: unknown[]): void {
|
||||
* Service for generating task titles from descriptions using Claude AI
|
||||
*/
|
||||
export class TitleGenerator extends EventEmitter {
|
||||
// Auto-detect Python command on initialization
|
||||
private pythonPath: string = findPythonCommand() || 'python';
|
||||
// Python path will be configured by pythonEnvManager after venv is ready
|
||||
private _pythonPath: string | null = null;
|
||||
private autoBuildSourcePath: string = '';
|
||||
|
||||
constructor() {
|
||||
@@ -35,13 +36,25 @@ export class TitleGenerator extends EventEmitter {
|
||||
*/
|
||||
configure(pythonPath?: string, autoBuildSourcePath?: string): void {
|
||||
if (pythonPath) {
|
||||
this.pythonPath = pythonPath;
|
||||
this._pythonPath = pythonPath;
|
||||
}
|
||||
if (autoBuildSourcePath) {
|
||||
this.autoBuildSourcePath = autoBuildSourcePath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured Python path.
|
||||
* Returns explicitly configured path, or falls back to getConfiguredPythonPath()
|
||||
* which uses the venv Python if ready.
|
||||
*/
|
||||
private get pythonPath(): string {
|
||||
if (this._pythonPath) {
|
||||
return this._pythonPath;
|
||||
}
|
||||
return getConfiguredPythonPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the auto-claude source path (detects automatically if not configured)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user