vibeusage 0.3.1 → 0.3.3

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/README.md CHANGED
@@ -209,7 +209,7 @@ graph LR
209
209
  C[Gemini CLI] -->|Session Logs| G
210
210
  D[Opencode] -->|SQLite DB| G
211
211
  E[Claude Code] -->|Hook Output| G
212
- F[OpenClaw] -->|Session Plugin| G
212
+ F[OpenClaw] -->|Session Plugin → Sanitized Ledger| G
213
213
  G -->|AI Tokens| H{Core Relay}
214
214
  H --> I[VibeUsage Dashboard]
215
215
  H --> J[AI Analytics Engine]
@@ -236,7 +236,7 @@ graph LR
236
236
 
237
237
  1. AI CLI tools generate logs during usage
238
238
  2. Local `notify-handler` detects changes and triggers sync
239
- 3. CLI incrementally parses logs and SQLite state, extracting whitelist token counts only
239
+ 3. CLI incrementally parses logs, SQLite state, and the OpenClaw sanitized ledger, extracting whitelist token counts only
240
240
  4. Data aggregated into 30-minute UTC buckets locally
241
241
  5. Batch upload to InsForge with idempotent deduplication
242
242
  6. Dashboard queries aggregated results for visualization
@@ -250,7 +250,7 @@ graph LR
250
250
  | **Gemini CLI** | `~/.gemini/tmp/**/chats/session-*.json` | `GEMINI_HOME` |
251
251
  | **Opencode** | `~/.local/share/opencode/opencode.db` (legacy `storage/message/**/*.json` fallback) | `OPENCODE_HOME` |
252
252
  | **Claude Code** | Parsed from hook output | - |
253
- | **OpenClaw** | Session plugin integration | - |
253
+ | **OpenClaw** | Session plugin local sanitized usage ledger | - |
254
254
 
255
255
  ## ⚙️ Configuration
256
256
 
@@ -279,6 +279,8 @@ graph LR
279
279
 
280
280
  </details>
281
281
 
282
+ See also: [`docs/openclaw-integration.md`](docs/openclaw-integration.md) for the OpenClaw single-path accounting contract.
283
+
282
284
  ## 🔧 Troubleshooting
283
285
 
284
286
  <details>
@@ -380,8 +382,8 @@ This project uses **OpenSpec** for spec-driven development. Before making signif
380
382
 
381
383
  1. Read [`openspec/project.md`](openspec/project.md) for project conventions
382
384
  2. Check [`openspec/AGENTS.md`](openspec/AGENTS.md) for the full OpenSpec workflow
383
- 3. Run `openspec list` to see active changes
384
- 4. Run `openspec list --specs` to see existing specifications
385
+ 3. Run `npx openspec list` to see active changes
386
+ 4. Run `npx openspec list --specs` to see existing specifications
385
387
 
386
388
  See [CLAUDE.md](CLAUDE.md) for detailed guidelines.
387
389
 
package/README.zh-CN.md CHANGED
@@ -143,7 +143,7 @@ npx vibeusage init [选项]
143
143
  | **Codex CLI** | `~/.codex/config.toml` | `notify` 钩子 |
144
144
  | **Every Code** | `~/.code/config.toml`(或 `CODE_HOME`) | `notify` 钩子 |
145
145
  | **Gemini CLI** | `~/.gemini/settings.json`(或 `GEMINI_HOME`) | `SessionEnd` 钩子 |
146
- | **Opencode** | 全局插件 | 消息解析器插件 |
146
+ | **Opencode** | OpenCode 配置/插件 | SQLite-first 解析插件 |
147
147
  | **Claude Code** | `~/.claude/settings.json` | `Stop` + `SessionEnd` 钩子 |
148
148
  | **OpenClaw** | 安装时自动链接 | Session plugin(需要重启) |
149
149
 
@@ -207,9 +207,9 @@ graph LR
207
207
  A[Codex CLI] -->|Rollout 日志| G(Tracker CLI)
208
208
  B[Every Code] -->|Rollout 日志| G
209
209
  C[Gemini CLI] -->|会话日志| G
210
- D[Opencode] -->|消息日志| G
210
+ D[Opencode] -->|SQLite DB| G
211
211
  E[Claude Code] -->|钩子输出| G
212
- F[OpenClaw] -->|Gateway 钩子| G
212
+ F[OpenClaw] -->|Session Plugin → Sanitized Ledger| G
213
213
  G -->|AI Tokens| H{核心中继}
