ai-otel-setup 1.0.4 → 1.0.6
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/cli.js +27 -6
- package/package.json +1 -1
- package/templates/on-session-start.js +7 -7
- package/templates/settings.template.json +2 -2
package/cli.js
CHANGED
|
@@ -280,10 +280,32 @@ function stripLegacyCodexHook(text) {
|
|
|
280
280
|
|
|
281
281
|
function stripLegacyCodexHooksFlag(text) {
|
|
282
282
|
// Codex 把 [features].codex_hooks 重命名为 [features].hooks,旧 key 启动时触发 deprecation 警告
|
|
283
|
-
// 删 = true 这行,由
|
|
283
|
+
// 删 = true 这行,由 ensureFeaturesHooksTrue 统一写 hooks = true;= false 是显式 opt-out,保留
|
|
284
284
|
return text.replace(/^[ \t]*codex_hooks[ \t]*=[ \t]*true[ \t]*\r?\n/gm, "");
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
+
function ensureFeaturesHooksTrue(text) {
|
|
288
|
+
// 在用户已有的 [features] 块原地插入 hooks = true(如缺失);没有 [features] 就新建。
|
|
289
|
+
// 不能写在 managed 块里——TOML 1.0 禁止同名 table 重复声明,会被严格解析器拒绝。
|
|
290
|
+
const lines = text.split(/\r?\n/);
|
|
291
|
+
let featuresIdx = -1;
|
|
292
|
+
let hooksKeyExists = false;
|
|
293
|
+
for (let i = 0; i < lines.length; i++) {
|
|
294
|
+
if (featuresIdx === -1) {
|
|
295
|
+
if (/^\s*\[features\]\s*$/.test(lines[i])) featuresIdx = i;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (/^\s*\[/.test(lines[i])) break; // 下一个 section,结束 [features] 主块扫描
|
|
299
|
+
if (/^[ \t]*hooks[ \t]*=/.test(lines[i])) hooksKeyExists = true;
|
|
300
|
+
}
|
|
301
|
+
if (featuresIdx >= 0) {
|
|
302
|
+
if (hooksKeyExists) return text; // 任何 hooks = ... 都尊重,不覆盖用户显式选择
|
|
303
|
+
lines.splice(featuresIdx + 1, 0, "hooks = true");
|
|
304
|
+
return lines.join("\n");
|
|
305
|
+
}
|
|
306
|
+
return text.trimEnd() + "\n\n[features]\nhooks = true\n";
|
|
307
|
+
}
|
|
308
|
+
|
|
287
309
|
function buildCodexManagedBlock(endpoint, hookDest, launcherDest) {
|
|
288
310
|
// exporter / trace_exporter / metrics_exporter 是 externally-tagged enum:
|
|
289
311
|
// - 写 scalar `exporter = "otlp-grpc"`:codex 解析为 unit variant,因为
|
|
@@ -293,11 +315,9 @@ function buildCodexManagedBlock(endpoint, hookDest, launcherDest) {
|
|
|
293
315
|
// - 只写 table `[otel.exporter."otlp-grpc"]`:✓ codex 把它解析为
|
|
294
316
|
// OtlpGrpc { endpoint },tag 来自 key 名。
|
|
295
317
|
// 官方 sample 之所以能 `exporter = "none"`,是因为 None 本身就是 unit variant。
|
|
318
|
+
// [features].hooks = true 由 ensureFeaturesHooksTrue 写到用户块里,避免重复声明 [features]
|
|
296
319
|
return [
|
|
297
320
|
CODEX_MANAGED_BEGIN,
|
|
298
|
-
"[features]",
|
|
299
|
-
"hooks = true",
|
|
300
|
-
"",
|
|
301
321
|
"[otel]",
|
|
302
322
|
'environment = "prod"',
|
|
303
323
|
"log_user_prompt = false",
|
|
@@ -336,11 +356,12 @@ function installCodex(home, endpoint) {
|
|
|
336
356
|
const bak = backup(configPath);
|
|
337
357
|
let existing = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf8") : "";
|
|
338
358
|
|
|
339
|
-
//
|
|
359
|
+
// 先剥离上一次的 managed 块和旧 schema 残留,再保证用户块里有 hooks = true
|
|
340
360
|
existing = stripCodexManagedBlock(existing);
|
|
341
361
|
existing = stripLegacyCodexOtel(existing);
|
|
342
362
|
existing = stripLegacyCodexHook(existing);
|
|
343
363
|
existing = stripLegacyCodexHooksFlag(existing);
|
|
364
|
+
existing = ensureFeaturesHooksTrue(existing);
|
|
344
365
|
|
|
345
366
|
// hook 同目录的 endpoint.json:hook 脚本运行时读它拿 logs endpoint,避免依赖
|
|
346
367
|
// shell 前缀注入 env(cmd.exe 不认那种语法,跨平台必须改成走文件)。
|
|
@@ -454,7 +475,7 @@ function main() {
|
|
|
454
475
|
};
|
|
455
476
|
|
|
456
477
|
// UserPromptSubmit 兜底 hook:复用同一脚本,由 stdin.hook_event_name 在脚本内部
|
|
457
|
-
// 分流。客户端做
|
|
478
|
+
// 分流。客户端做 2 分钟节流,服务端见 entry 已存在则仅补空。用于救 SessionStart
|
|
458
479
|
// 因网络/超时丢失的场景(线上观测约 60% 事件因此空 git/hostname)。
|
|
459
480
|
const promptHookEntry = {
|
|
460
481
|
matcher: "*",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-otel-setup",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "One-shot installer for AI CLI OpenTelemetry forwarding. Writes Claude Code, Codex CLI, and Gemini CLI telemetry config in a single npx command.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ai-otel-setup": "cli.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
*
|
|
21
21
|
* 节流(仅对 UserPromptSubmit):
|
|
22
22
|
* - 在 ~/.claude/cc-otel-state/sent-<sid>.flag 写 marker
|
|
23
|
-
* -
|
|
24
|
-
* -
|
|
23
|
+
* - 2 分钟内同 sid 跳过 OTLP 上报,避免高频敲键狂发
|
|
24
|
+
* - 2 分钟后过期允许重试,给丢包/瞬时故障留救命窗口
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
"use strict";
|
|
@@ -34,8 +34,8 @@ const http = require("http");
|
|
|
34
34
|
const https = require("https");
|
|
35
35
|
const { URL } = require("url");
|
|
36
36
|
|
|
37
|
-
// UserPromptSubmit 节流窗口:
|
|
38
|
-
const PROMPT_THROTTLE_MS =
|
|
37
|
+
// UserPromptSubmit 节流窗口:2 分钟
|
|
38
|
+
const PROMPT_THROTTLE_MS = 2 * 60 * 1000;
|
|
39
39
|
|
|
40
40
|
// -------- 环境变量读取 ----------
|
|
41
41
|
|
|
@@ -120,7 +120,7 @@ function safeGit(args) {
|
|
|
120
120
|
// CC 在 stdin 里告诉脚本是哪个 hook 触发的;UserPromptSubmit 走"兜底"分支
|
|
121
121
|
const isPromptFallback = input.hook_event_name === "UserPromptSubmit";
|
|
122
122
|
|
|
123
|
-
// 兜底路径节流:sid 维度
|
|
123
|
+
// 兜底路径节流:sid 维度 2 分钟最多一次(marker 文件 mtime 判断)。
|
|
124
124
|
// 失败重试窗口同时由此控制:marker 过期后允许下次 prompt 再发一次。
|
|
125
125
|
const stateDir = path.join(os.homedir(), ".claude", "cc-otel-state");
|
|
126
126
|
const markerPath = sessionId ? path.join(stateDir, `sent-${sessionId}.flag`) : null;
|
|
@@ -217,8 +217,8 @@ function safeGit(args) {
|
|
|
217
217
|
req.on("timeout", () => { req.destroy(); done(); });
|
|
218
218
|
|
|
219
219
|
// 在真正发包前 touch marker 文件——把"已尝试上报"持久化下来,
|
|
220
|
-
// 让后续
|
|
221
|
-
// 因为
|
|
220
|
+
// 让后续 2 分钟内的 UserPromptSubmit 跳过重复 POST。失败也照写,
|
|
221
|
+
// 因为 2 分钟后 marker 会过期允许重试,不会永久卡住。
|
|
222
222
|
if (markerPath) {
|
|
223
223
|
try {
|
|
224
224
|
fs.mkdirSync(stateDir, { recursive: true });
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"OTEL_LOGS_EXPORTER": "otlp",
|
|
6
6
|
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
|
|
7
7
|
"OTEL_EXPORTER_OTLP_ENDPOINT": "PLACEHOLDER_ENDPOINT",
|
|
8
|
-
"OTEL_LOGS_EXPORT_INTERVAL": "
|
|
9
|
-
"OTEL_METRIC_EXPORT_INTERVAL": "
|
|
8
|
+
"OTEL_LOGS_EXPORT_INTERVAL": "120000",
|
|
9
|
+
"OTEL_METRIC_EXPORT_INTERVAL": "300000",
|
|
10
10
|
"OTEL_METRICS_INCLUDE_VERSION": "true",
|
|
11
11
|
"OTEL_LOG_USER_PROMPTS": "0",
|
|
12
12
|
"OTEL_LOG_TOOL_DETAILS": "1",
|