oh-langfuse 0.1.78 → 0.1.80
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/SELF_VERIFY.md +25 -25
- package/bin/cli.js +23 -14
- package/codex_langfuse_notify.py +162 -2
- package/langfuse_hook.py +165 -2
- package/package.json +2 -1
- package/scripts/auto-update-runtime.mjs +14 -14
- package/scripts/cli-detection-utils.mjs +114 -114
- package/scripts/codex-langfuse-check.mjs +65 -65
- package/scripts/codex-langfuse-setup.mjs +127 -116
- package/scripts/json-utils.mjs +99 -99
- package/scripts/langfuse-check.mjs +50 -50
- package/scripts/langfuse-setup.mjs +139 -128
- package/scripts/opencode-langfuse-check.mjs +208 -199
- package/scripts/opencode-langfuse-run.mjs +12 -1
- package/scripts/opencode-langfuse-setup.mjs +88 -6
- package/scripts/opencode-skills-discover.mjs +125 -0
- package/scripts/real-self-verify.mjs +97 -85
- package/scripts/resolve-opencode-cli.mjs +53 -53
- package/scripts/update-langfuse-runtime.mjs +39 -22
- package/setup-langfuse.bat +19 -19
- package/setup-langfuse.sh +19 -19
|
@@ -2,17 +2,27 @@ import fs from "node:fs";
|
|
|
2
2
|
import fsp from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import os from "node:os";
|
|
5
|
-
import { execFileSync, spawnSync } from "node:child_process";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { defaultWindowsNpmCliCandidates, listSystemCliCandidates } from "./cli-detection-utils.mjs";
|
|
8
|
-
import { writeRuntimeInstallRecord } from "./runtime-state-utils.mjs";
|
|
5
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { defaultWindowsNpmCliCandidates, listSystemCliCandidates } from "./cli-detection-utils.mjs";
|
|
8
|
+
import { writeRuntimeInstallRecord } from "./runtime-state-utils.mjs";
|
|
9
9
|
|
|
10
10
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
11
|
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
12
12
|
|
|
13
|
+
const DEFAULT_LANGFUSE_BASE_URL = "https://metrics.openharmonyinsight.cn";
|
|
14
|
+
const LEGACY_LANGFUSE_BASE_URLS = new Set([
|
|
15
|
+
"http://120.46.221.227:3000",
|
|
16
|
+
"https://120.46.221.227:3000",
|
|
17
|
+
]);
|
|
13
18
|
const USER_ID_PATTERN = /^[a-z](?:\d{8}|wx\d{7})$/;
|
|
14
19
|
const USER_ID_PATTERN_TEXT = "^[a-z](?:\\d{8}|wx\\d{7})$";
|
|
15
20
|
|
|
21
|
+
function normalizeLegacyBaseUrl(baseUrl) {
|
|
22
|
+
const value = String(baseUrl || "").trim().replace(/\/+$/, "");
|
|
23
|
+
return LEGACY_LANGFUSE_BASE_URLS.has(value) ? DEFAULT_LANGFUSE_BASE_URL : baseUrl;
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
function normalizeUserId(v) {
|
|
17
27
|
return String(v || "").trim();
|
|
18
28
|
}
|
|
@@ -162,7 +172,7 @@ function writeAutoUpdateHelper(target) {
|
|
|
162
172
|
const helperDir = autoUpdateHelperDir();
|
|
163
173
|
ensureDir(helperDir);
|
|
164
174
|
const runtimePath = autoUpdateRuntimePath();
|
|
165
|
-
const fallbackArgs = ["-y", "--registry=https://registry.npmjs.org/", "oh-langfuse@latest", "auto-update", target, "--skip-check", "--startup-status"];
|
|
175
|
+
const fallbackArgs = ["-y", "--registry=https://registry.npmjs.org/", "oh-langfuse@latest", "auto-update", target, "--skip-check", "--startup-status"];
|
|
166
176
|
|
|
167
177
|
if (process.platform === "win32") {
|
|
168
178
|
const helper = path.join(helperDir, `oh-langfuse-auto-update-${target}.cmd`);
|
|
@@ -177,7 +187,7 @@ function writeAutoUpdateHelper(target) {
|
|
|
177
187
|
"exit /b 0",
|
|
178
188
|
""
|
|
179
189
|
];
|
|
180
|
-
fs.writeFileSync(helper, lines.join(os.EOL), "utf8");
|
|
190
|
+
fs.writeFileSync(helper, lines.join(os.EOL), "utf8");
|
|
181
191
|
return helper;
|
|
182
192
|
}
|
|
183
193
|
|
|
@@ -222,16 +232,16 @@ function existingCliCandidate(candidate, shimDir) {
|
|
|
222
232
|
}
|
|
223
233
|
}
|
|
224
234
|
|
|
225
|
-
function listCliCandidatesFromPath(target) {
|
|
226
|
-
return listSystemCliCandidates(target);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function resolveAgentCli({ target, preferred = "", shimDir = "" }) {
|
|
230
|
-
const candidates = [
|
|
231
|
-
preferred,
|
|
232
|
-
...defaultWindowsNpmCliCandidates(target),
|
|
233
|
-
...listCliCandidatesFromPath(target)
|
|
234
|
-
];
|
|
235
|
+
function listCliCandidatesFromPath(target) {
|
|
236
|
+
return listSystemCliCandidates(target);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function resolveAgentCli({ target, preferred = "", shimDir = "" }) {
|
|
240
|
+
const candidates = [
|
|
241
|
+
preferred,
|
|
242
|
+
...defaultWindowsNpmCliCandidates(target),
|
|
243
|
+
...listCliCandidatesFromPath(target)
|
|
244
|
+
];
|
|
235
245
|
for (const candidate of candidates) {
|
|
236
246
|
const found = existingCliCandidate(candidate, shimDir);
|
|
237
247
|
if (found) return found;
|
|
@@ -285,7 +295,7 @@ function writeAgentCommandShim({ baseDir, target, executable, realCli, publicKey
|
|
|
285
295
|
"exit /b %ERRORLEVEL%",
|
|
286
296
|
""
|
|
287
297
|
].filter(Boolean).join(os.EOL);
|
|
288
|
-
fs.writeFileSync(shim, content, "utf8");
|
|
298
|
+
fs.writeFileSync(shim, content, "utf8");
|
|
289
299
|
return { shim, shimDir };
|
|
290
300
|
}
|
|
291
301
|
|
|
@@ -317,7 +327,7 @@ function createAgentLauncher({ baseDir, target, executable }) {
|
|
|
317
327
|
`${executable} %*`,
|
|
318
328
|
""
|
|
319
329
|
].join(os.EOL);
|
|
320
|
-
fs.writeFileSync(launcher, content, "utf8");
|
|
330
|
+
fs.writeFileSync(launcher, content, "utf8");
|
|
321
331
|
return launcher;
|
|
322
332
|
}
|
|
323
333
|
|
|
@@ -340,87 +350,87 @@ function pythonExecutableInVenv(venvDir) {
|
|
|
340
350
|
: path.join(venvDir, "bin", "python");
|
|
341
351
|
}
|
|
342
352
|
|
|
343
|
-
function pythonEnv(extraPythonPath = "") {
|
|
344
|
-
const env = { ...process.env, PYTHONUTF8: "1" };
|
|
345
|
-
if (extraPythonPath) {
|
|
346
|
-
env.PYTHONPATH = process.env.PYTHONPATH ? `${extraPythonPath}${path.delimiter}${process.env.PYTHONPATH}` : extraPythonPath;
|
|
347
|
-
}
|
|
348
|
-
return env;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function pythonCanImport(pythonCmd, moduleName, extraPythonPath = "") {
|
|
352
|
-
try {
|
|
353
|
-
execFileSync(pythonCmd, ["-c", `import ${moduleName}`], { stdio: "ignore", encoding: "utf8", env: pythonEnv(extraPythonPath) });
|
|
354
|
-
return true;
|
|
355
|
-
} catch {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function installLangfuseIntoTargetDir({ pythonCmd, baseDir, pipIndexUrl }) {
|
|
361
|
-
const targetDir = path.join(baseDir, "langfuse-python");
|
|
362
|
-
ensureDir(targetDir);
|
|
363
|
-
console.log(`Installing Python package into isolated target: ${targetDir}`);
|
|
364
|
-
execFileSync(
|
|
365
|
-
pythonCmd,
|
|
366
|
-
["-m", "pip", "install", "--target", targetDir, "--upgrade", "langfuse", "-i", pipIndexUrl],
|
|
367
|
-
{ stdio: "inherit", encoding: "utf8", env: pythonEnv() }
|
|
368
|
-
);
|
|
369
|
-
if (!pythonCanImport(pythonCmd, "langfuse", targetDir)) {
|
|
370
|
-
throw new Error(`langfuse was installed into ${targetDir}, but ${pythonCmd} still cannot import it with PYTHONPATH.`);
|
|
371
|
-
}
|
|
372
|
-
return { python: pythonCmd, pythonPath: targetDir };
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function runPipInstallWithFallback({ pythonCmd, baseDir, pipIndexUrl }) {
|
|
376
|
-
const attempts = [
|
|
377
|
-
() => installLangfuseIntoTargetDir({ pythonCmd, baseDir, pipIndexUrl }),
|
|
378
|
-
() => installLangfuseIntoTargetDir({ pythonCmd: "python3", baseDir, pipIndexUrl }),
|
|
379
|
-
() => installLangfuseIntoTargetDir({ pythonCmd: "python", baseDir, pipIndexUrl })
|
|
380
|
-
];
|
|
381
|
-
|
|
382
|
-
const errors = [];
|
|
383
|
-
for (const attempt of attempts) {
|
|
384
|
-
try {
|
|
385
|
-
return attempt();
|
|
386
|
-
} catch (error) {
|
|
387
|
-
errors.push(error?.message || String(error));
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
throw new Error(
|
|
392
|
-
`Failed to install langfuse into isolated Python target. Last errors: ${errors.join(" | ")}`
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl }) {
|
|
397
|
-
console.log("Python venv is unavailable; falling back to isolated PYTHONPATH install for langfuse.");
|
|
398
|
-
return runPipInstallWithFallback({ pythonCmd, baseDir, pipIndexUrl });
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
async function createHookLauncher({ hooksDir, hookPython, hookPythonPath = "", pyPath }) {
|
|
402
|
-
if (process.platform === "win32") {
|
|
403
|
-
const launcher = path.join(hooksDir, "run-langfuse-hook.cmd");
|
|
404
|
-
const content = [
|
|
405
|
-
"@echo off",
|
|
406
|
-
hookPythonPath ? `set PYTHONPATH=${hookPythonPath};%PYTHONPATH%` : null,
|
|
407
|
-
`${cmdQuote(hookPython)} ${cmdQuote(pyPath)}`,
|
|
408
|
-
""
|
|
409
|
-
].filter(Boolean).join(os.EOL);
|
|
410
|
-
await fsp.writeFile(launcher, content, "utf8");
|
|
411
|
-
return launcher;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const launcher = path.join(hooksDir, "run-langfuse-hook.sh");
|
|
415
|
-
const content = [
|
|
416
|
-
"#!/usr/bin/env sh",
|
|
417
|
-
hookPythonPath ? `if [ -n "\${PYTHONPATH:-}" ]; then export PYTHONPATH=${shQuote(hookPythonPath)}:"$PYTHONPATH"; else export PYTHONPATH=${shQuote(hookPythonPath)}; fi` : null,
|
|
418
|
-
`exec ${shQuote(hookPython)} ${shQuote(pyPath)}`,
|
|
419
|
-
""
|
|
420
|
-
].filter(Boolean).join("\n");
|
|
421
|
-
await fsp.writeFile(launcher, content, "utf8");
|
|
422
|
-
fs.chmodSync(launcher, 0o755);
|
|
423
|
-
return launcher;
|
|
353
|
+
function pythonEnv(extraPythonPath = "") {
|
|
354
|
+
const env = { ...process.env, PYTHONUTF8: "1" };
|
|
355
|
+
if (extraPythonPath) {
|
|
356
|
+
env.PYTHONPATH = process.env.PYTHONPATH ? `${extraPythonPath}${path.delimiter}${process.env.PYTHONPATH}` : extraPythonPath;
|
|
357
|
+
}
|
|
358
|
+
return env;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function pythonCanImport(pythonCmd, moduleName, extraPythonPath = "") {
|
|
362
|
+
try {
|
|
363
|
+
execFileSync(pythonCmd, ["-c", `import ${moduleName}`], { stdio: "ignore", encoding: "utf8", env: pythonEnv(extraPythonPath) });
|
|
364
|
+
return true;
|
|
365
|
+
} catch {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function installLangfuseIntoTargetDir({ pythonCmd, baseDir, pipIndexUrl }) {
|
|
371
|
+
const targetDir = path.join(baseDir, "langfuse-python");
|
|
372
|
+
ensureDir(targetDir);
|
|
373
|
+
console.log(`Installing Python package into isolated target: ${targetDir}`);
|
|
374
|
+
execFileSync(
|
|
375
|
+
pythonCmd,
|
|
376
|
+
["-m", "pip", "install", "--target", targetDir, "--upgrade", "langfuse", "-i", pipIndexUrl],
|
|
377
|
+
{ stdio: "inherit", encoding: "utf8", env: pythonEnv() }
|
|
378
|
+
);
|
|
379
|
+
if (!pythonCanImport(pythonCmd, "langfuse", targetDir)) {
|
|
380
|
+
throw new Error(`langfuse was installed into ${targetDir}, but ${pythonCmd} still cannot import it with PYTHONPATH.`);
|
|
381
|
+
}
|
|
382
|
+
return { python: pythonCmd, pythonPath: targetDir };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function runPipInstallWithFallback({ pythonCmd, baseDir, pipIndexUrl }) {
|
|
386
|
+
const attempts = [
|
|
387
|
+
() => installLangfuseIntoTargetDir({ pythonCmd, baseDir, pipIndexUrl }),
|
|
388
|
+
() => installLangfuseIntoTargetDir({ pythonCmd: "python3", baseDir, pipIndexUrl }),
|
|
389
|
+
() => installLangfuseIntoTargetDir({ pythonCmd: "python", baseDir, pipIndexUrl })
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
const errors = [];
|
|
393
|
+
for (const attempt of attempts) {
|
|
394
|
+
try {
|
|
395
|
+
return attempt();
|
|
396
|
+
} catch (error) {
|
|
397
|
+
errors.push(error?.message || String(error));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
throw new Error(
|
|
402
|
+
`Failed to install langfuse into isolated Python target. Last errors: ${errors.join(" | ")}`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl }) {
|
|
407
|
+
console.log("Python venv is unavailable; falling back to isolated PYTHONPATH install for langfuse.");
|
|
408
|
+
return runPipInstallWithFallback({ pythonCmd, baseDir, pipIndexUrl });
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function createHookLauncher({ hooksDir, hookPython, hookPythonPath = "", pyPath }) {
|
|
412
|
+
if (process.platform === "win32") {
|
|
413
|
+
const launcher = path.join(hooksDir, "run-langfuse-hook.cmd");
|
|
414
|
+
const content = [
|
|
415
|
+
"@echo off",
|
|
416
|
+
hookPythonPath ? `set PYTHONPATH=${hookPythonPath};%PYTHONPATH%` : null,
|
|
417
|
+
`${cmdQuote(hookPython)} ${cmdQuote(pyPath)}`,
|
|
418
|
+
""
|
|
419
|
+
].filter(Boolean).join(os.EOL);
|
|
420
|
+
await fsp.writeFile(launcher, content, "utf8");
|
|
421
|
+
return launcher;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const launcher = path.join(hooksDir, "run-langfuse-hook.sh");
|
|
425
|
+
const content = [
|
|
426
|
+
"#!/usr/bin/env sh",
|
|
427
|
+
hookPythonPath ? `if [ -n "\${PYTHONPATH:-}" ]; then export PYTHONPATH=${shQuote(hookPythonPath)}:"$PYTHONPATH"; else export PYTHONPATH=${shQuote(hookPythonPath)}; fi` : null,
|
|
428
|
+
`exec ${shQuote(hookPython)} ${shQuote(pyPath)}`,
|
|
429
|
+
""
|
|
430
|
+
].filter(Boolean).join("\n");
|
|
431
|
+
await fsp.writeFile(launcher, content, "utf8");
|
|
432
|
+
fs.chmodSync(launcher, 0o755);
|
|
433
|
+
return launcher;
|
|
424
434
|
}
|
|
425
435
|
|
|
426
436
|
function createOrUpdateLangfuseVenv({ baseDir, pipIndexUrl = "https://pypi.tuna.tsinghua.edu.cn/simple" }) {
|
|
@@ -431,13 +441,13 @@ function createOrUpdateLangfuseVenv({ baseDir, pipIndexUrl = "https://pypi.tuna.
|
|
|
431
441
|
if (!fs.existsSync(venvPython)) {
|
|
432
442
|
console.log(`Creating Python virtual environment: ${venvDir}`);
|
|
433
443
|
try {
|
|
434
|
-
execFileSync(pythonCmd, ["-m", "venv", venvDir], { stdio: "inherit", encoding: "utf8", env: { ...process.env, PYTHONUTF8: "1" } });
|
|
435
|
-
} catch (e) {
|
|
436
|
-
if (process.platform !== "win32") {
|
|
437
|
-
return installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl });
|
|
438
|
-
}
|
|
439
|
-
throw new Error("Failed to create Python venv. Please confirm Python venv support is available.");
|
|
440
|
-
}
|
|
444
|
+
execFileSync(pythonCmd, ["-m", "venv", venvDir], { stdio: "inherit", encoding: "utf8", env: { ...process.env, PYTHONUTF8: "1" } });
|
|
445
|
+
} catch (e) {
|
|
446
|
+
if (process.platform !== "win32") {
|
|
447
|
+
return installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl });
|
|
448
|
+
}
|
|
449
|
+
throw new Error("Failed to create Python venv. Please confirm Python venv support is available.");
|
|
450
|
+
}
|
|
441
451
|
}
|
|
442
452
|
|
|
443
453
|
console.log("Installing/updating Python package in venv: langfuse");
|
|
@@ -446,23 +456,23 @@ function createOrUpdateLangfuseVenv({ baseDir, pipIndexUrl = "https://pypi.tuna.
|
|
|
446
456
|
venvPython,
|
|
447
457
|
["-m", "pip", "install", "-U", "langfuse", "-i", pipIndexUrl],
|
|
448
458
|
{ stdio: "inherit", encoding: "utf8", env: { ...process.env, PYTHONUTF8: "1" } }
|
|
449
|
-
);
|
|
450
|
-
} catch (e) {
|
|
451
|
-
if (process.platform !== "win32") {
|
|
452
|
-
return installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl });
|
|
453
|
-
}
|
|
454
|
-
throw new Error(`Failed to install langfuse in venv: ${venvPython} -m pip install -U langfuse -i ${pipIndexUrl}`);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (!pythonCanImport(venvPython, "langfuse")) {
|
|
458
|
-
if (process.platform !== "win32") {
|
|
459
|
-
return installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl });
|
|
460
|
-
}
|
|
461
|
-
throw new Error(`langfuse was installed in venv, but cannot be imported by ${venvPython}`);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return { python: venvPython, pythonPath: "" };
|
|
465
|
-
}
|
|
459
|
+
);
|
|
460
|
+
} catch (e) {
|
|
461
|
+
if (process.platform !== "win32") {
|
|
462
|
+
return installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl });
|
|
463
|
+
}
|
|
464
|
+
throw new Error(`Failed to install langfuse in venv: ${venvPython} -m pip install -U langfuse -i ${pipIndexUrl}`);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (!pythonCanImport(venvPython, "langfuse")) {
|
|
468
|
+
if (process.platform !== "win32") {
|
|
469
|
+
return installLangfuseWithSystemPython({ pythonCmd, baseDir, pipIndexUrl });
|
|
470
|
+
}
|
|
471
|
+
throw new Error(`langfuse was installed in venv, but cannot be imported by ${venvPython}`);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return { python: venvPython, pythonPath: "" };
|
|
475
|
+
}
|
|
466
476
|
|
|
467
477
|
async function main() {
|
|
468
478
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -473,13 +483,14 @@ async function main() {
|
|
|
473
483
|
}
|
|
474
484
|
assertValidUserId(userId);
|
|
475
485
|
|
|
476
|
-
const langfuseHost =
|
|
486
|
+
const langfuseHost = normalizeLegacyBaseUrl(
|
|
477
487
|
args.langfuseBaseUrl ||
|
|
478
488
|
args.langfuseHost ||
|
|
479
489
|
args.host ||
|
|
480
490
|
process.env.LANGFUSE_BASEURL ||
|
|
481
491
|
process.env.LANGFUSE_HOST ||
|
|
482
|
-
|
|
492
|
+
DEFAULT_LANGFUSE_BASE_URL
|
|
493
|
+
);
|
|
483
494
|
|
|
484
495
|
const publicKey =
|
|
485
496
|
args.publicKey || process.env.LANGFUSE_PUBLIC_KEY || "pk-lf-da0c90a7-6e93-4eb7-bb86-c1047c8d187d";
|
|
@@ -543,9 +554,9 @@ async function main() {
|
|
|
543
554
|
if (nextPyText !== pyText) {
|
|
544
555
|
await fsp.writeFile(pyPath, nextPyText, "utf8");
|
|
545
556
|
}
|
|
546
|
-
const hookRuntime = createOrUpdateLangfuseVenv({ baseDir: claudeDir, pipIndexUrl });
|
|
547
|
-
const hookPython = hookRuntime.python;
|
|
548
|
-
const hookLauncher = await createHookLauncher({ hooksDir, hookPython, hookPythonPath: hookRuntime.pythonPath, pyPath });
|
|
557
|
+
const hookRuntime = createOrUpdateLangfuseVenv({ baseDir: claudeDir, pipIndexUrl });
|
|
558
|
+
const hookPython = hookRuntime.python;
|
|
559
|
+
const hookLauncher = await createHookLauncher({ hooksDir, hookPython, hookPythonPath: hookRuntime.pythonPath, pyPath });
|
|
549
560
|
const claudeShimDir = path.join(claudeDir, "bin");
|
|
550
561
|
const realClaudeCli = resolveAgentCli({ target: "claude", preferred: args.cmd || "", shimDir: claudeShimDir });
|
|
551
562
|
const directShim = writeAgentCommandShim({
|