@luzhuohuan-bd/openclaw-otel-plugin-cli 0.1.0

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.
Files changed (2) hide show
  1. package/index.js +237 -0
  2. package/package.json +14 -0
package/index.js ADDED
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import inquirer from "inquirer";
5
+ import { execSync } from "node:child_process";
6
+ import fs from "node:fs/promises";
7
+ import path from "node:path";
8
+ import os from "node:os";
9
+
10
+ const PLUGIN_ID = "openclaw-otel-plugin";
11
+ const NPM_PACKAGE = "@luzhuohuan-bd/openclaw-otel-plugin";
12
+
13
+ // ─── Helpers ──────────────────────────────────────────────
14
+
15
+ function getConfigPath() {
16
+ const stateDir = process.env.OPENCLAW_STATE_DIR || path.join(os.homedir(), ".openclaw");
17
+ return path.join(stateDir, "openclaw.json");
18
+ }
19
+
20
+ async function readConfig() {
21
+ try {
22
+ return JSON.parse(await fs.readFile(getConfigPath(), "utf8"));
23
+ } catch (e) {
24
+ if (e.code === "ENOENT") return {};
25
+ throw e;
26
+ }
27
+ }
28
+
29
+ async function writeConfig(config) {
30
+ const p = getConfigPath();
31
+ await fs.mkdir(path.dirname(p), { recursive: true });
32
+ await fs.writeFile(p, JSON.stringify(config, null, 2), "utf8");
33
+ }
34
+
35
+ function run(cmd) {
36
+ execSync(cmd, { stdio: "inherit" });
37
+ }
38
+
39
+ function runQuiet(cmd) {
40
+ return execSync(cmd, { encoding: "utf8" }).trim();
41
+ }
42
+
43
+ function platformCmd(cmd) {
44
+ return process.platform === "win32" ? `${cmd}.cmd` : cmd;
45
+ }
46
+
47
+ // ─── Collect config ───────────────────────────────────────
48
+
49
+ async function collectConfig(opts) {
50
+ const config = await readConfig();
51
+ const existing = config.plugins?.entries?.[PLUGIN_ID]?.config || {};
52
+
53
+ if (opts.nonInteractive) {
54
+ return {
55
+ endpoint: opts.endpoint || existing.endpoint || "http://localhost:4318",
56
+ token: opts.token || "",
57
+ headers: opts.token
58
+ ? { Authorization: `Bearer ${opts.token}` }
59
+ : existing.headers || {},
60
+ debug: opts.debug ?? existing.debug ?? false,
61
+ allowDetail: opts.allowDetail ?? existing.allowUserDetailInfoReport ?? false,
62
+ };
63
+ }
64
+
65
+ const answers = await inquirer.prompt([
66
+ {
67
+ name: "endpoint",
68
+ type: "input",
69
+ message: "OTLP endpoint URL:",
70
+ default: opts.endpoint || existing.endpoint || "http://localhost:4318",
71
+ validate: (v) => (v && v.trim() ? true : "Endpoint cannot be empty"),
72
+ },
73
+ {
74
+ name: "needAuth",
75
+ type: "confirm",
76
+ message: "Need Bearer token authentication?",
77
+ default: !!existing.headers?.Authorization,
78
+ },
79
+ {
80
+ name: "token",
81
+ type: "input",
82
+ message: "Bearer token:",
83
+ when: (a) => a.needAuth,
84
+ default: opts.token || existing.headers?.Authorization?.replace(/^Bearer\s+/i, "") || "",
85
+ validate: (v) => (v && v.trim() ? true : "Token cannot be empty"),
86
+ },
87
+ {
88
+ name: "extraHeaders",
89
+ type: "input",
90
+ message: "Extra headers (key:value,key:value) or leave empty:",
91
+ default: "",
92
+ },
93
+ {
94
+ name: "debug",
95
+ type: "confirm",
96
+ message: "Enable debug logging?",
97
+ default: opts.debug ?? existing.debug ?? false,
98
+ },
99
+ {
100
+ name: "allowDetail",
101
+ type: "confirm",
102
+ message: "Report message content in spans/logs? (privacy sensitive)",
103
+ default: opts.allowDetail ?? existing.allowUserDetailInfoReport ?? false,
104
+ },
105
+ ]);
106
+
107
+ const headers = {};
108
+ if (answers.needAuth && answers.token) {
109
+ headers.Authorization = `Bearer ${answers.token.trim()}`;
110
+ }
111
+ if (answers.extraHeaders) {
112
+ for (const pair of answers.extraHeaders.split(",")) {
113
+ const [k, ...v] = pair.split(":");
114
+ if (k && v.length) headers[k.trim()] = v.join(":").trim();
115
+ }
116
+ }
117
+
118
+ return {
119
+ endpoint: answers.endpoint.trim(),
120
+ headers,
121
+ debug: answers.debug,
122
+ allowDetail: answers.allowDetail,
123
+ };
124
+ }
125
+
126
+ // ─── Update openclaw.json ─────────────────────────────────
127
+
128
+ async function updateConfig(pluginConfig) {
129
+ const config = await readConfig();
130
+
131
+ // plugins.allow
132
+ if (!config.plugins) config.plugins = {};
133
+ if (!config.plugins.allow) config.plugins.allow = [];
134
+ if (!config.plugins.allow.includes(PLUGIN_ID)) {
135
+ config.plugins.allow.push(PLUGIN_ID);
136
+ }
137
+
138
+ // plugins.entries
139
+ if (!config.plugins.entries) config.plugins.entries = {};
140
+ const entry = config.plugins.entries[PLUGIN_ID] || {};
141
+ entry.enabled = true;
142
+ entry.config = {
143
+ ...(entry.config || {}),
144
+ endpoint: pluginConfig.endpoint,
145
+ headers: Object.keys(pluginConfig.headers).length ? pluginConfig.headers : undefined,
146
+ debug: pluginConfig.debug,
147
+ allowUserDetailInfoReport: pluginConfig.allowDetail,
148
+ traces: true,
149
+ metrics: true,
150
+ logs: true,
151
+ };
152
+ // Clean undefined
153
+ Object.keys(entry.config).forEach((k) => entry.config[k] === undefined && delete entry.config[k]);
154
+ config.plugins.entries[PLUGIN_ID] = entry;
155
+
156
+ await writeConfig(config);
157
+ }
158
+
159
+ // ─── Commands ─────────────────────────────────────────────
160
+
161
+ async function handleInstall(opts) {
162
+ // Check openclaw CLI
163
+ const oc = platformCmd("openclaw");
164
+ try {
165
+ runQuiet(`${oc} --version`);
166
+ } catch {
167
+ console.error("Error: openclaw CLI not found. Please install openclaw first.");
168
+ process.exit(1);
169
+ }
170
+
171
+ console.log(`\n openclaw-otel-plugin installer\n`);
172
+
173
+ const pluginConfig = await collectConfig(opts);
174
+
175
+ // Install plugin
176
+ console.log(`\nInstalling ${NPM_PACKAGE}...`);
177
+ try {
178
+ run(`${oc} plugins install ${NPM_PACKAGE}`);
179
+ } catch {
180
+ console.error(`Failed to install ${NPM_PACKAGE}. Check your network or npm registry.`);
181
+ process.exit(1);
182
+ }
183
+
184
+ // Write config
185
+ await updateConfig(pluginConfig);
186
+ console.log("Configuration saved.");
187
+
188
+ // Restart
189
+ try {
190
+ console.log("Restarting OpenClaw gateway...");
191
+ run(`${oc} gateway restart`);
192
+ } catch {
193
+ console.log("Gateway restart failed. Run `openclaw gateway restart` manually.");
194
+ }
195
+
196
+ console.log(`\n Done! Plugin enabled with endpoint: ${pluginConfig.endpoint}\n`);
197
+ }
198
+
199
+ async function handleConfig(opts) {
200
+ const pluginConfig = await collectConfig(opts);
201
+ await updateConfig(pluginConfig);
202
+ console.log("Configuration updated.");
203
+
204
+ try {
205
+ run(`${platformCmd("openclaw")} gateway restart`);
206
+ console.log("Gateway restarted.");
207
+ } catch {
208
+ console.log("Run `openclaw gateway restart` to apply changes.");
209
+ }
210
+ }
211
+
212
+ // ─── CLI ──────────────────────────────────────────────────
213
+
214
+ const program = new Command();
215
+ program.name("openclaw-otel-plugin-cli").version("1.0.0");
216
+
217
+ program
218
+ .command("install", { isDefault: true })
219
+ .description("Install and configure openclaw-otel-plugin")
220
+ .option("--endpoint <url>", "OTLP endpoint URL")
221
+ .option("--token <token>", "Bearer token for authentication")
222
+ .option("--debug", "Enable debug logging")
223
+ .option("--allow-detail", "Report message content (privacy sensitive)")
224
+ .option("--non-interactive", "Run without prompts (requires --endpoint)")
225
+ .action(handleInstall);
226
+
227
+ program
228
+ .command("config")
229
+ .description("Update plugin configuration (without reinstalling)")
230
+ .option("--endpoint <url>", "OTLP endpoint URL")
231
+ .option("--token <token>", "Bearer token")
232
+ .option("--debug", "Enable debug logging")
233
+ .option("--allow-detail", "Report message content")
234
+ .option("--non-interactive", "Run without prompts")
235
+ .action(handleConfig);
236
+
237
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@luzhuohuan-bd/openclaw-otel-plugin-cli",
3
+ "version": "0.1.0",
4
+ "description": "One-click installer for @luzhuohuan-bd/openclaw-otel-plugin",
5
+ "type": "module",
6
+ "files": ["index.js"],
7
+ "bin": {
8
+ "openclaw-otel-plugin-cli": "./index.js"
9
+ },
10
+ "dependencies": {
11
+ "commander": "^12.0.0",
12
+ "inquirer": "^9.2.0"
13
+ }
14
+ }