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