214
214
  H --> I[VibeUsage 控制台]
215
215
  H --> J[AI 分析引擎]
@@ -236,7 +236,7 @@ graph LR
236
236
 
237
237
  1. AI CLI 工具在使用过程中生成日志
238
238
  2. 本地 `notify-handler` 检测更改并触发同步
239
- 3. CLI 增量解析日志,提取 token 计数(仅白名单字段)
239
+ 3. CLI 增量解析日志、SQLite 状态以及 OpenClaw 的本地脱敏 usage ledger,只提取白名单 token 计数
240
240
  4. 数据在本地聚合到 30 分钟 UTC 桶中
241
241
  5. 批量上传到 InsForge,带幂等去重
242
242
  6. 控制台查询聚合结果进行可视化
@@ -248,9 +248,9 @@ graph LR
248
248
  | **Codex CLI** | `~/.codex/sessions/**/rollout-*.jsonl` | `CODEX_HOME` |
249
249
  | **Every Code** | `~/.code/sessions/**/rollout-*.jsonl` | `CODE_HOME` |
250
250
  | **Gemini CLI** | `~/.gemini/tmp/**/chats/session-*.json` | `GEMINI_HOME` |
251
- | **Opencode** | `~/.opencode/messages/*.json` | - |
251
+ | **Opencode** | `~/.local/share/opencode/opencode.db`(旧版 `storage/message/**/*.json` 仅作回退) | `OPENCODE_HOME` |
252
252
  | **Claude Code** | 从钩子输出解析 | - |
253
- | **OpenClaw** | Gateway 钩子集成 | - |
253
+ | **OpenClaw** | Session plugin → 本地脱敏 usage ledger | - |
254
254
 
255
255
  ## ⚙️ 配置
256
256
 
@@ -278,6 +278,8 @@ graph LR
278
278
 
279
279
  </details>
280
280
 
281
+ 另见:[`docs/openclaw-integration.md`](docs/openclaw-integration.md),其中说明了 OpenClaw 单一路径 accounting 合同。
282
+
281
283
  ## 🔧 故障排查
282
284
 
283
285
  <details>
@@ -374,8 +376,8 @@ npm run smoke
374
376
 
375
377
  1. 阅读 [`openspec/project.md`](openspec/project.md) 了解项目约定
376
378
  2. 查看 [`openspec/AGENTS.md`](openspec/AGENTS.md) 了解完整的 OpenSpec 工作流
377
- 3. 运行 `openspec list` 查看活跃的更改
378
- 4. 运行 `openspec list --specs` 查看现有规格
379
+ 3. 运行 `npx openspec list` 查看活跃的更改
380
+ 4. 运行 `npx openspec list --specs` 查看现有规格
379
381
 
380
382
  详见 [CLAUDE.md](CLAUDE.md) 了解详细指南。
