@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.
- package/index.js +237 -0
- 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
|
+
}
|