oh-langfuse 0.1.9 → 0.1.11
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/bin/cli.js +78 -98
- package/package.json +1 -1
- package/scripts/opencode-langfuse-check.mjs +18 -17
- package/scripts/opencode-langfuse-setup.mjs +28 -27
package/bin/cli.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
8
7
|
|
|
9
8
|
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
9
|
const scriptsDir = path.join(rootDir, "scripts");
|
|
@@ -271,41 +270,22 @@ async function askYesNo(rl, label, { defaultValue = true } = {}) {
|
|
|
271
270
|
}
|
|
272
271
|
}
|
|
273
272
|
|
|
274
|
-
function
|
|
275
|
-
|
|
273
|
+
function rawKeySeq(raw) {
|
|
274
|
+
if (Buffer.isBuffer(raw)) return raw.toString("latin1");
|
|
275
|
+
return String(raw ?? "");
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
function
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return key.name === "up" || seq === "\x1b[A" || seq === "\x1bOA";
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function isDownKey(raw, key = {}) {
|
|
293
|
-
const seq = keySeq(raw, key);
|
|
294
|
-
return key.name === "down" || seq === "\x1b[B" || seq === "\x1bOB";
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function isEnterKey(raw, key = {}) {
|
|
298
|
-
const seq = keySeq(raw, key);
|
|
299
|
-
return key.name === "return" || key.name === "enter" || seq === "\r" || seq === "\n";
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function isSpaceKey(raw, key = {}) {
|
|
303
|
-
return key.name === "space" || keySeq(raw, key) === " ";
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function numberKey(raw, key = {}) {
|
|
307
|
-
const seq = keySeq(raw, key);
|
|
308
|
-
return /^[1-9]$/.test(seq) ? Number.parseInt(seq, 10) : Number.NaN;
|
|
278
|
+
function parseRawKey(raw) {
|
|
279
|
+
const seq = rawKeySeq(raw);
|
|
280
|
+
if (seq === "\x03") return { name: "ctrl-c", sequence: seq };
|
|
281
|
+
if (seq === "\x1b[A" || seq === "\x1bOA" || seq === "\x00H" || seq === "\xe0H") return { name: "up", sequence: seq };
|
|
282
|
+
if (seq === "\x1b[B" || seq === "\x1bOB" || seq === "\x00P" || seq === "\xe0P") return { name: "down", sequence: seq };
|
|
283
|
+
if (seq === "\r" || seq === "\n" || seq === "\r\n") return { name: "enter", sequence: seq };
|
|
284
|
+
if (seq === " ") return { name: "space", sequence: seq };
|
|
285
|
+
if (seq === "\x1b") return { name: "escape", sequence: seq };
|
|
286
|
+
if (/^[1-9]$/.test(seq)) return { name: "number", number: Number.parseInt(seq, 10), sequence: seq };
|
|
287
|
+
if (seq.length === 1) return { name: seq.toLowerCase(), sequence: seq };
|
|
288
|
+
return { name: "", sequence: seq };
|
|
309
289
|
}
|
|
310
290
|
|
|
311
291
|
function renderChoiceScreen(label, choices, index, options = {}) {
|
|
@@ -329,47 +309,47 @@ function renderChoiceScreen(label, choices, index, options = {}) {
|
|
|
329
309
|
});
|
|
330
310
|
}
|
|
331
311
|
|
|
332
|
-
async function askChoice(rl, label, choices, options = {}) {
|
|
333
|
-
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
334
|
-
rl.pause();
|
|
335
|
-
return await new Promise((resolve) => {
|
|
336
|
-
let index = 0;
|
|
312
|
+
async function askChoice(rl, label, choices, options = {}) {
|
|
313
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
314
|
+
rl.pause();
|
|
315
|
+
return await new Promise((resolve) => {
|
|
316
|
+
let index = 0;
|
|
337
317
|
const stdin = process.stdin;
|
|
338
|
-
|
|
339
|
-
function cleanup(value) {
|
|
340
|
-
stdin.off("
|
|
341
|
-
if (stdin.isTTY) stdin.setRawMode(false);
|
|
342
|
-
stdin.pause();
|
|
343
|
-
rl.resume();
|
|
344
|
-
clearScreen();
|
|
345
|
-
resolve(value);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function
|
|
349
|
-
|
|
350
|
-
if (
|
|
318
|
+
|
|
319
|
+
function cleanup(value) {
|
|
320
|
+
stdin.off("data", onData);
|
|
321
|
+
if (stdin.isTTY) stdin.setRawMode(false);
|
|
322
|
+
stdin.pause();
|
|
323
|
+
rl.resume();
|
|
324
|
+
clearScreen();
|
|
325
|
+
resolve(value);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function onData(raw) {
|
|
329
|
+
const key = parseRawKey(raw);
|
|
330
|
+
if (key.name === "ctrl-c") return cleanup("exit");
|
|
331
|
+
if (key.name === "up") {
|
|
351
332
|
index = (index - 1 + choices.length) % choices.length;
|
|
352
333
|
renderChoiceScreen(label, choices, index, options);
|
|
353
334
|
return;
|
|
354
335
|
}
|
|
355
|
-
if (
|
|
336
|
+
if (key.name === "down") {
|
|
356
337
|
index = (index + 1) % choices.length;
|
|
357
338
|
renderChoiceScreen(label, choices, index, options);
|
|
358
339
|
return;
|
|
359
340
|
}
|
|
360
|
-
if (
|
|
361
|
-
if (
|
|
362
|
-
const num =
|
|
341
|
+
if (key.name === "q" || key.name === "escape") return cleanup("exit");
|
|
342
|
+
if (key.name === "enter") return cleanup(choices[index].value);
|
|
343
|
+
const num = key.name === "number" ? key.number : Number.NaN;
|
|
363
344
|
if (Number.isInteger(num) && choices[num - 1]) return cleanup(choices[num - 1].value);
|
|
364
345
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
stdin.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
346
|
+
|
|
347
|
+
if (stdin.isTTY) stdin.setRawMode(true);
|
|
348
|
+
stdin.on("data", onData);
|
|
349
|
+
stdin.resume();
|
|
350
|
+
renderChoiceScreen(label, choices, index, options);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
373
353
|
|
|
374
354
|
console.log("");
|
|
375
355
|
console.log(label);
|
|
@@ -413,52 +393,52 @@ async function askMultiChoice(rl, label, choices, options = {}) {
|
|
|
413
393
|
let index = 0;
|
|
414
394
|
const selected = new Set(choices.filter((choice) => choice.selected).map((choice) => choice.value));
|
|
415
395
|
const stdin = process.stdin;
|
|
416
|
-
|
|
417
|
-
function cleanup(value) {
|
|
418
|
-
stdin.off("
|
|
419
|
-
if (stdin.isTTY) stdin.setRawMode(false);
|
|
420
|
-
stdin.pause();
|
|
421
|
-
rl.resume();
|
|
422
|
-
clearScreen();
|
|
423
|
-
resolve(value);
|
|
396
|
+
|
|
397
|
+
function cleanup(value) {
|
|
398
|
+
stdin.off("data", onData);
|
|
399
|
+
if (stdin.isTTY) stdin.setRawMode(false);
|
|
400
|
+
stdin.pause();
|
|
401
|
+
rl.resume();
|
|
402
|
+
clearScreen();
|
|
403
|
+
resolve(value);
|
|
424
404
|
}
|
|
425
405
|
|
|
426
406
|
function toggle() {
|
|
427
407
|
const value = choices[index].value;
|
|
428
408
|
if (selected.has(value)) selected.delete(value);
|
|
429
409
|
else selected.add(value);
|
|
430
|
-
renderMultiChoiceScreen(label, choices, index, selected, options);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
if (
|
|
410
|
+
renderMultiChoiceScreen(label, choices, index, selected, options);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function onData(raw) {
|
|
414
|
+
const key = parseRawKey(raw);
|
|
415
|
+
if (key.name === "ctrl-c") return cleanup([]);
|
|
416
|
+
if (key.name === "up") {
|
|
436
417
|
index = (index - 1 + choices.length) % choices.length;
|
|
437
418
|
renderMultiChoiceScreen(label, choices, index, selected, options);
|
|
438
419
|
return;
|
|
439
420
|
}
|
|
440
|
-
if (
|
|
421
|
+
if (key.name === "down") {
|
|
441
422
|
index = (index + 1) % choices.length;
|
|
442
423
|
renderMultiChoiceScreen(label, choices, index, selected, options);
|
|
443
424
|
return;
|
|
444
425
|
}
|
|
445
|
-
if (
|
|
446
|
-
if (
|
|
447
|
-
if (
|
|
448
|
-
const num =
|
|
426
|
+
if (key.name === "q" || key.name === "escape") return cleanup([]);
|
|
427
|
+
if (key.name === "space") return toggle();
|
|
428
|
+
if (key.name === "enter") return cleanup([...selected]);
|
|
429
|
+
const num = key.name === "number" ? key.number : Number.NaN;
|
|
449
430
|
if (Number.isInteger(num) && choices[num - 1]) {
|
|
450
431
|
index = num - 1;
|
|
451
432
|
toggle();
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
stdin.
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (stdin.isTTY) stdin.setRawMode(true);
|
|
437
|
+
stdin.on("data", onData);
|
|
438
|
+
stdin.resume();
|
|
439
|
+
renderMultiChoiceScreen(label, choices, index, selected, options);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
462
442
|
|
|
463
443
|
console.log("");
|
|
464
444
|
console.log(label);
|
package/package.json
CHANGED
|
@@ -53,23 +53,24 @@ function main() {
|
|
|
53
53
|
detail: JSON.stringify(settings?.plugin ?? null)
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
results.push({
|
|
57
|
-
item: "npm 包目录 node_modules/opencode-plugin-langfuse",
|
|
58
|
-
ok: fs.existsSync(pkgDir),
|
|
59
|
-
detail: pkgDir
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
results.push({
|
|
57
|
+
item: "npm 包目录 node_modules/opencode-plugin-langfuse",
|
|
58
|
+
ok: fs.existsSync(pkgDir),
|
|
59
|
+
detail: pkgDir
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
results.push({
|
|
63
|
+
item: "plugins 目录本地插件",
|
|
64
|
+
ok: fs.existsSync(path.join(pluginDest, "package.json")),
|
|
65
|
+
detail: pluginDest
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const launcherPath = path.join(opencodeDir, "launch-opencode-langfuse.cmd");
|
|
69
|
+
if (process.platform === "win32") {
|
|
70
|
+
results.push({
|
|
71
|
+
item: "启动脚本 launch-opencode-langfuse.cmd",
|
|
72
|
+
ok: fs.existsSync(launcherPath),
|
|
73
|
+
detail: launcherPath
|
|
73
74
|
});
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -188,11 +188,19 @@ function getPatchedLangfuseDistIndexJs() {
|
|
|
188
188
|
].join(os.EOL);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
function patchDistIndexJs(distIndexPath) {
|
|
192
|
-
const code = getPatchedLangfuseDistIndexJs();
|
|
193
|
-
ensureDir(path.dirname(distIndexPath));
|
|
194
|
-
fs.writeFileSync(distIndexPath, code + os.EOL, "utf8");
|
|
195
|
-
}
|
|
191
|
+
function patchDistIndexJs(distIndexPath) {
|
|
192
|
+
const code = getPatchedLangfuseDistIndexJs();
|
|
193
|
+
ensureDir(path.dirname(distIndexPath));
|
|
194
|
+
fs.writeFileSync(distIndexPath, code + os.EOL, "utf8");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function syncPluginPackageToLocalPlugins({ pkgDir, pluginDest, pluginsDir }) {
|
|
198
|
+
ensureDir(pluginsDir);
|
|
199
|
+
if (fs.existsSync(pluginDest)) {
|
|
200
|
+
fs.rmSync(pluginDest, { recursive: true, force: true });
|
|
201
|
+
}
|
|
202
|
+
fs.cpSync(pkgDir, pluginDest, { recursive: true });
|
|
203
|
+
}
|
|
196
204
|
|
|
197
205
|
function writeWindowsLauncherCmd(opencodeDir, { publicKey, secretKey, baseUrl, userId }) {
|
|
198
206
|
if (process.platform !== "win32") return null;
|
|
@@ -383,28 +391,21 @@ function main() {
|
|
|
383
391
|
);
|
|
384
392
|
}
|
|
385
393
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
if (process.platform === "win32") {
|
|
403
|
-
const distIndexInPlugins = path.join(pluginDest, "dist", "index.js");
|
|
404
|
-
if (fs.existsSync(distIndexInPlugins)) {
|
|
405
|
-
patchDistIndexJs(distIndexInPlugins);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
394
|
+
const existing = readJsonIfExists(opencodeJsonPath) ?? {};
|
|
395
|
+
// Patch the official plugin to inject userId and make behavior consistent.
|
|
396
|
+
const distIndexInNodeModules = path.join(pkgDir, "dist", "index.js");
|
|
397
|
+
if (fs.existsSync(distIndexInNodeModules)) {
|
|
398
|
+
patchDistIndexJs(distIndexInNodeModules);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log("Syncing plugin into ~/.config/opencode/plugins/ ...");
|
|
402
|
+
syncPluginPackageToLocalPlugins({ pkgDir, pluginDest, pluginsDir });
|
|
403
|
+
console.log(`Plugin ready at: ${pluginDest}`);
|
|
404
|
+
|
|
405
|
+
const distIndexInPlugins = path.join(pluginDest, "dist", "index.js");
|
|
406
|
+
if (fs.existsSync(distIndexInPlugins)) {
|
|
407
|
+
patchDistIndexJs(distIndexInPlugins);
|
|
408
|
+
}
|
|
408
409
|
|
|
409
410
|
// Write plugin user config to support userId injection.
|
|
410
411
|
if (userId) {
|