381
383
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeusage",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Codex CLI token usage tracker (macOS-first, notify-driven).",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -81,7 +81,6 @@ async function cmdStatus(argv = []) {
81
81
  const geminiProbe = probeByName.get("gemini");
82
82
  const opencodeProbe = probeByName.get("opencode");
83
83
  const openclawSessionProbe = probeByName.get("openclaw-session");
84
- const openclawLegacyProbe = probeByName.get("openclaw-legacy");
85
84
  const opencodeDbPresent = Boolean((await safeStat(opencodeDbPath))?.isFile?.());
86
85
  const opencodeSqliteState =
87
86
  cursors?.opencodeSqlite && typeof cursors.opencodeSqlite === "object"
@@ -118,10 +117,6 @@ async function cmdStatus(argv = []) {
118
117
  descriptors.get("openclaw-session"),
119
118
  openclawSessionProbe,
120
119
  )}`,
121
- `- OpenClaw hook (legacy): ${renderIntegrationStatus(
122
- descriptors.get("openclaw-legacy"),
123
- openclawLegacyProbe,
124
- )}`,
125
120
  ...subscriptionLines,
126
121
  "",
127
122
  ]
@@ -13,9 +13,15 @@ const {
13
13
  parseClaudeIncremental,
14
14
  parseGeminiIncremental,
15
15
  parseOpencodeIncremental,
16
- parseOpenclawIncremental,
16
+ normalizeHourlyState,
17
+ getHourlyBucket,
18
+ addTotals,
19
+ bucketKey,
20
+ enqueueTouchedBuckets,
21
+ toUtcHalfHourStart,
17
22
  } = require("../lib/rollout");
18
23
  const { drainQueueToCloud } = require("../lib/uploader");
24
+ const { readOpenclawUsageLedger } = require("../lib/openclaw-usage-ledger");
19
25
  const { collectLocalSubscriptions } = require("../lib/subscriptions");
20
26
  const { createProgress, renderBar, formatNumber, formatBytes } = require("../lib/progress");
21
27
  const { syncHeartbeat } = require("../lib/vibeusage-api");
@@ -70,13 +76,6 @@ async function cmdSync(argv) {
70
76
  const opencodeStorageDir = path.join(opencodeHome, "storage");
71
77
  const opencodeDbPath = path.join(opencodeHome, "opencode.db");
72
78
 
73
- // OpenClaw session-plugin integration: allow a plugin-triggered sync to request incremental parsing
74
- // for a single session jsonl. We still parse all regular sources so model/source attribution stays
75
- // complete (e.g. Kimi sessions).
76
- const openclawSignal = opts.fromOpenclaw
77
- ? resolveOpenclawSignal({ home, env: process.env })
78
- : null;
79
-
80
79
  const sources = [
81
80
  { source: "codex", sessionsDir: path.join(codexHome, "sessions") },
82
81
  { source: "every-code", sessionsDir: path.join(codeHome, "sessions") },
@@ -93,10 +92,6 @@ async function cmdSync(argv) {
93
92
  }
94
93
  }
95
94
 
96
- const openclawFiles = openclawSignal?.sessionFile
97
- ? [{ path: openclawSignal.sessionFile, source: "openclaw" }]
98
- : [];
99
-
100
95
  if (progress?.enabled) {
101
96
  progress.start(
102
97
  `Parsing ${renderBar(0)} 0/${formatNumber(rolloutFiles.length)} files | buckets 0`,
@@ -119,28 +114,13 @@ async function cmdSync(argv) {
119
114
  },
120
115
  });
121
116
 
122
- let openclawResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
123
- if (openclawFiles.length > 0) {
124
- // Only runs when explicitly triggered by OpenClaw session-plugin events.
125
- openclawResult = await parseOpenclawIncremental({
126
- sessionFiles: openclawFiles,
127
- cursors,
128
- queuePath,
129
- projectQueuePath,
130
- source: "openclaw",
131
- });
132
- }
133
-
134
- const openclawFallback = await applyOpenclawTotalsFallback({
135
- trackerDir,
136
- signal: openclawSignal,
137
- cursors,
138
- queuePath,
139
- projectQueuePath,
140
- });
141
- openclawResult.filesProcessed += openclawFallback.filesProcessed;
142
- openclawResult.eventsAggregated += openclawFallback.eventsAggregated;
143
- openclawResult.bucketsQueued += openclawFallback.bucketsQueued;
117
+ const openclawResult = opts.fromOpenclaw
118
+ ? await parseOpenclawSanitizedLedger({
119
+ trackerDir,
120
+ cursors,
121
+ queuePath,
122
+ })
123
+ : { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
144
124
 
145
125
  const claudeFiles = await listClaudeProjectFiles(claudeProjectsDir);
146
126
  let claudeResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
@@ -456,153 +436,65 @@ function parseArgs(argv) {
456
436
 
457
437
  module.exports = { cmdSync };
458
438
 
459
- function normalizeString(value) {
460
- if (typeof value !== "string") return null;
461
- const trimmed = value.trim();
462
- return trimmed.length > 0 ? trimmed : null;
463
- }
464
-
465
- function resolveOpenclawSignal({ home, env } = {}) {
466
- if (!env) return null;
467
-
468
- const agentId = normalizeString(env.VIBEUSAGE_OPENCLAW_AGENT_ID);
469
- const sessionId = normalizeString(env.VIBEUSAGE_OPENCLAW_PREV_SESSION_ID);
470
- if (!agentId || !sessionId) return null;
471
-
472
- const openclawHome =
473
- normalizeString(env.VIBEUSAGE_OPENCLAW_HOME) || path.join(home || os.homedir(), ".openclaw");
474
- const sessionFile = path.join(openclawHome, "agents", agentId, "sessions", `${sessionId}.jsonl`);
475
-
476
- const prevTotals = {
477
- totalTokens: normalizeNonNegativeInt(env.VIBEUSAGE_OPENCLAW_PREV_TOTAL_TOKENS),
478
- inputTokens: normalizeNonNegativeInt(env.VIBEUSAGE_OPENCLAW_PREV_INPUT_TOKENS),
479
- outputTokens: normalizeNonNegativeInt(env.VIBEUSAGE_OPENCLAW_PREV_OUTPUT_TOKENS),
480
- model: normalizeString(env.VIBEUSAGE_OPENCLAW_PREV_MODEL),
481
- updatedAt: normalizeIsoOrEpoch(env.VIBEUSAGE_OPENCLAW_PREV_UPDATED_AT),
482
- };
483
-
484
- return {
485
- agentId,
486
- sessionId,
487
- sessionKey: normalizeString(env.VIBEUSAGE_OPENCLAW_SESSION_KEY),
488
- openclawHome,
489
- sessionFile,
490
- prevTotals,
491
- };
492
- }
439
+ async function parseOpenclawSanitizedLedger({ trackerDir, cursors, queuePath }) {
440
+ const ledgerCursor =
441
+ cursors?.openclawLedger && typeof cursors.openclawLedger === "object"
442
+ ? cursors.openclawLedger
443
+ : {};
444
+ const offset = Math.max(0, Number(ledgerCursor.offset || 0));
445
+ const { events, endOffset } = await readOpenclawUsageLedger({ trackerDir, offset });
446
+
447
+ const hourlyState = normalizeHourlyState(cursors?.hourly);
448
+ const touchedBuckets = new Set();
449
+ let eventsAggregated = 0;
450
+
451
+ for (const event of events) {
452
+ if (!event || typeof event !== "object") continue;
453
+ const bucketStart = toUtcHalfHourStart(event.emittedAt);
454
+ if (!bucketStart) continue;
455
+
456
+ const model =
457
+ typeof event.model === "string" && event.model.trim() ? event.model.trim() : "unknown";
458
+ const source =
459
+ typeof event.source === "string" && event.source.trim() ? event.source.trim() : "openclaw";
460
+ const delta = {
461
+ input_tokens: Math.max(0, Number(event.inputTokens || 0)),
462
+ cached_input_tokens: Math.max(0, Number(event.cachedInputTokens || 0)),
463
+ output_tokens: Math.max(0, Number(event.outputTokens || 0)),
464
+ reasoning_output_tokens: Math.max(0, Number(event.reasoningOutputTokens || 0)),
465
+ total_tokens: Math.max(0, Number(event.totalTokens || 0)),
466
+ };
467
+
468
+ if (
469
+ delta.input_tokens === 0 &&
470
+ delta.cached_input_tokens === 0 &&
471
+ delta.output_tokens === 0 &&
472
+ delta.reasoning_output_tokens === 0 &&
473
+ delta.total_tokens === 0
474
+ ) {
475
+ continue;
476
+ }
493
477
 
494
- async function applyOpenclawTotalsFallback({
495
- trackerDir,
496
- signal,
497
- cursors,
498
- queuePath,
499
- projectQueuePath,
500
- }) {
501
- const totalTokens = Number(signal?.prevTotals?.totalTokens || 0);
502
- if (!trackerDir || !signal || totalTokens <= 0) {
503
- return { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
478
+ const bucket = getHourlyBucket(hourlyState, source, model, bucketStart);
479
+ addTotals(bucket.totals, delta);
480
+ touchedBuckets.add(bucketKey(source, model, bucketStart));
481
+ eventsAggregated += 1;
504
482
  }
505
483
 
506
- const sessionKey = `${signal.agentId}:${signal.sessionId}`;
507
- const statePath = path.join(trackerDir, "openclaw.fallback.state.json");
508
- const fallbackFilePath = path.join(trackerDir, "openclaw.fallback.jsonl");
509
- const state = (await readJson(statePath)) || { version: 1, sessions: {} };
510
- const sessions = state.sessions && typeof state.sessions === "object" ? state.sessions : {};
511
- const prev =
512
- sessions[sessionKey] && typeof sessions[sessionKey] === "object" ? sessions[sessionKey] : null;
513
-
514
- const current = {
515
- totalTokens: normalizeNonNegativeInt(signal?.prevTotals?.totalTokens) || 0,
516
- inputTokens: normalizeNonNegativeInt(signal?.prevTotals?.inputTokens) || 0,
517
- outputTokens: normalizeNonNegativeInt(signal?.prevTotals?.outputTokens) || 0,
518
- model: normalizeString(signal?.prevTotals?.model) || "unknown",
519
- updatedAt: normalizeIsoOrEpoch(signal?.prevTotals?.updatedAt) || new Date().toISOString(),
520
- seenAt: new Date().toISOString(),
484
+ const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
485
+ hourlyState.updatedAt = new Date().toISOString();
486
+ cursors.hourly = hourlyState;
487
+ cursors.openclawLedger = {
488
+ version: 1,
489
+ offset: endOffset,
490
+ updatedAt: new Date().toISOString(),
521
491
  };
522
492
 
523
- let deltaTotal = current.totalTokens;
524
- let deltaInput = current.inputTokens;
525
- let deltaOutput = current.outputTokens;
526
- if (prev) {
527
- deltaTotal = Math.max(
528
- 0,
529
- current.totalTokens - (normalizeNonNegativeInt(prev.totalTokens) || 0),
530
- );
531
- deltaInput = Math.max(
532
- 0,
533
- current.inputTokens - (normalizeNonNegativeInt(prev.inputTokens) || 0),
534
- );
535
- deltaOutput = Math.max(
536
- 0,
537
- current.outputTokens - (normalizeNonNegativeInt(prev.outputTokens) || 0),
538
- );
539
- }
540
-
541
- if (deltaTotal > 0 && deltaInput + deltaOutput === 0) {
542
- deltaInput = deltaTotal;
543
- }
544
-
545
- sessions[sessionKey] = current;
546
- state.version = 1;
547
- state.sessions = sessions;
548
-
549
- if (deltaTotal <= 0) {
550
- await writeJson(statePath, state);
551
- return { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
552
- }
553
-
554
- await ensureDir(path.dirname(fallbackFilePath));
555
- const syntheticMessage = {
556
- type: "message",
557
- timestamp: current.updatedAt,
558
- message: {
559
- role: "assistant",
560
- model: current.model,
561
- usage: {
562
- input: deltaInput,
563
- output: deltaOutput,
564
- cacheRead: 0,
565
- cacheWrite: 0,
566
- totalTokens: deltaTotal,
567
- },
568
- },
493
+ return {
494
+ filesProcessed: endOffset > offset ? 1 : 0,
495
+ eventsAggregated,
496
+ bucketsQueued,
569
497
  };
570
- await fs.appendFile(fallbackFilePath, `${JSON.stringify(syntheticMessage)}\n`, "utf8");
571
- await writeJson(statePath, state);
572
-
573
- return parseOpenclawIncremental({
574
- sessionFiles: [{ path: fallbackFilePath, source: "openclaw" }],
575
- cursors,
576
- queuePath,
577
- projectQueuePath,
578
- source: "openclaw",
579
- });
580
- }
581
-
582
- function normalizeNonNegativeInt(value) {
583
- const n = Number(value);
584
- if (!Number.isFinite(n) || n < 0) return null;
585
- return Math.floor(n);
586
- }
587
-
588
- function normalizeIsoOrEpoch(value) {
589
- if (typeof value === "string") {
590
- const trimmed = value.trim();
591
- if (trimmed.length > 0 && !Number.isNaN(Date.parse(trimmed))) return trimmed;
592
- const numeric = Number(trimmed);
593
- if (Number.isFinite(numeric) && numeric > 0) {
594
- const ms = numeric < 1e12 ? Math.floor(numeric * 1000) : Math.floor(numeric);
595
- const iso = new Date(ms).toISOString();
596
- if (!Number.isNaN(Date.parse(iso))) return iso;
597
- }
598
- }
599
-
600
- const n = Number(value);
601
- if (!Number.isFinite(n) || n <= 0) return null;
602
- const ms = n < 1e12 ? Math.floor(n * 1000) : Math.floor(n);
603
- const dt = new Date(ms);
604
- if (Number.isNaN(dt.getTime())) return null;
605
- return dt.toISOString();
606
498
  }
607
499
 
608
500
  async function safeStatSize(p) {
@@ -93,17 +93,6 @@ async function cmdUninstall(argv) {
93
93
  unreadableText: (result) =>
94
94
  `- OpenClaw session plugin: skipped (${result.detail || "openclaw config unreadable"})`,
95
95
  }),
96
- renderHookLine({
97
- exists: true,
98
- result: resultByName.get("openclaw-legacy"),
99
- missingText: "- OpenClaw hook (legacy): skipped (openclaw config not found)",
100
- removedText: (result) =>
101
- `- OpenClaw hook (legacy) removed: ${result.detail || result.openclawConfigPath || "unknown"}`,
102
- noChangeText: "- OpenClaw hook (legacy): no change",
103
- skippedText: "- OpenClaw hook (legacy): no change",
104
- unreadableText: (result) =>
105
- `- OpenClaw hook (legacy): skipped (${result.detail || "openclaw config unreadable"})`,
106
- }),
107
96
  opts.purge ? `- Purged: ${path.join(home, ".vibeusage")}` : "- Purge: skipped (use --purge)",
108
97
  "",
109
98
  ].join("\n"),
@@ -61,7 +61,6 @@ async function collectTrackerDiagnostics({
61
61
  const geminiProbe = probeByName.get("gemini");
62
62
  const opencodeProbe = probeByName.get("opencode");
63
63
  const openclawSessionProbe = probeByName.get("openclaw-session");
64
- const openclawLegacyProbe = probeByName.get("openclaw-legacy");
65
64
 
66
65
  const codexNotify = Array.isArray(codexProbe?.currentNotify)
67
66
  ? codexProbe.currentNotify.map((value) => redactValue(value, home))
@@ -156,14 +155,6 @@ async function collectTrackerDiagnostics({
156
155
  typeof openclawSessionProbe?.detail === "string"
157
156
  ? redactError(openclawSessionProbe.detail, home)
158
157
  : null,
159
- openclaw_hook_status: openclawLegacyProbe?.status || "unknown",
160
- openclaw_hook_configured: Boolean(openclawLegacyProbe?.configured),
161
- openclaw_hook_linked: Boolean(openclawLegacyProbe?.linked),
162
- openclaw_hook_enabled: Boolean(openclawLegacyProbe?.enabled),
163
- openclaw_hook_detail:
164
- typeof openclawLegacyProbe?.detail === "string"
165
- ? redactError(openclawLegacyProbe.detail, home)
166
- : null,
167
158
  },
168
159
  upload: {
169
160
  last_success_at: lastSuccessAt,
package/src/lib/doctor.js CHANGED
@@ -313,8 +313,7 @@ function buildDiagnosticsChecks(diagnostics) {
313
313
  notify.claude_hook_configured ||
314
314
  notify.gemini_hook_configured ||
315
315
  notify.opencode_plugin_configured ||
316
- notify.openclaw_session_plugin_configured ||
317
- notify.openclaw_hook_configured,
316
+ notify.openclaw_session_plugin_configured,
318
317
  );
319
318
 
320
319
  checks.push({
@@ -338,19 +337,6 @@ function buildDiagnosticsChecks(diagnostics) {
338
337
  });
339
338
  }
340
339
 
341
- if (notify.openclaw_hook_status === "unreadable") {
342
- checks.push({
343
- id: "notify.openclaw_hook",
344
- status: "warn",
345
- detail: "OpenClaw hook config unreadable",
346
- critical: false,
347
- meta: {
348
- status: notify.openclaw_hook_status,
349
- detail: notify.openclaw_hook_detail || null,
350
- },
351
- });
352
- }
353
-
354
340
  const uploadError = diagnostics?.upload?.last_error || null;
355
341
  checks.push({
356
342
  id: "upload.last_error",
@@ -8,7 +8,6 @@ const {
8
8
  buildGeminiHookCommand,
9
9
  } = require("../gemini-config");
10
10
  const { resolveOpencodeConfigDir } = require("../opencode-config");
11
- const { resolveOpenclawHookPaths } = require("../openclaw-hook");
12
11
  const { resolveOpenclawSessionPluginPaths } = require("../openclaw-session-plugin");
13
12
  const { resolveTrackerPaths } = require("../tracker-paths");
14
13
 
@@ -63,11 +62,6 @@ async function createIntegrationContext({
63
62
  trackerDir: resolvedTrackerPaths.trackerDir,
64
63
  env,
65
64
  }),
66
- openclawLegacy: resolveOpenclawHookPaths({
67
- home,
68
- trackerDir: resolvedTrackerPaths.trackerDir,
69
- env,
70
- }),
71
65
  };
72
66
  }
73
67
 
@@ -5,7 +5,6 @@ const claude = require("./claude");
5
5
  const gemini = require("./gemini");
6
6
  const opencode = require("./opencode");
7
7
  const openclawSession = require("./openclaw-session");
8
- const openclawLegacy = require("./openclaw-legacy");
9
8
 
10
9
  const INTEGRATIONS = [
11
10
  codex,
@@ -14,7 +13,6 @@ const INTEGRATIONS = [
14
13
  gemini,
15
14
  opencode,
16
15
  openclawSession,
17
- openclawLegacy,
18
16
  ];
19
17
 
20
18
  function listIntegrations() {