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.
@@ -1,199 +1,208 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import os from "node:os";
4
- import { parseJsonRelaxed, stripBom } from "./json-utils.mjs";
5
-
6
- function parseArgs(argv) {
7
- const args = {};
8
- for (const raw of argv) {
9
- if (!raw.startsWith("--")) continue;
10
- const eq = raw.indexOf("=");
11
- if (eq === -1) args[raw.slice(2)] = true;
12
- else args[raw.slice(2, eq)] = raw.slice(eq + 1);
13
- }
14
- return args;
15
- }
16
-
17
- function readTextIfExists(p) {
18
- if (!fs.existsSync(p)) return "";
19
- return stripBom(fs.readFileSync(p, "utf8"));
20
- }
21
-
22
- function readJsonIfExists(p) {
23
- const txt = readTextIfExists(p);
24
- if (!txt.trim()) return null;
25
- return parseJsonRelaxed(txt, p);
26
- }
27
-
28
- function hasPlugin(pluginField, name) {
29
- if (Array.isArray(pluginField)) return pluginField.includes(name);
30
- if (typeof pluginField === "string") return pluginField === name;
31
- return false;
32
- }
33
-
34
- function shellConfigPath() {
35
- const shell = process.env.SHELL || "/bin/bash";
36
- return path.join(os.homedir(), shell.includes("zsh") ? ".zshrc" : ".bashrc");
37
- }
38
-
39
- function isPatchedPlugin(distIndexPath) {
40
- const code = readTextIfExists(distIndexPath);
41
- return (
42
- code.includes("LangfuseSpanProcessor") &&
43
- code.includes("LANGFUSE_PUBLIC_KEY") &&
44
- code.includes("createUserIdSpanProcessor")
45
- );
46
- }
47
-
48
- function addResult(results, item, ok, detail, fix = "") {
49
- results.push({ item, ok, detail, fix });
50
- }
51
-
52
- function main() {
53
- const home = os.homedir();
54
- const opencodeDir = path.join(home, ".config", "opencode");
55
- const pkgDir = path.join(opencodeDir, "node_modules", "opencode-plugin-langfuse");
56
- const pluginDest = path.join(opencodeDir, "plugins", "opencode-plugin-langfuse");
57
- const langfusePluginPath = "./plugins/opencode-plugin-langfuse";
58
- const opencodeJsonPath = path.join(opencodeDir, "opencode.json");
59
- const pluginDistIndex = path.join(pluginDest, "dist", "index.js");
60
- const nodeModulesDistIndex = path.join(pkgDir, "dist", "index.js");
61
- const userConfigPath = path.join(home, ".config", "opencode-plugin-langfuse", "config.json");
62
- const windowsLauncherPath = path.join(opencodeDir, "launch-opencode-langfuse.cmd");
63
- const unixLauncherPath = path.join(opencodeDir, "launch-opencode-langfuse.sh");
64
- const opencodeCommandShimPath = path.join(opencodeDir, "bin", process.platform === "win32" ? "opencode.cmd" : "opencode");
65
-
66
- const results = [];
67
-
68
- addResult(
69
- results,
70
- "OpenCode config dir",
71
- fs.existsSync(opencodeDir),
72
- opencodeDir,
73
- "Run: npx oh-langfuse@latest setup opencode"
74
- );
75
-
76
- const settings = readJsonIfExists(opencodeJsonPath);
77
- addResult(results, "opencode.json", !!settings, opencodeJsonPath, "Run setup again to create OpenCode config.");
78
-
79
- const otOk = !!(settings?.experimental?.openTelemetry === true);
80
- addResult(
81
- results,
82
- "experimental.openTelemetry",
83
- otOk,
84
- otOk ? "true" : String(settings?.experimental?.openTelemetry ?? "missing"),
85
- "Run: npx oh-langfuse@latest setup opencode"
86
- );
87
-
88
- const plOk = hasPlugin(settings?.plugin, langfusePluginPath);
89
- addResult(
90
- results,
91
- `plugin contains ${langfusePluginPath}`,
92
- plOk,
93
- JSON.stringify(settings?.plugin ?? null),
94
- "Run setup again so opencode.json points at the local plugin copy."
95
- );
96
-
97
- addResult(
98
- results,
99
- "npm package",
100
- fs.existsSync(path.join(pkgDir, "package.json")),
101
- pkgDir,
102
- "Install may have failed. Try: npx oh-langfuse@latest setup opencode --npmRegistry=https://registry.npmmirror.com"
103
- );
104
-
105
- addResult(
106
- results,
107
- "local plugin copy",
108
- fs.existsSync(path.join(pluginDest, "package.json")),
109
- pluginDest,
110
- "Run setup again; OpenCode loads ./plugins/opencode-plugin-langfuse from opencode.json."
111
- );
112
-
113
- addResult(
114
- results,
115
- "local plugin patch",
116
- isPatchedPlugin(pluginDistIndex),
117
- pluginDistIndex,
118
- "Run setup again to patch Langfuse credentials/userId injection into the plugin."
119
- );
120
-
121
- addResult(
122
- results,
123
- "node_modules plugin patch",
124
- isPatchedPlugin(nodeModulesDistIndex),
125
- nodeModulesDistIndex,
126
- "Run setup again to patch the installed npm package before copying it."
127
- );
128
-
129
- const userConfig = readJsonIfExists(userConfigPath);
130
- addResult(
131
- results,
132
- "OpenCode Langfuse user config",
133
- !!(userConfig?.userId || userConfig?.usrid),
134
- userConfigPath,
135
- "Run setup again and enter userId when prompted."
136
- );
137
-
138
- addResult(
139
- results,
140
- "opencode command shim",
141
- fs.existsSync(opencodeCommandShimPath),
142
- opencodeCommandShimPath,
143
- "Run setup again after installing OpenCode so the direct opencode command can load Langfuse and auto-update checks."
144
- );
145
-
146
- if (process.platform === "win32") {
147
- addResult(
148
- results,
149
- "launcher cmd",
150
- fs.existsSync(windowsLauncherPath),
151
- windowsLauncherPath,
152
- "Run setup again to create a launcher with LANGFUSE_* variables."
153
- );
154
- } else {
155
- const shellRc = shellConfigPath();
156
- const shellRcText = readTextIfExists(shellRc);
157
- addResult(
158
- results,
159
- "shell rc LANGFUSE_* block",
160
- shellRcText.includes("# === Langfuse OpenCode Setup ===") && shellRcText.includes("LANGFUSE_PUBLIC_KEY"),
161
- shellRc,
162
- "Run setup again, then open a new terminal or source the shown shell rc file."
163
- );
164
- addResult(
165
- results,
166
- "launcher sh",
167
- fs.existsSync(unixLauncherPath),
168
- unixLauncherPath,
169
- "Run setup again to create a launcher with LANGFUSE_* variables."
170
- );
171
- }
172
-
173
- const w = Math.max(...results.map((r) => r.item.length)) + 2;
174
- const failed = [];
175
- for (const r of results) {
176
- const status = r.ok ? "OK " : "BAD";
177
- const pad = (s, n) => (s.length >= n ? s : s + " ".repeat(n - s.length));
178
- console.log(`${status} ${pad(r.item, w)} ${r.detail}`);
179
- if (!r.ok) failed.push(r);
180
- }
181
-
182
- if (failed.length) {
183
- console.log("");
184
- console.log("Fix suggestions:");
185
- for (const r of failed) {
186
- if (r.fix) console.log(`- ${r.item}: ${r.fix}`);
187
- }
188
- }
189
-
190
- process.exit(failed.length ? 2 : 0);
191
- }
192
-
193
- try {
194
- parseArgs(process.argv.slice(2));
195
- main();
196
- } catch (e) {
197
- console.error(e?.message || String(e));
198
- process.exit(1);
199
- }
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import { parseJsonRelaxed, stripBom } from "./json-utils.mjs";
5
+
6
+ function parseArgs(argv) {
7
+ const args = {};
8
+ for (const raw of argv) {
9
+ if (!raw.startsWith("--")) continue;
10
+ const eq = raw.indexOf("=");
11
+ if (eq === -1) args[raw.slice(2)] = true;
12
+ else args[raw.slice(2, eq)] = raw.slice(eq + 1);
13
+ }
14
+ return args;
15
+ }
16
+
17
+ function readTextIfExists(p) {
18
+ if (!fs.existsSync(p)) return "";
19
+ return stripBom(fs.readFileSync(p, "utf8"));
20
+ }
21
+
22
+ function readJsonIfExists(p) {
23
+ const txt = readTextIfExists(p);
24
+ if (!txt.trim()) return null;
25
+ return parseJsonRelaxed(txt, p);
26
+ }
27
+
28
+ function hasPlugin(pluginField, name) {
29
+ if (Array.isArray(pluginField)) return pluginField.includes(name);
30
+ if (typeof pluginField === "string") return pluginField === name;
31
+ return false;
32
+ }
33
+
34
+ function shellConfigPath() {
35
+ const shell = process.env.SHELL || "/bin/bash";
36
+ return path.join(os.homedir(), shell.includes("zsh") ? ".zshrc" : ".bashrc");
37
+ }
38
+
39
+ function isPatchedPlugin(distIndexPath) {
40
+ const code = readTextIfExists(distIndexPath);
41
+ return (
42
+ code.includes("LangfuseSpanProcessor") &&
43
+ code.includes("LANGFUSE_PUBLIC_KEY") &&
44
+ code.includes("createUserIdSpanProcessor")
45
+ );
46
+ }
47
+
48
+ function addResult(results, item, ok, detail, fix = "") {
49
+ results.push({ item, ok, detail, fix });
50
+ }
51
+
52
+ function main() {
53
+ const home = os.homedir();
54
+ const opencodeDir = path.join(home, ".config", "opencode");
55
+ const pkgDir = path.join(opencodeDir, "node_modules", "opencode-plugin-langfuse");
56
+ const pluginDest = path.join(opencodeDir, "plugins", "opencode-plugin-langfuse");
57
+ const langfusePluginPath = "./plugins/opencode-plugin-langfuse";
58
+ const opencodeJsonPath = path.join(opencodeDir, "opencode.json");
59
+ const pluginDistIndex = path.join(pluginDest, "dist", "index.js");
60
+ const nodeModulesDistIndex = path.join(pkgDir, "dist", "index.js");
61
+ const userConfigPath = path.join(home, ".config", "opencode-plugin-langfuse", "config.json");
62
+ const windowsLauncherPath = path.join(opencodeDir, "launch-opencode-langfuse.cmd");
63
+ const unixLauncherPath = path.join(opencodeDir, "launch-opencode-langfuse.sh");
64
+ const opencodeCommandShimPath = path.join(opencodeDir, "bin", process.platform === "win32" ? "opencode.cmd" : "opencode");
65
+ const skillsDiscoverPath = path.join(home, ".config", "oh-langfuse", "opencode-skills-discover.mjs");
66
+
67
+ const results = [];
68
+
69
+ addResult(
70
+ results,
71
+ "OpenCode config dir",
72
+ fs.existsSync(opencodeDir),
73
+ opencodeDir,
74
+ "Run: npx oh-langfuse@latest setup opencode"
75
+ );
76
+
77
+ const settings = readJsonIfExists(opencodeJsonPath);
78
+ addResult(results, "opencode.json", !!settings, opencodeJsonPath, "Run setup again to create OpenCode config.");
79
+
80
+ const otOk = !!(settings?.experimental?.openTelemetry === true);
81
+ addResult(
82
+ results,
83
+ "experimental.openTelemetry",
84
+ otOk,
85
+ otOk ? "true" : String(settings?.experimental?.openTelemetry ?? "missing"),
86
+ "Run: npx oh-langfuse@latest setup opencode"
87
+ );
88
+
89
+ const plOk = hasPlugin(settings?.plugin, langfusePluginPath);
90
+ addResult(
91
+ results,
92
+ `plugin contains ${langfusePluginPath}`,
93
+ plOk,
94
+ JSON.stringify(settings?.plugin ?? null),
95
+ "Run setup again so opencode.json points at the local plugin copy."
96
+ );
97
+
98
+ addResult(
99
+ results,
100
+ "npm package",
101
+ fs.existsSync(path.join(pkgDir, "package.json")),
102
+ pkgDir,
103
+ "Install may have failed. Try: npx oh-langfuse@latest setup opencode --npmRegistry=https://registry.npmmirror.com"
104
+ );
105
+
106
+ addResult(
107
+ results,
108
+ "local plugin copy",
109
+ fs.existsSync(path.join(pluginDest, "package.json")),
110
+ pluginDest,
111
+ "Run setup again; OpenCode loads ./plugins/opencode-plugin-langfuse from opencode.json."
112
+ );
113
+
114
+ addResult(
115
+ results,
116
+ "local plugin patch",
117
+ isPatchedPlugin(pluginDistIndex),
118
+ pluginDistIndex,
119
+ "Run setup again to patch Langfuse credentials/userId injection into the plugin."
120
+ );
121
+
122
+ addResult(
123
+ results,
124
+ "node_modules plugin patch",
125
+ isPatchedPlugin(nodeModulesDistIndex),
126
+ nodeModulesDistIndex,
127
+ "Run setup again to patch the installed npm package before copying it."
128
+ );
129
+
130
+ const userConfig = readJsonIfExists(userConfigPath);
131
+ addResult(
132
+ results,
133
+ "OpenCode Langfuse user config",
134
+ !!(userConfig?.userId || userConfig?.usrid),
135
+ userConfigPath,
136
+ "Run setup again and enter userId when prompted."
137
+ );
138
+
139
+ addResult(
140
+ results,
141
+ "opencode command shim",
142
+ fs.existsSync(opencodeCommandShimPath),
143
+ opencodeCommandShimPath,
144
+ "Run setup again after installing OpenCode so the direct opencode command can load Langfuse and auto-update checks."
145
+ );
146
+
147
+ addResult(
148
+ results,
149
+ "skills discover helper",
150
+ fs.existsSync(skillsDiscoverPath),
151
+ skillsDiscoverPath,
152
+ "Run setup again to install the skills-discover helper that registers nearby skill repos."
153
+ );
154
+
155
+ if (process.platform === "win32") {
156
+ addResult(
157
+ results,
158
+ "launcher cmd",
159
+ fs.existsSync(windowsLauncherPath),
160
+ windowsLauncherPath,
161
+ "Run setup again to create a launcher with LANGFUSE_* variables."
162
+ );
163
+ } else {
164
+ const shellRc = shellConfigPath();
165
+ const shellRcText = readTextIfExists(shellRc);
166
+ addResult(
167
+ results,
168
+ "shell rc LANGFUSE_* block",
169
+ shellRcText.includes("# === Langfuse OpenCode Setup ===") && shellRcText.includes("LANGFUSE_PUBLIC_KEY"),
170
+ shellRc,
171
+ "Run setup again, then open a new terminal or source the shown shell rc file."
172
+ );
173
+ addResult(
174
+ results,
175
+ "launcher sh",
176
+ fs.existsSync(unixLauncherPath),
177
+ unixLauncherPath,
178
+ "Run setup again to create a launcher with LANGFUSE_* variables."
179
+ );
180
+ }
181
+
182
+ const w = Math.max(...results.map((r) => r.item.length)) + 2;
183
+ const failed = [];
184
+ for (const r of results) {
185
+ const status = r.ok ? "OK " : "BAD";
186
+ const pad = (s, n) => (s.length >= n ? s : s + " ".repeat(n - s.length));
187
+ console.log(`${status} ${pad(r.item, w)} ${r.detail}`);
188
+ if (!r.ok) failed.push(r);
189
+ }
190
+
191
+ if (failed.length) {
192
+ console.log("");
193
+ console.log("Fix suggestions:");
194
+ for (const r of failed) {
195
+ if (r.fix) console.log(`- ${r.item}: ${r.fix}`);
196
+ }
197
+ }
198
+
199
+ process.exit(failed.length ? 2 : 0);
200
+ }
201
+
202
+ try {
203
+ parseArgs(process.argv.slice(2));
204
+ main();
205
+ } catch (e) {
206
+ console.error(e?.message || String(e));
207
+ process.exit(1);
208
+ }
@@ -5,6 +5,16 @@ import { resolveOpencodeCli } from "./resolve-opencode-cli.mjs";
5
5
 
