oh-langfuse 0.1.79 → 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.
@@ -1,114 +1,114 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { spawnSync } from "node:child_process";
5
-
6
- function unique(values) {
7
- return [...new Set(values.filter(Boolean))];
8
- }
9
-
10
- function stripExecutableExtension(name) {
11
- return String(name || "").replace(/\.(cmd|bat|exe)$/i, "");
12
- }
13
-
14
- function isTargetBinary(target, candidate) {
15
- const base = stripExecutableExtension(path.basename(String(candidate || ""))).toLowerCase();
16
- return base === String(target || "").toLowerCase();
17
- }
18
-
19
- function sortWindowsCliCandidates(candidates) {
20
- const extPriority = (candidate) => {
21
- const ext = path.extname(String(candidate || "")).toLowerCase();
22
- if (ext === ".cmd" || ext === ".bat" || ext === ".exe") return 0;
23
- return 1;
24
- };
25
- return [...candidates].sort((a, b) => extPriority(a) - extPriority(b));
26
- }
27
-
28
- export function parseWhereisBinaryOutput(target, stdout) {
29
- const candidates = [];
30
- for (const line of String(stdout || "").split(/\r?\n/)) {
31
- const colon = line.indexOf(":");
32
- const body = colon === -1 ? line : line.slice(colon + 1);
33
- for (const token of body.split(/\s+/)) {
34
- const candidate = token.trim();
35
- if (candidate && path.isAbsolute(candidate) && isTargetBinary(target, candidate)) {
36
- candidates.push(candidate);
37
- }
38
- }
39
- }
40
- return unique(candidates);
41
- }
42
-
43
- function isLikelyNodePrefix(name) {
44
- return /^(node|nodejs)(?:[-_.].*)?$/i.test(String(name || ""));
45
- }
46
-
47
- export function listCommonUnixCliCandidates(target, roots = ["/usr/local", "/opt"]) {
48
- if (process.platform === "win32") return [];
49
- const candidates = [
50
- path.join("/usr/local", "bin", target),
51
- path.join("/usr", "bin", target),
52
- path.join("/bin", target),
53
- ];
54
- for (const root of roots) {
55
- try {
56
- if (!fs.existsSync(root)) continue;
57
- for (const ent of fs.readdirSync(root, { withFileTypes: true })) {
58
- if (ent.isDirectory() && isLikelyNodePrefix(ent.name)) {
59
- candidates.push(path.join(root, ent.name, "bin", target));
60
- }
61
- }
62
- } catch {
63
- // Best effort only; command lookups and other candidates may still work.
64
- }
65
- }
66
- return unique(candidates);
67
- }
68
-
69
- export function listSystemCliCandidates(target) {
70
- if (!target) return [];
71
- const candidates = [];
72
- if (process.platform === "win32") {
73
- const result = spawnSync("where.exe", [target], { encoding: "utf8", windowsHide: true });
74
- if (result.status === 0) {
75
- candidates.push(...String(result.stdout || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
76
- }
77
- return sortWindowsCliCandidates(unique(candidates));
78
- }
79
-
80
- const which = spawnSync("which", ["-a", target], { encoding: "utf8", windowsHide: true });
81
- if (which.status === 0) {
82
- candidates.push(...String(which.stdout || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
83
- }
84
-
85
- const whereis = spawnSync("whereis", ["-b", target], { encoding: "utf8", windowsHide: true });
86
- if (whereis.status === 0) {
87
- candidates.push(...parseWhereisBinaryOutput(target, whereis.stdout));
88
- }
89
-
90
- candidates.push(...listCommonUnixCliCandidates(target));
91
- return unique(candidates);
92
- }
93
-
94
- export function candidateExists(candidate) {
95
- try {
96
- return Boolean(candidate) && fs.existsSync(candidate);
97
- } catch {
98
- return false;
99
- }
100
- }
101
-
102
- export function hasSystemCli(target) {
103
- return listSystemCliCandidates(target).some(candidateExists);
104
- }
105
-
106
- export function defaultWindowsNpmCliCandidates(target) {
107
- const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
108
- if (process.platform !== "win32") return [];
109
- return [
110
- path.join(appData, "npm", `${target}.cmd`),
111
- path.join(appData, "npm", `${target}.exe`),
112
- path.join(appData, "npm", target),
113
- ];
114
- }
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+
6
+ function unique(values) {
7
+ return [...new Set(values.filter(Boolean))];
8
+ }
9
+
10
+ function stripExecutableExtension(name) {
11
+ return String(name || "").replace(/\.(cmd|bat|exe)$/i, "");
12
+ }
13
+
14
+ function isTargetBinary(target, candidate) {
15
+ const base = stripExecutableExtension(path.basename(String(candidate || ""))).toLowerCase();
16
+ return base === String(target || "").toLowerCase();
17
+ }
18
+
19
+ function sortWindowsCliCandidates(candidates) {
20
+ const extPriority = (candidate) => {
21
+ const ext = path.extname(String(candidate || "")).toLowerCase();
22
+ if (ext === ".cmd" || ext === ".bat" || ext === ".exe") return 0;
23
+ return 1;
24
+ };
25
+ return [...candidates].sort((a, b) => extPriority(a) - extPriority(b));
26
+ }
27
+
28
+ export function parseWhereisBinaryOutput(target, stdout) {
29
+ const candidates = [];
30
+ for (const line of String(stdout || "").split(/\r?\n/)) {
31
+ const colon = line.indexOf(":");
32
+ const body = colon === -1 ? line : line.slice(colon + 1);
33
+ for (const token of body.split(/\s+/)) {
34
+ const candidate = token.trim();
35
+ if (candidate && path.isAbsolute(candidate) && isTargetBinary(target, candidate)) {
36
+ candidates.push(candidate);
37
+ }
38
+ }
39
+ }
40
+ return unique(candidates);
41
+ }
42
+
43
+ function isLikelyNodePrefix(name) {
44
+ return /^(node|nodejs)(?:[-_.].*)?$/i.test(String(name || ""));
45
+ }
46
+
47
+ export function listCommonUnixCliCandidates(target, roots = ["/usr/local", "/opt"]) {
48
+ if (process.platform === "win32") return [];
49
+ const candidates = [
50
+ path.join("/usr/local", "bin", target),
51
+ path.join("/usr", "bin", target),
52
+ path.join("/bin", target),
53
+ ];
54
+ for (const root of roots) {
55
+ try {
56
+ if (!fs.existsSync(root)) continue;
57
+ for (const ent of fs.readdirSync(root, { withFileTypes: true })) {
58
+ if (ent.isDirectory() && isLikelyNodePrefix(ent.name)) {
59
+ candidates.push(path.join(root, ent.name, "bin", target));
60
+ }
61
+ }
62
+ } catch {
63
+ // Best effort only; command lookups and other candidates may still work.
64
+ }
65
+ }
66
+ return unique(candidates);
67
+ }
68
+
69
+ export function listSystemCliCandidates(target) {
70
+ if (!target) return [];
71
+ const candidates = [];
72
+ if (process.platform === "win32") {
73
+ const result = spawnSync("where.exe", [target], { encoding: "utf8", windowsHide: true });
74
+ if (result.status === 0) {
75
+ candidates.push(...String(result.stdout || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
76
+ }
77
+ return sortWindowsCliCandidates(unique(candidates));
78
+ }
79
+
80
+ const which = spawnSync("which", ["-a", target], { encoding: "utf8", windowsHide: true });
81
+ if (which.status === 0) {
82
+ candidates.push(...String(which.stdout || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
83
+ }
84
+
85
+ const whereis = spawnSync("whereis", ["-b", target], { encoding: "utf8", windowsHide: true });
86
+ if (whereis.status === 0) {
87
+ candidates.push(...parseWhereisBinaryOutput(target, whereis.stdout));
88
+ }
89
+
90
+ candidates.push(...listCommonUnixCliCandidates(target));
91
+ return unique(candidates);
92
+ }
93
+
94
+ export function candidateExists(candidate) {
95
+ try {
96
+ return Boolean(candidate) && fs.existsSync(candidate);
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ export function hasSystemCli(target) {
103
+ return listSystemCliCandidates(target).some(candidateExists);
104
+ }
105
+
106
+ export function defaultWindowsNpmCliCandidates(target) {
107
+ const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
108
+ if (process.platform !== "win32") return [];
109
+ return [
110
+ path.join(appData, "npm", `${target}.cmd`),
111
+ path.join(appData, "npm", `${target}.exe`),
112
+ path.join(appData, "npm", target),
113
+ ];
114
+ }
@@ -38,47 +38,47 @@ function findLatestSession(sessionsDir) {
38
38
  return latest;
39
39
  }
40
40
 
41
- function commandOk(cmd, args, env = process.env) {
42
- const r = spawnSync(cmd, args, { encoding: "utf8", env });
43
- return { ok: !r.error && r.status === 0, detail: (r.stdout || r.stderr || "").trim() };
44
- }
45
-
46
- function isWindowsAppsPath(candidate) {
47
- return String(candidate || "").toLowerCase().includes("\\program files\\windowsapps\\");
48
- }
49
-
50
- function readCodexShimTarget(codexHome) {
51
- if (process.platform !== "win32") return "";
52
- const shimPath = path.join(codexHome, "bin", "codex.cmd");
53
- if (!fs.existsSync(shimPath)) return "";
54
- const text = stripBom(fs.readFileSync(shimPath, "utf8"));
55
- const matches = text.matchAll(/^\s*call\s+"([^"]+)"/gim);
56
- for (const match of matches) {
57
- const base = path.basename(match[1]).toLowerCase();
58
- if (base === "codex.exe" || base === "codex.cmd" || base === "codex") return match[1];
59
- }
60
- return "";
61
- }
62
-
63
- function venvPython(codexHome) {
64
- return process.platform === "win32"
65
- ? path.join(codexHome, "langfuse-venv", "Scripts", "python.exe")
66
- : path.join(codexHome, "langfuse-venv", "bin", "python");
67
- }
68
-
69
- function fallbackPythonCommand() {
70
- return process.platform === "win32" ? "python" : "python3";
71
- }
72
-
73
- function isolatedPythonPath(codexHome) {
74
- return path.join(codexHome, "langfuse-python");
75
- }
76
-
77
- function langfuseImportOk(hookPython, fallbackTarget) {
78
- if (fs.existsSync(hookPython)) return commandOk(hookPython, ["-c", "import langfuse; print('langfuse ok')"]);
79
- const env = { ...process.env, PYTHONPATH: process.env.PYTHONPATH ? `${fallbackTarget}${path.delimiter}${process.env.PYTHONPATH}` : fallbackTarget };
80
- return commandOk(fallbackPythonCommand(), ["-c", "import langfuse; print('langfuse ok')"], env);
81
- }
41
+ function commandOk(cmd, args, env = process.env) {
42
+ const r = spawnSync(cmd, args, { encoding: "utf8", env });
43
+ return { ok: !r.error && r.status === 0, detail: (r.stdout || r.stderr || "").trim() };
44
+ }
45
+
46
+ function isWindowsAppsPath(candidate) {
47
+ return String(candidate || "").toLowerCase().includes("\\program files\\windowsapps\\");
48
+ }
49
+
50
+ function readCodexShimTarget(codexHome) {
51
+ if (process.platform !== "win32") return "";
52
+ const shimPath = path.join(codexHome, "bin", "codex.cmd");
53
+ if (!fs.existsSync(shimPath)) return "";
54
+ const text = stripBom(fs.readFileSync(shimPath, "utf8"));
55
+ const matches = text.matchAll(/^\s*call\s+"([^"]+)"/gim);
56
+ for (const match of matches) {
57
+ const base = path.basename(match[1]).toLowerCase();
58
+ if (base === "codex.exe" || base === "codex.cmd" || base === "codex") return match[1];
59
+ }
60
+ return "";
61
+ }
62
+
63
+ function venvPython(codexHome) {
64
+ return process.platform === "win32"
65
+ ? path.join(codexHome, "langfuse-venv", "Scripts", "python.exe")
66
+ : path.join(codexHome, "langfuse-venv", "bin", "python");
67
+ }
68
+
69
+ function fallbackPythonCommand() {
70
+ return process.platform === "win32" ? "python" : "python3";
71
+ }
72
+
73
+ function isolatedPythonPath(codexHome) {
74
+ return path.join(codexHome, "langfuse-python");
75
+ }
76
+
77
+ function langfuseImportOk(hookPython, fallbackTarget) {
78
+ if (fs.existsSync(hookPython)) return commandOk(hookPython, ["-c", "import langfuse; print('langfuse ok')"]);
79
+ const env = { ...process.env, PYTHONPATH: process.env.PYTHONPATH ? `${fallbackTarget}${path.delimiter}${process.env.PYTHONPATH}` : fallbackTarget };
80
+ return commandOk(fallbackPythonCommand(), ["-c", "import langfuse; print('langfuse ok')"], env);
81
+ }
82
82
 
83
83
  function configHasNotify(configText) {
84
84
  const firstSection = configText.search(/^\s*\[/m);
@@ -98,15 +98,15 @@ function main() {
98
98
  const sessionsDir = path.join(codexHome, "sessions");
99
99
  const latestSession = findLatestSession(sessionsDir);
100
100
  const langfuseConfig = readJsonIfExists(langfuseConfigPath);
101
- const configText = fs.existsSync(configPath) ? stripBom(fs.readFileSync(configPath, "utf8")) : "";
102
- const hookPython = venvPython(codexHome);
103
- const fallbackTarget = isolatedPythonPath(codexHome);
104
- const python = commandOk(process.platform === "win32" ? "python" : "python3", ["--version"]);
105
- const langfuseImport = langfuseImportOk(hookPython, fallbackTarget);
106
- const codexShimTarget = readCodexShimTarget(codexHome);
107
- const codexShimUsesWindowsApps = !!codexShimTarget && isWindowsAppsPath(codexShimTarget);
108
-
109
- const logPath = path.join(codexHome, "langfuse", "codex_langfuse_notify.log");
101
+ const configText = fs.existsSync(configPath) ? stripBom(fs.readFileSync(configPath, "utf8")) : "";
102
+ const hookPython = venvPython(codexHome);
103
+ const fallbackTarget = isolatedPythonPath(codexHome);
104
+ const python = commandOk(process.platform === "win32" ? "python" : "python3", ["--version"]);
105
+ const langfuseImport = langfuseImportOk(hookPython, fallbackTarget);
106
+ const codexShimTarget = readCodexShimTarget(codexHome);
107
+ const codexShimUsesWindowsApps = !!codexShimTarget && isWindowsAppsPath(codexShimTarget);
108
+
109
+ const logPath = path.join(codexHome, "langfuse", "codex_langfuse_notify.log");
110
110
  const logText = fs.existsSync(logPath) ? stripBom(fs.readFileSync(logPath, "utf8")) : "";
111
111
  const recentLogHasError = /Traceback|ERROR|Exception|Failed/i.test(logText.slice(-4000));
112
112
 
@@ -145,21 +145,21 @@ function main() {
145
145
  "Start a Codex conversation, then check again.",
146
146
  { required: false }
147
147
  );
148
- addResult(results, "Python", python.ok, python.detail || "not found", "Install Python and pip, then rerun setup.");
149
- addResult(
150
- results,
151
- "Codex command shim target",
152
- !codexShimUsesWindowsApps,
153
- codexShimTarget || "not installed",
154
- "Run setup/update again with a fixed oh-langfuse version; WindowsApps Codex paths cannot be called directly."
155
- );
156
- addResult(
157
- results,
158
- "Langfuse hook Python",
159
- fs.existsSync(hookPython) || fs.existsSync(fallbackTarget),
160
- fs.existsSync(hookPython) ? hookPython : `${fallbackPythonCommand()} with PYTHONPATH=${fallbackTarget}`,
161
- "Run setup again; on Linux the installer can fall back to an isolated PYTHONPATH target."
162
- );
148
+ addResult(results, "Python", python.ok, python.detail || "not found", "Install Python and pip, then rerun setup.");
149
+ addResult(
150
+ results,
151
+ "Codex command shim target",
152
+ !codexShimUsesWindowsApps,
153
+ codexShimTarget || "not installed",
154
+ "Run setup/update again with a fixed oh-langfuse version; WindowsApps Codex paths cannot be called directly."
155
+ );
156
+ addResult(
157
+ results,
158
+ "Langfuse hook Python",
159
+ fs.existsSync(hookPython) || fs.existsSync(fallbackTarget),
160
+ fs.existsSync(hookPython) ? hookPython : `${fallbackPythonCommand()} with PYTHONPATH=${fallbackTarget}`,
161
+ "Run setup again; on Linux the installer can fall back to an isolated PYTHONPATH target."
162
+ );
163
163
  addResult(
164
164
  results,
165
165
  "Python langfuse package",