trickle-cli 0.1.224 → 0.1.225

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.
@@ -1219,6 +1219,9 @@ function injectObservation(command, backendUrl, opts) {
1219
1219
  // Auto-enable terminal type summary when running via trickle run
1220
1220
  if (!process.env.TRICKLE_SUMMARY)
1221
1221
  env.TRICKLE_SUMMARY = "1";
1222
+ // Ensure trickle is importable even if the target Python is a venv without it.
1223
+ // Find trickle's install location from any available system Python and inject via PYTHONPATH.
1224
+ ensureTricklePythonPath(python, env);
1222
1225
  return {
1223
1226
  instrumentedCommand: `${python} -c "from trickle.observe_runner import main; main()" ${rest}`,
1224
1227
  env,
@@ -1229,6 +1232,7 @@ function injectObservation(command, backendUrl, opts) {
1229
1232
  env.TRICKLE_OBSERVE_INCLUDE = opts.include;
1230
1233
  if (opts.exclude)
1231
1234
  env.TRICKLE_OBSERVE_EXCLUDE = opts.exclude;
1235
+ ensureTricklePythonPath("python", env);
1232
1236
  return {
1233
1237
  instrumentedCommand: `python -c "from trickle.observe_runner import main; main()" -m ${command}`,
1234
1238
  env,
@@ -1239,6 +1243,37 @@ function injectObservation(command, backendUrl, opts) {
1239
1243
  env.NODE_OPTIONS = `${existing} -r ${observePath}`.trim();
1240
1244
  return { instrumentedCommand: command, env };
1241
1245
  }
1246
+ /**
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.
1251
+ */
1252
+ function ensureTricklePythonPath(targetPython, env) {
1253
+ // First check if the target Python already has trickle
1254
+ try {
1255
+ (0, child_process_1.execSync)(`${targetPython} -c "import trickle" 2>/dev/null`, { stdio: "ignore", timeout: 5000 });
1256
+ return; // trickle is already importable
1257
+ }
1258
+ catch {
1259
+ // Not available in target Python — find it elsewhere
1260
+ }
1261
+ // Try common Python commands to find where trickle is installed
1262
+ const candidates = ["python3", "python", "python3.11", "python3.12", "python3.13", "python3.10"];
1263
+ for (const py of candidates) {
1264
+ 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) {
1267
+ const existing = env.PYTHONPATH || process.env.PYTHONPATH || "";
1268
+ env.PYTHONPATH = existing ? `${sitePath}:${existing}` : sitePath;
1269
+ return;
1270
+ }
1271
+ }
1272
+ catch {
1273
+ continue;
1274
+ }
1275
+ }
1276
+ }
1242
1277
  function resolveObservePath() {
1243
1278
  try {
1244
1279
  return require.resolve("trickle-observe/observe");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trickle-cli",
3
- "version": "0.1.224",
3
+ "version": "0.1.225",
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",
@@ -1,6 +1,6 @@
1
1
  import * as path from "path";
2
2
  import * as fs from "fs";
3
- import { spawn, ChildProcess } from "child_process";
3
+ import { spawn, execSync, ChildProcess } from "child_process";
4
4
  import chalk from "chalk";
5
5
  import { getBackendUrl } from "../config";
6
6
  import {
@@ -1336,6 +1336,9 @@ function injectObservation(
1336
1336
  if (opts.exclude) env.TRICKLE_OBSERVE_EXCLUDE = opts.exclude;
1337
1337
  // Auto-enable terminal type summary when running via trickle run
1338
1338
  if (!process.env.TRICKLE_SUMMARY) env.TRICKLE_SUMMARY = "1";
1339
+ // Ensure trickle is importable even if the target Python is a venv without it.
1340
+ // Find trickle's install location from any available system Python and inject via PYTHONPATH.
1341
+ ensureTricklePythonPath(python, env);
1339
1342
  return {
1340
1343
  instrumentedCommand: `${python} -c "from trickle.observe_runner import main; main()" ${rest}`,
1341
1344
  env,
@@ -1345,6 +1348,7 @@ function injectObservation(
1345
1348
  if (/^(pytest|uvicorn|gunicorn|flask|django-admin)\b/.test(command)) {
1346
1349
  if (opts.include) env.TRICKLE_OBSERVE_INCLUDE = opts.include;
1347
1350
  if (opts.exclude) env.TRICKLE_OBSERVE_EXCLUDE = opts.exclude;
1351
+ ensureTricklePythonPath("python", env);
1348
1352
  return {
1349
1353
  instrumentedCommand: `python -c "from trickle.observe_runner import main; main()" -m ${command}`,
1350
1354
  env,
@@ -1361,6 +1365,46 @@ function injectObservation(
1361
1365
  return { instrumentedCommand: command, env };
1362
1366
  }
1363
1367
 
1368
+ /**
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.
1373
+ */
1374
+ function ensureTricklePythonPath(
1375
+ targetPython: string,
1376
+ env: Record<string, string>,
1377
+ ): void {
1378
+ // First check if the target Python already has trickle
1379
+ try {
1380
+ execSync(
1381
+ `${targetPython} -c "import trickle" 2>/dev/null`,
1382
+ { stdio: "ignore", timeout: 5000 },
1383
+ );
1384
+ return; // trickle is already importable
1385
+ } catch {
1386
+ // Not available in target Python — find it elsewhere
1387
+ }
1388
+
1389
+ // Try common Python commands to find where trickle is installed
1390
+ const candidates = ["python3", "python", "python3.11", "python3.12", "python3.13", "python3.10"];
1391
+ for (const py of candidates) {
1392
+ try {
1393
+ const sitePath = execSync(
1394
+ `${py} -c "import trickle, os; print(os.path.dirname(os.path.dirname(trickle.__file__)))"`,
1395
+ { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] },
1396
+ ).trim();
1397
+ if (sitePath) {
1398
+ const existing = env.PYTHONPATH || process.env.PYTHONPATH || "";
1399
+ env.PYTHONPATH = existing ? `${sitePath}:${existing}` : sitePath;
1400
+ return;
1401
+ }
1402
+ } catch {
1403
+ continue;
1404
+ }
1405
+ }
1406
+ }
1407
+
1364
1408
  function resolveObservePath(): string {
1365
1409
  try {
1366
1410
  return require.resolve("trickle-observe/observe");