6
6
  const USER_ID_PATTERN = /^[a-z](?:\d{8}|wx\d{7})$/;
7
7
  const USER_ID_PATTERN_TEXT = "^[a-z](?:\\d{8}|wx\\d{7})$";
8
+ const DEFAULT_LANGFUSE_BASE_URL = "https://metrics.openharmonyinsight.cn";
9
+ const LEGACY_LANGFUSE_BASE_URLS = new Set([
10
+ "http://120.46.221.227:3000",
11
+ "https://120.46.221.227:3000",
12
+ ]);
13
+
14
+ function normalizeLegacyBaseUrl(baseUrl) {
15
+ const value = String(baseUrl || "").trim().replace(/\/+$/, "");
16
+ return LEGACY_LANGFUSE_BASE_URLS.has(value) ? DEFAULT_LANGFUSE_BASE_URL : baseUrl;
17
+ }
8
18
 
9
19
  function normalizeUserId(v) {
10
20
  return String(v || "").trim();
@@ -42,7 +52,7 @@ function main() {
42
52
  args.publicKey || process.env.LANGFUSE_PUBLIC_KEY || "pk-lf-da0c90a7-6e93-4eb7-bb86-c1047c8d187d";
43
53
  const secretKey =
44
54
  args.secretKey || process.env.LANGFUSE_SECRET_KEY || "sk-lf-0269b85d-bfdc-442c-bfa3-e737954e3315";
45
- const baseUrl = args.langfuseBaseUrl || process.env.LANGFUSE_BASEURL || "http://120.46.221.227:3000";
55
+ const baseUrl = normalizeLegacyBaseUrl(args.langfuseBaseUrl || process.env.LANGFUSE_BASEURL || DEFAULT_LANGFUSE_BASE_URL);
46
56
  const userId = normalizeUserId(args.userId || args.userid || "");
47
57
  if (!userId) {
48
58
  throw new Error("Missing userId. Run with --userId=your-id.");
@@ -56,6 +66,7 @@ function main() {
56
66
  if (args["no-set-env"]) setupExtra.push("--no-set-env");
57
67
  if (args["skip-plugin-install"]) setupExtra.push("--skip-plugin-install");
58
68
  if (userId) setupExtra.push(`--userId=${userId}`);
69
+ setupExtra.push(`--langfuseBaseUrl=${baseUrl}`);
59
70
  runNodeScript(setupPath, setupExtra);
60
71
 
61
72
  // 2) 直接带环境变量启动 opencode(本进程内有效)
@@ -12,6 +12,16 @@ const USER_ID_PATTERN = /^[a-z](?:\d{8}|wx\d{7})$/;
12
12
  const USER_ID_PATTERN_TEXT = "^[a-z](?:\\d{8}|wx\\d{7})$";
13
13
  const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
14
14
  const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8"));
15
+ const DEFAULT_LANGFUSE_BASE_URL = "https://metrics.openharmonyinsight.cn";
16
+ const LEGACY_LANGFUSE_BASE_URLS = new Set([
17
+ "http://120.46.221.227:3000",
18
+ "https://120.46.221.227:3000",
19
+ ]);
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
+ }
15
25
 
16
26
  function normalizeUserId(v) {
17
27
  return String(v || "").trim();
@@ -134,6 +144,7 @@ export function getPatchedLangfuseDistIndexJs() {
134
144
  'import os from "node:os";',
135
145
  'import path from "node:path";',
136
146
  'import { execFileSync } from "node:child_process";',
147
+ 'import { pathToFileURL } from "node:url";',
137
148
  'import { NodeSDK } from "@opentelemetry/sdk-node";',
138
149
  'import { trace } from "@opentelemetry/api";',
139
150
  "",
@@ -677,7 +688,7 @@ export function getPatchedLangfuseDistIndexJs() {
677
688
  " }).filter((event) => event.skill_name);",
678
689
  "};",
679
690
  "",
680
- "const collectKnownSkillNames = async () => {",
691
+ "const collectKnownSkillNames = async (extraPaths) => {",
681
692
  " const dirs = [",
682
693
  " path.join(process.cwd(), '.opencode', 'skill'),",
683
694
  " path.join(process.cwd(), '.opencode', 'skills'),",
@@ -686,6 +697,7 @@ export function getPatchedLangfuseDistIndexJs() {
686
697
  " path.join(os.homedir(), '.opencode', 'skill'),",
687
698
  " path.join(os.homedir(), '.opencode', 'skills'),",
688
699
  " ];",
700
+ " if (Array.isArray(extraPaths)) for (const p of extraPaths) if (typeof p === 'string' && p) dirs.push(p);",
689
701
  " const names = new Set();",
690
702
  " for (const dir of dirs) {",
691
703
  " try {",
@@ -709,7 +721,7 @@ export function getPatchedLangfuseDistIndexJs() {
709
721
  "export const LangfusePlugin = async ({ client }) => {",
710
722
  " const publicKey = process.env.LANGFUSE_PUBLIC_KEY;",
711
723
  " const secretKey = process.env.LANGFUSE_SECRET_KEY;",
712
- ' const baseUrl = process.env.LANGFUSE_BASEURL ?? "https://cloud.langfuse.com";',
724
+ ' const baseUrl = process.env.LANGFUSE_BASEURL ?? "https://metrics.openharmonyinsight.cn";',
713
725
  ' const environment = process.env.LANGFUSE_ENVIRONMENT ?? "development";',
714
726
  "",
715
727
  " const userIdFromConfig = await readConfiguredUserId();",
@@ -735,7 +747,7 @@ export function getPatchedLangfuseDistIndexJs() {
735
747
  ' log("warn", `OTEL SDK start failed: ${err?.message ?? err}`);',
736
748
  " });",
737
749
  " const getMetricsTracer = () => trace.getTracer('oh-langfuse-opencode-metrics');",
738
- " const knownSkillNames = await collectKnownSkillNames();",
750
+ " let knownSkillNames = await collectKnownSkillNames();",
739
751
  " const startupSkillUsages = detectOpencodeSkillUsages(process.argv.join('\\n'), knownSkillNames);",
740
752
  " const startupPromptText = limitObservationText(promptFromArgv());",
741
753
  " const messageTextById = new Map();",
@@ -913,6 +925,26 @@ export function getPatchedLangfuseDistIndexJs() {
913
925
  " if (!config.experimental?.openTelemetry) {",
914
926
  ' log("warn", "OpenTelemetry experimental feature is disabled in Opencode config - tracing disabled");',
915
927
  " }",
928
+ " try {",
929
+ " const discoverPath = path.join(os.homedir(), '.config', 'oh-langfuse', 'opencode-skills-discover.mjs');",
930
+ " let roots = [];",
931
+ " try {",
932
+ " const mod = await import(pathToFileURL(discoverPath).href);",
933
+ " if (mod && typeof mod.collectSkillRoots === 'function') roots = mod.collectSkillRoots(process.cwd());",
934
+ " } catch (e) { /* discover script absent or import failed: skip augmentation */ }",
935
+ " if (roots.length) {",
936
+ " config.skills = config.skills || {};",
937
+ " config.skills.paths = Array.isArray(config.skills.paths) ? [...config.skills.paths] : [];",
938
+ " for (const r of roots) if (!config.skills.paths.includes(r)) config.skills.paths.push(r);",
939
+ " config.permission = config.permission || {};",
940
+ " config.permission.skill = config.permission.skill || {};",
941
+ " config.permission.skill['*'] = 'allow';",
942
+ " }",
943
+ " knownSkillNames = await collectKnownSkillNames(config.skills && config.skills.paths);",
944
+ " if (knownSkillNames.length) log('info', `OpenCode skills discovered -> ${knownSkillNames.length}`);",
945
+ " } catch (e) {",
946
+ " log('warn', `skills config hook failed: ${e && e.message || e}`);",
947
+ " }",
916
948
  " },",
917
949
  " event: async ({ event }) => {",
918
950
  " const eventType = pickEventString(event?.name, event?.type, eventPayload(event)?.name, eventPayload(event)?.type);",
@@ -1024,6 +1056,29 @@ function unixAutoUpdateCommand(target) {
1024
1056
  return `${shQuote(writeAutoUpdateHelper(target))} || true`;
1025
1057
  }
1026
1058
 
1059
+ function skillsDiscoverScriptPath() {
1060
+ return path.join(rootDir, "scripts", "opencode-skills-discover.mjs");
1061
+ }
1062
+
1063
+ function skillsDiscoverInstallPath() {
1064
+ return path.join(os.homedir(), ".config", "oh-langfuse", "opencode-skills-discover.mjs");
1065
+ }
1066
+
1067
+ function installSkillsDiscoverHelper() {
1068
+ const dest = skillsDiscoverInstallPath();
1069
+ ensureDir(path.dirname(dest));
1070
+ fs.copyFileSync(skillsDiscoverScriptPath(), dest);
1071
+ return dest;
1072
+ }
1073
+
1074
+ // installSkillsDiscoverHelper() (above) copies the skills-discover script to
1075
+ // ~/.config/oh-langfuse/opencode-skills-discover.mjs. The opencode plugin's
1076
+ // config hook imports that script (see getPatchedLangfuseDistIndexJs) and calls
1077
+ // its collectSkillRoots(cwd) to register skill repos near cwd into config.skills.paths,
1078
+ // PATH-independently. The earlier shim/OPENCODE_CONFIG_CONTENT approach was removed:
1079
+ // it was shadowed by system-level opencode wrappers (e.g. DevEco Studio) and is
1080
+ // redundant with the plugin config hook, which runs inside opencode regardless of PATH.
1081
+
1027
1082
  function windowsGitPathBootstrap() {
1028
1083
  return [
1029
1084
  "for /f \"delims=\" %%G in ('where.exe git.exe 2^>nul') do (",
@@ -1058,6 +1113,21 @@ function writeWindowsUpdateCheckScript(dir) {
1058
1113
  return p;
1059
1114
  }
1060
1115
 
1116
+ function readRealOpencodeFromManagedShim(shimPath) {
1117
+ try {
1118
+ if (!shimPath || !fs.existsSync(shimPath)) return "";
1119
+ const text = fs.readFileSync(shimPath, "utf8");
1120
+ const cmdMatch = text.match(/set\s+"OH_LANGFUSE_REAL_OPENCODE=([^"]+)"/i);
1121
+ if (cmdMatch?.[1] && fs.existsSync(cmdMatch[1])) return path.resolve(cmdMatch[1]);
1122
+ const shMatch = text.match(/^OH_LANGFUSE_REAL_OPENCODE=(?:"([^"]+)"|'([^']+)'|([^\r\n]+))/m);
1123
+ const candidate = shMatch?.[1] || shMatch?.[2] || shMatch?.[3];
1124
+ if (candidate && fs.existsSync(candidate.trim())) return path.resolve(candidate.trim());
1125
+ } catch {
1126
+ // Best effort only; setup can still fall back to ordinary CLI discovery.
1127
+ }
1128
+ return "";
1129
+ }
1130
+
1061
1131
  function writeOpencodeCommandShim(opencodeDir, { publicKey, secretKey, baseUrl, userId, realOpencodeCli }) {
1062
1132
  const shimDir = path.join(opencodeDir, "bin");
1063
1133
  ensureDir(shimDir);
@@ -1502,8 +1572,9 @@ async function main() {
1502
1572
  args.publicKey || process.env.LANGFUSE_PUBLIC_KEY || "pk-lf-da0c90a7-6e93-4eb7-bb86-c1047c8d187d";
1503
1573
  const secretKey =
1504
1574
  args.secretKey || process.env.LANGFUSE_SECRET_KEY || "sk-lf-0269b85d-bfdc-442c-bfa3-e737954e3315";
1505
- const baseUrl =
1506
- args.langfuseBaseUrl || process.env.LANGFUSE_BASEURL || "http://120.46.221.227:3000";
1575
+ const baseUrl = normalizeLegacyBaseUrl(
1576
+ args.langfuseBaseUrl || process.env.LANGFUSE_BASEURL || DEFAULT_LANGFUSE_BASE_URL
1577
+ );
1507
1578
  const userId = normalizeUserId(args.userId || args.userid || "");
1508
1579
  if (!userId || typeof userId !== "string") {
1509
1580
  throw new Error("缺少参数:--userId=你的工号");
@@ -1552,6 +1623,17 @@ async function main() {
1552
1623
  patchDistIndexJs(distIndexInPlugins);
1553
1624
  }
1554
1625
 
1626
+ // Install the skills-discover script the plugin's config hook imports at runtime.
1627
+ // Wrapped so a missing source file (e.g. not in the published package's files
1628
+ // allowlist) degrades gracefully instead of failing the whole setup.
1629
+ try {
1630
+ const installedDiscover = installSkillsDiscoverHelper();
1631
+ console.log(`Skills discover helper ready: ${installedDiscover}`);
1632
+ } catch (e) {
1633
+ console.log(`Skills discover helper skipped: ${e?.message || e}`);
1634
+ console.log("Skill-repo auto-discovery will not be available until setup is re-run from a complete package.");
1635
+ }
1636
+
1555
1637
  // Write plugin user config to support userId injection.
1556
1638
  if (userId) {
1557
1639
  const cfg = writeLangfusePluginUserConfig({ userId });
@@ -1577,7 +1659,7 @@ async function main() {
1577
1659
  if (realOpencodeCli) {
1578
1660
  const relativeToShim = path.relative(path.resolve(shimDir), path.resolve(realOpencodeCli));
1579
1661
  if (relativeToShim && !relativeToShim.startsWith("..") && !path.isAbsolute(relativeToShim)) {
1580
- realOpencodeCli = "";
1662
+ realOpencodeCli = readRealOpencodeFromManagedShim(realOpencodeCli);
1581
1663
  }
1582
1664
  }
1583
1665
  console.log(`已更新:${opencodeJsonPath}`);