oh-langfuse 0.1.25 → 0.1.26

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.
@@ -0,0 +1,178 @@
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
+ import { fileURLToPath } from "node:url";
6
+ import { extractVersionFromNpmMetadata, selectUpdateTargets } from "./update-utils.mjs";
7
+
8
+ const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8"));
10
+
11
+ const DEFAULT_LANGFUSE_BASE_URL = "http://120.46.221.227:3000";
12
+ const DEFAULT_LANGFUSE_PUBLIC_KEY = "pk-lf-da0c90a7-6e93-4eb7-bb86-c1047c8d187d";
13
+ const DEFAULT_LANGFUSE_SECRET_KEY = "sk-lf-0269b85d-bfdc-442c-bfa3-e737954e3315";
14
+
15
+ function parseArgs(argv) {
16
+ const args = { _: [] };
17
+ for (const raw of argv) {
18
+ if (!raw.startsWith("--")) {
19
+ args._.push(raw);
20
+ continue;
21
+ }
22
+ const eq = raw.indexOf("=");
23
+ if (eq === -1) args[raw.slice(2)] = true;
24
+ else args[raw.slice(2, eq)] = raw.slice(eq + 1);
25
+ }
26
+ return args;
27
+ }
28
+
29
+ function stripBom(s) {
30
+ return typeof s === "string" && s.charCodeAt(0) === 0xfeff ? s.slice(1) : s;
31
+ }
32
+
33
+ function readJsonIfExists(p) {
34
+ try {
35
+ if (!fs.existsSync(p)) return null;
36
+ const text = stripBom(fs.readFileSync(p, "utf8"));
37
+ if (!text.trim()) return null;
38
+ return JSON.parse(text);
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ function detectInstalledTargets(home = os.homedir()) {
45
+ const codexHome = process.env.CODEX_HOME || path.join(home, ".codex");
46
+ return {
47
+ claude: fs.existsSync(path.join(home, ".claude", "hooks", "langfuse_hook.py")),
48
+ opencode:
49
+ fs.existsSync(path.join(home, ".config", "opencode", "opencode.json")) ||
50
+ fs.existsSync(path.join(home, ".config", "opencode", "plugins", "opencode-plugin-langfuse")),
51
+ codex: fs.existsSync(path.join(codexHome, "hooks", "codex_langfuse_notify.py")),
52
+ };
53
+ }
54
+
55
+ function claudeConfig(home) {
56
+ const settings = readJsonIfExists(path.join(home, ".claude", "settings.json")) || {};
57
+ const env = settings.env || {};
58
+ return {
59
+ baseUrl: env.LANGFUSE_BASEURL || env.LANGFUSE_HOST || env.CC_LANGFUSE_BASE_URL,
60
+ publicKey: env.LANGFUSE_PUBLIC_KEY,
61
+ secretKey: env.LANGFUSE_SECRET_KEY,
62
+ userId: env.LANGFUSE_USER_ID || env.CC_USER_ID || env.CC_LANGFUSE_USER_ID || env.CLAUDE_USER_ID,
63
+ };
64
+ }
65
+
66
+ function codexConfig(home) {
67
+ const codexHome = process.env.CODEX_HOME || path.join(home, ".codex");
68
+ const config = readJsonIfExists(path.join(codexHome, "langfuse", "config.json")) || {};
69
+ return {
70
+ baseUrl: config.baseUrl,
71
+ publicKey: config.publicKey,
72
+ secretKey: config.secretKey,
73
+ userId: config.userId,
74
+ };
75
+ }
76
+
77
+ function opencodeConfig(home) {
78
+ const config = readJsonIfExists(path.join(home, ".config", "opencode-plugin-langfuse", "config.json")) || {};
79
+ return {
80
+ baseUrl: process.env.LANGFUSE_BASEURL || process.env.LANGFUSE_HOST,
81
+ publicKey: process.env.LANGFUSE_PUBLIC_KEY,
82
+ secretKey: process.env.LANGFUSE_SECRET_KEY,
83
+ userId: config.userId || config.usrid || process.env.LANGFUSE_USER_ID,
84
+ };
85
+ }
86
+
87
+ function mergedConfig(target, args) {
88
+ const home = os.homedir();
89
+ const existing = target === "claude" ? claudeConfig(home) : target === "codex" ? codexConfig(home) : opencodeConfig(home);
90
+ const config = {
91
+ baseUrl: args.langfuseBaseUrl || args.langfuseHost || args.host || existing.baseUrl || process.env.LANGFUSE_BASEURL || DEFAULT_LANGFUSE_BASE_URL,
92
+ publicKey: args.publicKey || existing.publicKey || process.env.LANGFUSE_PUBLIC_KEY || DEFAULT_LANGFUSE_PUBLIC_KEY,
93
+ secretKey: args.secretKey || existing.secretKey || process.env.LANGFUSE_SECRET_KEY || DEFAULT_LANGFUSE_SECRET_KEY,
94
+ userId: args.userId || args.userid || existing.userId || process.env.LANGFUSE_USER_ID || process.env.CC_USER_ID || "",
95
+ };
96
+ if (!config.userId) throw new Error(`Missing user id for ${target}; provide --userId=<employee-id> or run setup first.`);
97
+ return config;
98
+ }
99
+
100
+ async function latestVersion(registry = "https://registry.npmjs.org") {
101
+ const base = registry.replace(/\/+$/, "");
102
+ const response = await fetch(`${base}/oh-langfuse`, { headers: { accept: "application/json" } });
103
+ if (!response.ok) throw new Error(`npm registry ${response.status} ${response.statusText}`);
104
+ return extractVersionFromNpmMetadata(await response.json());
105
+ }
106
+
107
+ function runNodeScript(script, args, options = {}) {
108
+ const result = spawnSync(process.execPath, [path.join(rootDir, "scripts", script), ...args], {
109
+ cwd: rootDir,
110
+ stdio: "inherit",
111
+ windowsHide: true,
112
+ timeout: options.timeoutMs || 900000,
113
+ });
114
+ if ((result.status ?? 1) !== 0) {
115
+ throw new Error(`${script} failed with exit code ${result.status ?? 1}`);
116
+ }
117
+ }
118
+
119
+ function setupArgs(target, config, args) {
120
+ const out = [
121
+ `--userId=${config.userId}`,
122
+ `--langfuseBaseUrl=${config.baseUrl}`,
123
+ `--publicKey=${config.publicKey}`,
124
+ `--secretKey=${config.secretKey}`,
125
+ ];
126
+ if (args.pipIndexUrl) out.push(`--pipIndexUrl=${args.pipIndexUrl}`);
127
+ if (args.npmRegistry && target === "opencode") out.push(`--npmRegistry=${args.npmRegistry}`);
128
+ if ((args["skip-plugin-install"] || args.skipPluginInstall) && target === "opencode") out.push("--skip-plugin-install");
129
+ return out;
130
+ }
131
+
132
+ function setupScript(target) {
133
+ if (target === "claude") return "langfuse-setup.mjs";
134
+ if (target === "codex") return "codex-langfuse-setup.mjs";
135
+ return "opencode-langfuse-setup.mjs";
136
+ }
137
+
138
+ function checkScript(target) {
139
+ if (target === "claude") return "langfuse-check.mjs";
140
+ if (target === "codex") return "codex-langfuse-check.mjs";
141
+ return "opencode-langfuse-check.mjs";
142
+ }
143
+
144
+ async function main() {
145
+ const args = parseArgs(process.argv.slice(2));
146
+ const targetArg = args._[0] || "all";
147
+ const installed = detectInstalledTargets();
148
+ const targets = selectUpdateTargets(targetArg, installed);
149
+ if (!targets.length) {
150
+ throw new Error("No installed oh-langfuse targets were detected. Run setup first, or specify a target with credentials.");
151
+ }
152
+
153
+ console.log(`[INFO] Running package: ${packageJson.name}@${packageJson.version}`);
154
+ try {
155
+ const latest = await latestVersion(args.npmRegistry);
156
+ console.log(`[INFO] npm latest: ${latest}`);
157
+ if (latest !== packageJson.version) {
158
+ console.log(`[INFO] To update a global install, run: npm install -g oh-langfuse@latest`);
159
+ }
160
+ } catch (error) {
161
+ console.log(`[WARN] Could not query npm latest version: ${error.message}`);
162
+ }
163
+
164
+ for (const target of targets) {
165
+ console.log("");
166
+ console.log(`[INFO] Updating ${target} runtime...`);
167
+ const config = mergedConfig(target, args);
168
+ runNodeScript(setupScript(target), setupArgs(target, config, args));
169
+ if (!args["skip-check"]) {
170
+ runNodeScript(checkScript(target), []);
171
+ }
172
+ }
173
+ }
174
+
175
+ main().catch((error) => {
176
+ console.error(`[FAIL] ${error?.message || String(error)}`);
177
+ process.exit(1);
178
+ });
@@ -0,0 +1,20 @@
1
+ const ALLOWED_TARGETS = new Set(["claude", "opencode", "codex"]);
2
+
3
+ export function extractVersionFromNpmMetadata(metadata) {
4
+ const latest = metadata?.["dist-tags"]?.latest;
5
+ if (typeof latest !== "string" || !latest.trim()) {
6
+ throw new Error("npm registry metadata does not include dist-tags.latest");
7
+ }
8
+ return latest.trim();
9
+ }
10
+
11
+ export function selectUpdateTargets(target = "all", installed = {}) {
12
+ const normalized = String(target || "all").trim().toLowerCase();
13
+ if (normalized === "all") {
14
+ return ["claude", "opencode", "codex"].filter((name) => Boolean(installed[name]));
15
+ }
16
+ if (!ALLOWED_TARGETS.has(normalized)) {
17
+ throw new Error(`Unsupported update target: ${target}`);
18
+ }
19
+ return [normalized];
20
+ }