@cnpinsight/cnpclawinsights 2.0.1 → 2.0.2

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 (3) hide show
  1. package/README.md +63 -0
  2. package/dist/cli.mjs +206 -0
  3. package/package.json +4 -1
package/README.md CHANGED
@@ -20,6 +20,69 @@ openclaw plugins install @cnpinsight/cnpclawinsights
20
20
  openclaw plugins install @cnpinsight/cnpclawinsights@2.0.0
21
21
  ```
22
22
 
23
+ ## 安装器
24
+
25
+ 如果你不想手动编辑 `openclaw.json`,可以使用安装器命令自动写入插件配置。
26
+
27
+ 最简单的方式:
28
+
29
+ ```bash
30
+ npm exec -y --package=@cnpinsight/cnpclawinsights -- \
31
+ cnpclawinsights-install \
32
+ --non-interactive
33
+ ```
34
+
35
+ 这会自动把配置写入默认的 `~/.openclaw/openclaw.json`,并生成:
36
+
37
+ ```json
38
+ {
39
+ "plugins": {
40
+ "entries": {
41
+ "cnpclawinsights": {
42
+ "enabled": true,
43
+ "config": {
44
+ "otel": {
45
+ "endpoint": "http://127.0.0.1:4318"
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ 指定 endpoint 并开启 logs:
55
+
56
+ ```bash
57
+ npm exec -y --package=@cnpinsight/cnpclawinsights -- \
58
+ cnpclawinsights-install \
59
+ --non-interactive \
60
+ --endpoint http://127.0.0.1:4318 \
61
+ --logs
62
+ ```
63
+
64
+ 指定 service name 和 header:
65
+
66
+ ```bash
67
+ npm exec -y --package=@cnpinsight/cnpclawinsights -- \
68
+ cnpclawinsights-install \
69
+ --non-interactive \
70
+ --service-name openclaw \
71
+ --header Authorization="Bearer <token>"
72
+ ```
73
+
74
+ 安装器支持的主要参数:
75
+
76
+ - `--config-path <path>`:指定 `openclaw.json` 路径
77
+ - `--endpoint <url>`:设置 OTLP endpoint
78
+ - `--service-name <name>`:设置 OTel service name
79
+ - `--logs` / `--no-logs`:开启或关闭 logs 导出
80
+ - `--traces` / `--no-traces`:开启或关闭 traces 导出
81
+ - `--metrics` / `--no-metrics`:开启或关闭 metrics 导出
82
+ - `--sample-rate <0..1>`:设置 trace 采样率
83
+ - `--flush-interval-ms <ms>`:设置导出刷新间隔
84
+ - `--header <k=v>`:追加请求头,可重复传入
85
+
23
86
  ## 最小配置
24
87
 
25
88
  在 `openclaw.json` 中添加:
package/dist/cli.mjs ADDED
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ const PLUGIN_ID = "cnpclawinsights";
8
+ const LEGACY_PLUGIN_ID = "cnpclawinsight";
9
+ const DEFAULT_CONFIG_PATH = path.join(os.homedir(), ".openclaw", "openclaw.json");
10
+ const DEFAULT_ENDPOINT = "http://127.0.0.1:4318";
11
+
12
+ function printHelp() {
13
+ console.log(`Usage:
14
+ cnpclawinsights-install [options]
15
+
16
+ Options:
17
+ --config-path <path> Path to openclaw.json
18
+ --endpoint <url> OTLP endpoint, default: ${DEFAULT_ENDPOINT}
19
+ --service-name <name> OpenTelemetry service name
20
+ --logs Enable logs export
21
+ --no-logs Disable logs export
22
+ --traces Enable traces export
23
+ --no-traces Disable traces export
24
+ --metrics Enable metrics export
25
+ --no-metrics Disable metrics export
26
+ --sample-rate <0..1> Trace sampling rate
27
+ --flush-interval-ms <ms> Export flush interval
28
+ --header <k=v> Add OTLP header, repeatable
29
+ --non-interactive Run without prompts
30
+ --help Show this help
31
+ `);
32
+ }
33
+
34
+ function fail(message) {
35
+ console.error(`cnpclawinsights-install: ${message}`);
36
+ process.exit(1);
37
+ }
38
+
39
+ function parseArgs(argv) {
40
+ const options = {
41
+ configPath: DEFAULT_CONFIG_PATH,
42
+ endpoint: DEFAULT_ENDPOINT,
43
+ headers: {}
44
+ };
45
+
46
+ for (let i = 0; i < argv.length; i += 1) {
47
+ const arg = argv[i];
48
+ switch (arg) {
49
+ case "--config-path":
50
+ options.configPath = argv[++i];
51
+ break;
52
+ case "--endpoint":
53
+ options.endpoint = argv[++i];
54
+ break;
55
+ case "--service-name":
56
+ options.serviceName = argv[++i];
57
+ break;
58
+ case "--logs":
59
+ options.logs = true;
60
+ break;
61
+ case "--no-logs":
62
+ options.logs = false;
63
+ break;
64
+ case "--traces":
65
+ options.traces = true;
66
+ break;
67
+ case "--no-traces":
68
+ options.traces = false;
69
+ break;
70
+ case "--metrics":
71
+ options.metrics = true;
72
+ break;
73
+ case "--no-metrics":
74
+ options.metrics = false;
75
+ break;
76
+ case "--sample-rate":
77
+ options.sampleRate = Number(argv[++i]);
78
+ break;
79
+ case "--flush-interval-ms":
80
+ options.flushIntervalMs = Number(argv[++i]);
81
+ break;
82
+ case "--header": {
83
+ const raw = argv[++i];
84
+ const eqIndex = raw.indexOf("=");
85
+ if (eqIndex <= 0) fail(`invalid --header value: ${raw}`);
86
+ const key = raw.slice(0, eqIndex).trim();
87
+ const value = raw.slice(eqIndex + 1).trim();
88
+ if (!key || !value) fail(`invalid --header value: ${raw}`);
89
+ options.headers[key] = value;
90
+ break;
91
+ }
92
+ case "--non-interactive":
93
+ options.nonInteractive = true;
94
+ break;
95
+ case "--help":
96
+ case "-h":
97
+ options.help = true;
98
+ break;
99
+ default:
100
+ fail(`unknown argument: ${arg}`);
101
+ }
102
+ }
103
+
104
+ return options;
105
+ }
106
+
107
+ function ensureParentDir(filePath) {
108
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
109
+ }
110
+
111
+ function readConfig(configPath) {
112
+ if (!fs.existsSync(configPath)) {
113
+ return {};
114
+ }
115
+ try {
116
+ return JSON.parse(fs.readFileSync(configPath, "utf8"));
117
+ } catch (error) {
118
+ fail(`failed to parse config file ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
119
+ }
120
+ }
121
+
122
+ function normalizePluginEntry(entries) {
123
+ const pluralEntry = entries[PLUGIN_ID];
124
+ if (pluralEntry && typeof pluralEntry === "object") {
125
+ return pluralEntry;
126
+ }
127
+ const legacyEntry = entries[LEGACY_PLUGIN_ID];
128
+ if (legacyEntry && typeof legacyEntry === "object") {
129
+ delete entries[LEGACY_PLUGIN_ID];
130
+ entries[PLUGIN_ID] = legacyEntry;
131
+ return legacyEntry;
132
+ }
133
+ const entry = {};
134
+ entries[PLUGIN_ID] = entry;
135
+ return entry;
136
+ }
137
+
138
+ function buildPluginConfig(options) {
139
+ const otel = {
140
+ endpoint: options.endpoint
141
+ };
142
+
143
+ if (options.logs !== undefined) otel.logs = options.logs;
144
+ if (options.traces !== undefined) otel.traces = options.traces;
145
+ if (options.metrics !== undefined) otel.metrics = options.metrics;
146
+ if (options.serviceName) otel.serviceName = options.serviceName;
147
+ if (options.sampleRate !== undefined) otel.sampleRate = options.sampleRate;
148
+ if (options.flushIntervalMs !== undefined) otel.flushIntervalMs = options.flushIntervalMs;
149
+ if (Object.keys(options.headers).length > 0) otel.headers = options.headers;
150
+
151
+ return { otel };
152
+ }
153
+
154
+ function writeConfig(configPath, config) {
155
+ ensureParentDir(configPath);
156
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
157
+ }
158
+
159
+ function main() {
160
+ const options = parseArgs(process.argv.slice(2));
161
+ if (options.help) {
162
+ printHelp();
163
+ return;
164
+ }
165
+
166
+ if (options.sampleRate !== undefined && (!Number.isFinite(options.sampleRate) || options.sampleRate < 0 || options.sampleRate > 1)) {
167
+ fail("--sample-rate must be a number between 0 and 1");
168
+ }
169
+
170
+ if (options.flushIntervalMs !== undefined && (!Number.isInteger(options.flushIntervalMs) || options.flushIntervalMs < 1000)) {
171
+ fail("--flush-interval-ms must be an integer >= 1000");
172
+ }
173
+
174
+ const config = readConfig(options.configPath);
175
+ if (!config.plugins || typeof config.plugins !== "object" || Array.isArray(config.plugins)) {
176
+ config.plugins = {};
177
+ }
178
+ if (!config.plugins.entries || typeof config.plugins.entries !== "object" || Array.isArray(config.plugins.entries)) {
179
+ config.plugins.entries = {};
180
+ }
181
+ const entry = normalizePluginEntry(config.plugins.entries);
182
+ entry.enabled = true;
183
+ entry.config = {
184
+ ...(entry.config && typeof entry.config === "object" && !Array.isArray(entry.config) ? entry.config : {}),
185
+ ...buildPluginConfig(options)
186
+ };
187
+
188
+ if (!Array.isArray(config.plugins.allow)) {
189
+ config.plugins.allow = [];
190
+ }
191
+ if (!config.plugins.allow.includes(PLUGIN_ID)) {
192
+ config.plugins.allow.push(PLUGIN_ID);
193
+ }
194
+ config.plugins.allow = config.plugins.allow.filter((value, index, values) => value !== LEGACY_PLUGIN_ID || values.indexOf(PLUGIN_ID) === -1);
195
+
196
+ writeConfig(options.configPath, config);
197
+
198
+ console.log(`Updated ${options.configPath}`);
199
+ console.log(`Configured plugins.entries.${PLUGIN_ID}.enabled = true`);
200
+ console.log(`Configured plugins.entries.${PLUGIN_ID}.config.otel.endpoint = ${options.endpoint}`);
201
+ if (options.logs !== undefined) {
202
+ console.log(`Configured plugins.entries.${PLUGIN_ID}.config.otel.logs = ${String(options.logs)}`);
203
+ }
204
+ }
205
+
206
+ main();
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@cnpinsight/cnpclawinsights",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "OpenClaw insights OpenTelemetry exporter",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/openclaw/openclaw"
8
8
  },
9
9
  "type": "module",
10
+ "bin": {
11
+ "cnpclawinsights-install": "dist/cli.mjs"
12
+ },
10
13
  "dependencies": {
11
14
  "@opentelemetry/api": "1.9.1",
12
15
  "@opentelemetry/api-logs": "0.218.0",