trickle-cli 0.1.225 → 0.1.227

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.
@@ -886,7 +886,7 @@ async function handleRequest(req) {
886
886
  case "get_environment": {
887
887
  const envFile = path.join(findTrickleDir(), "environment.json");
888
888
  if (!fs.existsSync(envFile)) {
889
- result = { environment: "No environment snapshot. Run the app with trickle first." };
889
+ result = { environment: "No environment snapshot. Set TRICKLE_CAPTURE_ENV=1 and run the app with trickle to capture." };
890
890
  }
891
891
  else {
892
892
  try {
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.runCommand = runCommand;
40
40
  const path = __importStar(require("path"));
41
41
  const fs = __importStar(require("fs"));
42
+ const os = __importStar(require("os"));
42
43
  const child_process_1 = require("child_process");
43
44
  const chalk_1 = __importDefault(require("chalk"));
44
45
  const config_1 = require("../config");
@@ -1244,10 +1245,10 @@ function injectObservation(command, backendUrl, opts) {
1244
1245
  return { instrumentedCommand: command, env };
1245
1246
  }
1246
1247
  /**
1247
- * Ensure the target Python can import trickle by adding the trickle package's
1248
- * site-packages to PYTHONPATH. This handles the case where the user runs a venv
1249
- * Python that doesn't have trickle-observe installed we find it from the system
1250
- * Python (or any Python that has it) and inject the path.
1248
+ * Ensure the target Python can import trickle by creating an isolated temp
1249
+ * directory with a symlink to just the trickle package and adding it to PYTHONPATH.
1250
+ * This avoids polluting the target Python with the entire system site-packages,
1251
+ * which would cause binary incompatibilities across Python versions.
1251
1252
  */
1252
1253
  function ensureTricklePythonPath(targetPython, env) {
1253
1254
  // First check if the target Python already has trickle
@@ -1262,10 +1263,16 @@ function ensureTricklePythonPath(targetPython, env) {
1262
1263
  const candidates = ["python3", "python", "python3.11", "python3.12", "python3.13", "python3.10"];
1263
1264
  for (const py of candidates) {
1264
1265
  try {
1265
- const sitePath = (0, child_process_1.execSync)(`${py} -c "import trickle, os; print(os.path.dirname(os.path.dirname(trickle.__file__)))"`, { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] }).trim();
1266
- if (sitePath) {
1266
+ // Get the trickle package directory (e.g. /opt/anaconda3/.../site-packages/trickle)
1267
+ const trickleDir = (0, child_process_1.execSync)(`${py} -c "import trickle, os; print(os.path.dirname(trickle.__file__))"`, { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] }).trim();
1268
+ if (trickleDir && fs.existsSync(trickleDir)) {
1269
+ // Create a temp directory with just a symlink to the trickle package.
1270
+ // This ensures only trickle (pure Python) is visible, not incompatible
1271
+ // binary packages from a different Python version's site-packages.
1272
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "trickle-pypath-"));
1273
+ fs.symlinkSync(trickleDir, path.join(tmpDir, "trickle"));
1267
1274
  const existing = env.PYTHONPATH || process.env.PYTHONPATH || "";
1268
- env.PYTHONPATH = existing ? `${sitePath}:${existing}` : sitePath;
1275
+ env.PYTHONPATH = existing ? `${tmpDir}:${existing}` : tmpDir;
1269
1276
  return;
1270
1277
  }
1271
1278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trickle-cli",
3
- "version": "0.1.225",
3
+ "version": "0.1.227",
4
4
  "description": "Zero-code runtime observability for JS/Python + AI agent debugging. Traces LangChain, CrewAI, OpenAI, Anthropic, Gemini. Eval, security, compliance, cost tracking. Free, local-first.",
5
5
  "keywords": [
6
6
  "observability",
@@ -875,7 +875,7 @@ async function handleRequest(req: JsonRpcRequest): Promise<JsonRpcResponse> {
875
875
  case "get_environment": {
876
876
  const envFile = path.join(findTrickleDir(), "environment.json");
877
877
  if (!fs.existsSync(envFile)) {
878
- result = { environment: "No environment snapshot. Run the app with trickle first." };
878
+ result = { environment: "No environment snapshot. Set TRICKLE_CAPTURE_ENV=1 and run the app with trickle to capture." };
879
879
  } else {
880
880
  try { result = JSON.parse(fs.readFileSync(envFile, "utf-8")); } catch { result = { environment: "Failed to read environment snapshot." }; }
881
881
  }
@@ -1,5 +1,6 @@
1
1
  import * as path from "path";
2
2
  import * as fs from "fs";
3
+ import * as os from "os";
3
4
  import { spawn, execSync, ChildProcess } from "child_process";
4
5
  import chalk from "chalk";
5
6
  import { getBackendUrl } from "../config";
@@ -1366,10 +1367,10 @@ function injectObservation(
1366
1367
  }
1367
1368
 
1368
1369
  /**
1369
- * Ensure the target Python can import trickle by adding the trickle package's
1370
- * site-packages to PYTHONPATH. This handles the case where the user runs a venv
1371
- * Python that doesn't have trickle-observe installed we find it from the system
1372
- * Python (or any Python that has it) and inject the path.
1370
+ * Ensure the target Python can import trickle by creating an isolated temp
1371
+ * directory with a symlink to just the trickle package and adding it to PYTHONPATH.
1372
+ * This avoids polluting the target Python with the entire system site-packages,
1373
+ * which would cause binary incompatibilities across Python versions.
1373
1374
  */
1374
1375
  function ensureTricklePythonPath(
1375
1376
  targetPython: string,
@@ -1390,13 +1391,19 @@ function ensureTricklePythonPath(
1390
1391
  const candidates = ["python3", "python", "python3.11", "python3.12", "python3.13", "python3.10"];
1391
1392
  for (const py of candidates) {
1392
1393
  try {
1393
- const sitePath = execSync(
1394
- `${py} -c "import trickle, os; print(os.path.dirname(os.path.dirname(trickle.__file__)))"`,
1394
+ // Get the trickle package directory (e.g. /opt/anaconda3/.../site-packages/trickle)
1395
+ const trickleDir = execSync(
1396
+ `${py} -c "import trickle, os; print(os.path.dirname(trickle.__file__))"`,
1395
1397
  { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] },
1396
1398
  ).trim();
1397
- if (sitePath) {
1399
+ if (trickleDir && fs.existsSync(trickleDir)) {
1400
+ // Create a temp directory with just a symlink to the trickle package.
1401
+ // This ensures only trickle (pure Python) is visible, not incompatible
1402
+ // binary packages from a different Python version's site-packages.
1403
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "trickle-pypath-"));
1404
+ fs.symlinkSync(trickleDir, path.join(tmpDir, "trickle"));
1398
1405
  const existing = env.PYTHONPATH || process.env.PYTHONPATH || "";
1399
- env.PYTHONPATH = existing ? `${sitePath}:${existing}` : sitePath;
1406
+ env.PYTHONPATH = existing ? `${tmpDir}:${existing}` : tmpDir;
1400
1407
  return;
1401
1408
  }
1402
1409
  } catch {