vibeusage 0.2.23 → 0.3.1

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 (143) hide show
  1. package/README.md +25 -20
  2. package/README.zh-CN.md +7 -2
  3. package/node_modules/@insforge/sdk/LICENSE +201 -201
  4. package/node_modules/@insforge/sdk/README.md +326 -259
  5. package/node_modules/@insforge/sdk/dist/index.d.mts +377 -182
  6. package/node_modules/@insforge/sdk/dist/index.d.ts +377 -182
  7. package/node_modules/@insforge/sdk/dist/index.js +1172 -677
  8. package/node_modules/@insforge/sdk/dist/index.js.map +1 -1
  9. package/node_modules/@insforge/sdk/dist/index.mjs +1171 -677
  10. package/node_modules/@insforge/sdk/dist/index.mjs.map +1 -1
  11. package/node_modules/@insforge/sdk/package.json +68 -68
  12. package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.d.ts +1120 -43
  13. package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.d.ts.map +1 -1
  14. package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.js +179 -5
  15. package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.js.map +1 -1
  16. package/node_modules/@insforge/shared-schemas/dist/ai.schema.d.ts +25 -25
  17. package/node_modules/@insforge/shared-schemas/dist/ai.schema.d.ts.map +1 -1
  18. package/node_modules/@insforge/shared-schemas/dist/ai.schema.js +2 -2
  19. package/node_modules/@insforge/shared-schemas/dist/ai.schema.js.map +1 -1
  20. package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.d.ts +197 -51
  21. package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.d.ts.map +1 -1
  22. package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.js +87 -23
  23. package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.js.map +1 -1
  24. package/node_modules/@insforge/shared-schemas/dist/auth.schema.d.ts +32 -3
  25. package/node_modules/@insforge/shared-schemas/dist/auth.schema.d.ts.map +1 -1
  26. package/node_modules/@insforge/shared-schemas/dist/auth.schema.js +21 -3
  27. package/node_modules/@insforge/shared-schemas/dist/auth.schema.js.map +1 -1
  28. package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.d.ts +380 -0
  29. package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.d.ts.map +1 -1
  30. package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js +74 -0
  31. package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js.map +1 -1
  32. package/node_modules/@insforge/shared-schemas/dist/database-api.schema.d.ts +13 -13
  33. package/node_modules/@insforge/shared-schemas/dist/database-api.schema.js +1 -1
  34. package/node_modules/@insforge/shared-schemas/dist/database-api.schema.js.map +1 -1
  35. package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.d.ts +735 -0
  36. package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.d.ts.map +1 -0
  37. package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.js +209 -0
  38. package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.js.map +1 -0
  39. package/node_modules/@insforge/shared-schemas/dist/deployments.schema.d.ts +37 -0
  40. package/node_modules/@insforge/shared-schemas/dist/deployments.schema.d.ts.map +1 -0
  41. package/node_modules/@insforge/shared-schemas/dist/deployments.schema.js +25 -0
  42. package/node_modules/@insforge/shared-schemas/dist/deployments.schema.js.map +1 -0
  43. package/node_modules/@insforge/shared-schemas/dist/docs.schema.d.ts +5 -1
  44. package/node_modules/@insforge/shared-schemas/dist/docs.schema.d.ts.map +1 -1
  45. package/node_modules/@insforge/shared-schemas/dist/docs.schema.js +34 -4
  46. package/node_modules/@insforge/shared-schemas/dist/docs.schema.js.map +1 -1
  47. package/node_modules/@insforge/shared-schemas/dist/email-api.schema.js +1 -1
  48. package/node_modules/@insforge/shared-schemas/dist/email-api.schema.js.map +1 -1
  49. package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.d.ts +186 -6
  50. package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.d.ts.map +1 -1
  51. package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.js +21 -2
  52. package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.js.map +1 -1
  53. package/node_modules/@insforge/shared-schemas/dist/functions.schema.d.ts +5 -5
  54. package/node_modules/@insforge/shared-schemas/dist/functions.schema.js +1 -1
  55. package/node_modules/@insforge/shared-schemas/dist/functions.schema.js.map +1 -1
  56. package/node_modules/@insforge/shared-schemas/dist/index.d.ts +24 -18
  57. package/node_modules/@insforge/shared-schemas/dist/index.d.ts.map +1 -1
  58. package/node_modules/@insforge/shared-schemas/dist/index.js +24 -18
  59. package/node_modules/@insforge/shared-schemas/dist/index.js.map +1 -1
  60. package/node_modules/@insforge/shared-schemas/dist/logs-api.schema.js +1 -1
  61. package/node_modules/@insforge/shared-schemas/dist/logs-api.schema.js.map +1 -1
  62. package/node_modules/@insforge/shared-schemas/dist/logs.schema.d.ts +43 -0
  63. package/node_modules/@insforge/shared-schemas/dist/logs.schema.d.ts.map +1 -1
  64. package/node_modules/@insforge/shared-schemas/dist/logs.schema.js +11 -0
  65. package/node_modules/@insforge/shared-schemas/dist/logs.schema.js.map +1 -1
  66. package/node_modules/@insforge/shared-schemas/dist/metadata.schema.d.ts +229 -172
  67. package/node_modules/@insforge/shared-schemas/dist/metadata.schema.d.ts.map +1 -1
  68. package/node_modules/@insforge/shared-schemas/dist/metadata.schema.js +27 -7
  69. package/node_modules/@insforge/shared-schemas/dist/metadata.schema.js.map +1 -1
  70. package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.d.ts +51 -0
  71. package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.d.ts.map +1 -0
  72. package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.js +31 -0
  73. package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.js.map +1 -0
  74. package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.d.ts +31 -0
  75. package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.d.ts.map +1 -0
  76. package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.js +12 -0
  77. package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.js.map +1 -0
  78. package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.d.ts +39 -20
  79. package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.d.ts.map +1 -1
  80. package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.js +5 -1
  81. package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.js.map +1 -1
  82. package/node_modules/@insforge/shared-schemas/dist/realtime.schema.d.ts +12 -4
  83. package/node_modules/@insforge/shared-schemas/dist/realtime.schema.d.ts.map +1 -1
  84. package/node_modules/@insforge/shared-schemas/dist/realtime.schema.js +6 -0
  85. package/node_modules/@insforge/shared-schemas/dist/realtime.schema.js.map +1 -1
  86. package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.d.ts +287 -0
  87. package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.d.ts.map +1 -0
  88. package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js +81 -0
  89. package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js.map +1 -0
  90. package/node_modules/@insforge/shared-schemas/dist/schedules.schema.d.ts +77 -0
  91. package/node_modules/@insforge/shared-schemas/dist/schedules.schema.d.ts.map +1 -0
  92. package/node_modules/@insforge/shared-schemas/dist/schedules.schema.js +36 -0
  93. package/node_modules/@insforge/shared-schemas/dist/schedules.schema.js.map +1 -0
  94. package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.d.ts +113 -0
  95. package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.d.ts.map +1 -0
  96. package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.js +31 -0
  97. package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.js.map +1 -0
  98. package/node_modules/@insforge/shared-schemas/dist/secrets.schema.d.ts +31 -0
  99. package/node_modules/@insforge/shared-schemas/dist/secrets.schema.d.ts.map +1 -0
  100. package/node_modules/@insforge/shared-schemas/dist/secrets.schema.js +13 -0
  101. package/node_modules/@insforge/shared-schemas/dist/secrets.schema.js.map +1 -0
  102. package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.d.ts +27 -2
  103. package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.d.ts.map +1 -1
  104. package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.js +9 -1
  105. package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.js.map +1 -1
  106. package/node_modules/@insforge/shared-schemas/dist/storage.schema.d.ts +17 -0
  107. package/node_modules/@insforge/shared-schemas/dist/storage.schema.d.ts.map +1 -1
  108. package/node_modules/@insforge/shared-schemas/dist/storage.schema.js +6 -0
  109. package/node_modules/@insforge/shared-schemas/dist/storage.schema.js.map +1 -1
  110. package/node_modules/@insforge/shared-schemas/package.json +2 -1
  111. package/package.json +5 -6
  112. package/src/cli.js +2 -2
  113. package/src/commands/init.js +20 -362
  114. package/src/commands/status.js +58 -51
  115. package/src/commands/sync.js +37 -25
  116. package/src/commands/uninstall.js +121 -104
  117. package/src/lib/claude-config.js +130 -35
  118. package/src/lib/diagnostics.js +88 -57
  119. package/src/lib/doctor.js +50 -0
  120. package/src/lib/insforge-client.js +13 -9
  121. package/src/lib/integrations/claude.js +106 -0
  122. package/src/lib/integrations/codex.js +88 -0
  123. package/src/lib/integrations/context.js +76 -0
  124. package/src/lib/integrations/every-code.js +88 -0
  125. package/src/lib/integrations/gemini.js +86 -0
  126. package/src/lib/integrations/index.js +85 -0
  127. package/src/lib/integrations/openclaw-legacy.js +123 -0
  128. package/src/lib/integrations/openclaw-session.js +132 -0
  129. package/src/lib/integrations/opencode.js +86 -0
  130. package/src/lib/integrations/utils.js +39 -0
  131. package/src/lib/opencode-sqlite.js +113 -0
  132. package/src/lib/opencode-usage-audit.js +3 -2
  133. package/src/lib/rollout.js +227 -1
  134. package/src/lib/runtime-config.js +7 -5
  135. package/src/lib/vibeusage-api.js +11 -7
  136. package/src/shared/copy-registry.cjs +142 -0
  137. package/src/shared/copy-registry.cjs.d.ts +33 -0
  138. package/src/shared/runtime-defaults.cjs +11 -0
  139. package/src/shared/runtime-defaults.cjs.d.ts +3 -0
  140. package/src/shared/vibeusage-function-contract.cjs +34 -0
  141. package/src/shared/vibeusage-function-contract.cjs.d.ts +4 -0
  142. package/src/commands/activate-if-needed.js +0 -41
  143. package/src/lib/activation-check.js +0 -341
@@ -3,26 +3,27 @@ const path = require("node:path");
3
3
  const fs = require("node:fs/promises");
4
4
 
5
5
  const { readJson } = require("./fs");
6
- const { readCodexNotify, readEveryCodeNotify } = require("./codex-config");
7
- const { isClaudeHookConfigured, buildClaudeHookCommand } = require("./claude-config");
8
- const {
9
- resolveGeminiConfigDir,
10
- resolveGeminiSettingsPath,
11
- buildGeminiHookCommand,
12
- isGeminiHookConfigured,
13
- } = require("./gemini-config");
14
- const { resolveOpencodeConfigDir, isOpencodePluginInstalled } = require("./opencode-config");
15
6
  const { normalizeState: normalizeUploadState } = require("./upload-throttle");
16
- const { probeOpenclawHookState } = require("./openclaw-hook");
17
- const { probeOpenclawSessionPluginState } = require("./openclaw-session-plugin");
18
- const { resolveTrackerPaths } = require("./tracker-paths");
7
+ const { createIntegrationContext, probeIntegrations } = require("./integrations");
19
8
 
20
9
  async function collectTrackerDiagnostics({
21
10
  home = os.homedir(),
22
11
  codexHome = process.env.CODEX_HOME || path.join(home, ".codex"),
23
12
  codeHome = process.env.CODE_HOME || path.join(home, ".code"),
24
13
  } = {}) {
25
- const { trackerDir, binDir } = await resolveTrackerPaths({ home });
14
+ const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
15
+ const opencodeHome = process.env.OPENCODE_HOME || path.join(xdgDataHome, "opencode");
16
+ const opencodeStorageDir = path.join(opencodeHome, "storage");
17
+ const opencodeDbPath = path.join(opencodeHome, "opencode.db");
18
+ const integrationContext = await createIntegrationContext({
19
+ home,
20
+ env: {
21
+ ...process.env,
22
+ CODEX_HOME: codexHome,
23
+ CODE_HOME: codeHome,
24
+ },
25
+ });
26
+ const trackerDir = integrationContext.trackerPaths.trackerDir;
26
27
  const configPath = path.join(trackerDir, "config.json");
27
28
  const queuePath = path.join(trackerDir, "queue.jsonl");
28
29
  const queueStatePath = path.join(trackerDir, "queue.state.json");
@@ -34,16 +35,17 @@ async function collectTrackerDiagnostics({
34
35
  const autoRetryPath = path.join(trackerDir, "auto.retry.json");
35
36
  const codexConfigPath = path.join(codexHome, "config.toml");
36
37
  const codeConfigPath = path.join(codeHome, "config.toml");
37
- const claudeConfigPath = path.join(home, ".claude", "settings.json");
38
- const geminiConfigDir = resolveGeminiConfigDir({ home, env: process.env });
39
- const geminiSettingsPath = resolveGeminiSettingsPath({ configDir: geminiConfigDir });
40
- const opencodeConfigDir = resolveOpencodeConfigDir({ home, env: process.env });
41
38
 
42
39
  const config = await readJson(configPath);
43
40
  const cursors = await readJson(cursorsPath);
44
41
  const queueState = (await readJson(queueStatePath)) || { offset: 0 };
45
42
  const uploadThrottle = normalizeUploadState(await readJson(uploadThrottlePath));
46
43
  const autoRetry = await readJson(autoRetryPath);
44
+ const probes = await probeIntegrations(integrationContext);
45
+ const probeByName = new Map(probes.map((probe) => [probe.name, probe]));
46
+ const opencodeSqliteCursor =
47
+ cursors?.opencodeSqlite && typeof cursors.opencodeSqlite === "object" ? cursors.opencodeSqlite : {};
48
+ const opencodeDbStat = await safeStat(opencodeDbPath);
47
49
 
48
50
  const queueSize = await safeStatSize(queuePath);
49
51
  const offsetBytes = Number(queueState.offset || 0);
@@ -53,33 +55,20 @@ async function collectTrackerDiagnostics({
53
55
  const lastOpenclawSync = (await safeReadText(openclawSignalPath))?.trim() || null;
54
56
  const lastNotifySpawn = parseEpochMsToIso((await safeReadText(throttlePath))?.trim() || null);
55
57
 
56
- const codexNotifyRaw = await readCodexNotify(codexConfigPath);
57
- const notifyConfigured = Array.isArray(codexNotifyRaw) && codexNotifyRaw.length > 0;
58
- const codexNotify = notifyConfigured ? codexNotifyRaw.map((v) => redactValue(v, home)) : null;
59
- const everyCodeNotifyRaw = await readEveryCodeNotify(codeConfigPath);
60
- const everyCodeConfigured = Array.isArray(everyCodeNotifyRaw) && everyCodeNotifyRaw.length > 0;
61
- const everyCodeNotify = everyCodeConfigured
62
- ? everyCodeNotifyRaw.map((v) => redactValue(v, home))
58
+ const codexProbe = probeByName.get("codex");
59
+ const everyCodeProbe = probeByName.get("every-code");
60
+ const claudeProbe = probeByName.get("claude");
61
+ const geminiProbe = probeByName.get("gemini");
62
+ const opencodeProbe = probeByName.get("opencode");
63
+ const openclawSessionProbe = probeByName.get("openclaw-session");
64
+ const openclawLegacyProbe = probeByName.get("openclaw-legacy");
65
+
66
+ const codexNotify = Array.isArray(codexProbe?.currentNotify)
67
+ ? codexProbe.currentNotify.map((value) => redactValue(value, home))
68
+ : null;
69
+ const everyCodeNotify = Array.isArray(everyCodeProbe?.currentNotify)
70
+ ? everyCodeProbe.currentNotify.map((value) => redactValue(value, home))
63
71
  : null;
64
- const claudeHookCommand = buildClaudeHookCommand(path.join(binDir, "notify.cjs"));
65
- const claudeHookConfigured = await isClaudeHookConfigured({
66
- settingsPath: claudeConfigPath,
67
- hookCommand: claudeHookCommand,
68
- });
69
- const geminiHookCommand = buildGeminiHookCommand(path.join(binDir, "notify.cjs"));
70
- const geminiHookConfigured = await isGeminiHookConfigured({
71
- settingsPath: geminiSettingsPath,
72
- hookCommand: geminiHookCommand,
73
- });
74
- const opencodePluginConfigured = await isOpencodePluginInstalled({
75
- configDir: opencodeConfigDir,
76
- });
77
- const openclawSessionPluginState = await probeOpenclawSessionPluginState({
78
- home,
79
- trackerDir,
80
- env: process.env,
81
- });
82
- const openclawHookState = await probeOpenclawHookState({ home, trackerDir, env: process.env });
83
72
 
84
73
  const lastSuccessAt = uploadThrottle.lastSuccessMs
85
74
  ? new Date(uploadThrottle.lastSuccessMs).toISOString()
@@ -101,9 +90,9 @@ async function collectTrackerDiagnostics({
101
90
  codex_config: redactValue(codexConfigPath, home),
102
91
  code_home: redactValue(codeHome, home),
103
92
  code_config: redactValue(codeConfigPath, home),
104
- claude_config: redactValue(claudeConfigPath, home),
105
- gemini_config: redactValue(geminiSettingsPath, home),
106
- opencode_config: redactValue(opencodeConfigDir, home),
93
+ claude_config: redactValue(integrationContext.claude.settingsPath, home),
94
+ gemini_config: redactValue(integrationContext.gemini.settingsPath, home),
95
+ opencode_config: redactValue(integrationContext.opencode.configDir, home),
107
96
  },
108
97
  config: {
109
98
  base_url: typeof config?.baseUrl === "string" ? config.baseUrl : null,
@@ -124,23 +113,57 @@ async function collectTrackerDiagnostics({
124
113
  pending_bytes: pendingBytes,
125
114
  updated_at: typeof queueState.updatedAt === "string" ? queueState.updatedAt : null,
126
115
  },
116
+ opencode: {
117
+ storage_dir: redactValue(opencodeStorageDir, home),
118
+ db_path: redactValue(opencodeDbPath, home),
119
+ sqlite_db_present: Boolean(opencodeDbStat?.isFile?.()),
120
+ sqlite_status:
121
+ typeof opencodeSqliteCursor.lastStatus === "string" && opencodeSqliteCursor.lastStatus.trim()
122
+ ? opencodeSqliteCursor.lastStatus.trim()
123
+ : "never_checked",
124
+ sqlite_last_checked_at:
125
+ typeof opencodeSqliteCursor.lastCheckedAt === "string"
126
+ ? opencodeSqliteCursor.lastCheckedAt
127
+ : null,
128
+ sqlite_cursor_updated_at:
129
+ typeof opencodeSqliteCursor.updatedAt === "string" ? opencodeSqliteCursor.updatedAt : null,
130
+ sqlite_error_code:
131
+ typeof opencodeSqliteCursor.lastErrorCode === "string"
132
+ ? opencodeSqliteCursor.lastErrorCode
133
+ : null,
134
+ },
127
135
  notify: {
128
136
  last_notify: lastNotify,
129
137
  last_openclaw_triggered_sync: lastOpenclawSync,
130
138
  last_notify_triggered_sync: lastNotifySpawn,
131
- codex_notify_configured: notifyConfigured,
139
+ codex_notify_status: codexProbe?.status || "unknown",
140
+ codex_notify_configured: Boolean(codexProbe?.configured),
132
141
  codex_notify: codexNotify,
133
- every_code_notify_configured: everyCodeConfigured,
142
+ every_code_notify_status: everyCodeProbe?.status || "unknown",
143
+ every_code_notify_configured: Boolean(everyCodeProbe?.configured),
134
144
  every_code_notify: everyCodeNotify,
135
- claude_hook_configured: claudeHookConfigured,
136
- gemini_hook_configured: geminiHookConfigured,
137
- opencode_plugin_configured: opencodePluginConfigured,
138
- openclaw_session_plugin_configured: Boolean(openclawSessionPluginState?.configured),
139
- openclaw_session_plugin_linked: Boolean(openclawSessionPluginState?.linked),
140
- openclaw_session_plugin_enabled: Boolean(openclawSessionPluginState?.enabled),
141
- openclaw_hook_configured: Boolean(openclawHookState?.configured),
142
- openclaw_hook_linked: Boolean(openclawHookState?.linked),
143
- openclaw_hook_enabled: Boolean(openclawHookState?.enabled),
145
+ claude_hook_status: claudeProbe?.status || "unknown",
146
+ claude_hook_configured: Boolean(claudeProbe?.configured),
147
+ gemini_hook_status: geminiProbe?.status || "unknown",
148
+ gemini_hook_configured: Boolean(geminiProbe?.configured),
149
+ opencode_plugin_status: opencodeProbe?.status || "unknown",
150
+ opencode_plugin_configured: Boolean(opencodeProbe?.configured),
151
+ openclaw_session_plugin_status: openclawSessionProbe?.status || "unknown",
152
+ openclaw_session_plugin_configured: Boolean(openclawSessionProbe?.configured),
153
+ openclaw_session_plugin_linked: Boolean(openclawSessionProbe?.linked),
154
+ openclaw_session_plugin_enabled: Boolean(openclawSessionProbe?.enabled),
155
+ openclaw_session_plugin_detail:
156
+ typeof openclawSessionProbe?.detail === "string"
157
+ ? redactError(openclawSessionProbe.detail, home)
158
+ : 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,
144
167
  },
145
168
  upload: {
146
169
  last_success_at: lastSuccessAt,
@@ -197,6 +220,14 @@ async function safeStatSize(p) {
197
220
  }
198
221
  }
199
222
 
223
+ async function safeStat(p) {
224
+ try {
225
+ return await fs.stat(p);
226
+ } catch (_e) {
227
+ return null;
228
+ }
229
+ }
230
+
200
231
  async function safeReadText(p) {
201
232
  try {
202
233
  return await fs.readFile(p, "utf8");
package/src/lib/doctor.js CHANGED
@@ -313,6 +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 ||
316
317
  notify.openclaw_hook_configured,
317
318
  );
318
319
 
@@ -324,6 +325,32 @@ function buildDiagnosticsChecks(diagnostics) {
324
325
  meta: { configured: notifyConfigured },
325
326
  });
326
327
 
328
+ if (notify.openclaw_session_plugin_status === "unreadable") {
329
+ checks.push({
330
+ id: "notify.openclaw_session_plugin",
331
+ status: "warn",
332
+ detail: "OpenClaw session plugin config unreadable",
333
+ critical: false,
334
+ meta: {
335
+ status: notify.openclaw_session_plugin_status,
336
+ detail: notify.openclaw_session_plugin_detail || null,
337
+ },
338
+ });
339
+ }
340
+
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
+
327
354
  const uploadError = diagnostics?.upload?.last_error || null;
328
355
  checks.push({
329
356
  id: "upload.last_error",
@@ -333,6 +360,29 @@ function buildDiagnosticsChecks(diagnostics) {
333
360
  meta: { last_error: uploadError ? uploadError.message || null : null },
334
361
  });
335
362
 
363
+ const opencode = diagnostics?.opencode || {};
364
+ const sqliteStatus =
365
+ typeof opencode.sqlite_status === "string" && opencode.sqlite_status.trim()
366
+ ? opencode.sqlite_status.trim()
367
+ : "never_checked";
368
+ let opencodeStatus = "warn";
369
+ if (sqliteStatus === "ok") opencodeStatus = "ok";
370
+ else if (sqliteStatus === "missing-sqlite3" || sqliteStatus === "query-failed") {
371
+ opencodeStatus = "fail";
372
+ }
373
+ checks.push({
374
+ id: "opencode.sqlite_support",
375
+ status: opencodeStatus,
376
+ detail: `OpenCode SQLite reader ${sqliteStatus}`,
377
+ critical: false,
378
+ meta: {
379
+ sqlite_status: sqliteStatus,
380
+ sqlite_db_present: Boolean(opencode.sqlite_db_present),
381
+ sqlite_last_checked_at: opencode.sqlite_last_checked_at || null,
382
+ sqlite_error_code: opencode.sqlite_error_code || null,
383
+ },
384
+ });
385
+
336
386
  return checks;
337
387
  }
338
388
 
@@ -1,13 +1,17 @@
1
1
  "use strict";
2
2
 
3
- function loadInsforgeSdk() {
4
- try {
5
- return require("@insforge/sdk");
6
- } catch (err) {
7
- const wrapped = new Error("Missing dependency @insforge/sdk. Please reinstall vibeusage.");
8
- wrapped.cause = err;
9
- throw wrapped;
3
+ let sdkModulePromise = null;
4
+
5
+ async function loadInsforgeSdk() {
6
+ if (!sdkModulePromise) {
7
+ sdkModulePromise = import("@insforge/sdk").catch((err) => {
8
+ sdkModulePromise = null;
9
+ const wrapped = new Error("Missing dependency @insforge/sdk. Please reinstall vibeusage.");
10
+ wrapped.cause = err;
11
+ throw wrapped;
12
+ });
10
13
  }
14
+ return sdkModulePromise;
11
15
  }
12
16
 
13
17
  function getAnonKey({ env = process.env } = {}) {
@@ -45,9 +49,9 @@ function createTimeoutFetch(baseFetch) {
45
49
  };
46
50
  }
47
51
 
48
- function createInsforgeClient({ baseUrl, accessToken } = {}) {
52
+ async function createInsforgeClient({ baseUrl, accessToken } = {}) {
49
53
  if (!baseUrl) throw new Error("Missing baseUrl");
50
- const { createClient } = loadInsforgeSdk();
54
+ const { createClient } = await loadInsforgeSdk();
51
55
  const anonKey = getAnonKey();
52
56
  return createClient({
53
57
  baseUrl,
@@ -0,0 +1,106 @@
1
+ const { probeClaudeHook, upsertClaudeHook, removeClaudeHook } = require("../claude-config");
2
+ const { isDir, isFile } = require("./utils");
3
+
4
+ module.exports = {
5
+ name: "claude",
6
+ summaryLabel: "Claude",
7
+ statusLabel: "Claude hooks",
8
+ async probe(ctx) {
9
+ const hasConfigDir = await isDir(ctx.claude.configDir);
10
+ if (!hasConfigDir) {
11
+ return baseProbe(this, { status: "not_installed", detail: "Config not found" });
12
+ }
13
+
14
+ const hasSettings = await isFile(ctx.claude.settingsPath);
15
+ if (!hasSettings) {
16
+ return baseProbe(this, { status: "drifted", detail: "Run vibeusage init to install hooks" });
17
+ }
18
+
19
+ const hookState = await probeClaudeHook({
20
+ settingsPath: ctx.claude.settingsPath,
21
+ hookCommand: ctx.claude.hookCommand,
22
+ });
23
+
24
+ if (hookState.configured) {
25
+ return baseProbe(this, {
26
+ status: "ready",
27
+ detail: "Hooks installed",
28
+ configured: true,
29
+ hookState,
30
+ });
31
+ }
32
+
33
+ const sessionEndPresent = hookState.eventStates?.SessionEnd === true;
34
+ const stopPresent = hookState.eventStates?.Stop === true;
35
+ const status = hookState.anyPresent && sessionEndPresent && !stopPresent
36
+ ? "unsupported_legacy"
37
+ : "drifted";
38
+ return baseProbe(this, {
39
+ status,
40
+ detail:
41
+ status === "unsupported_legacy"
42
+ ? "Legacy hook config detected; run vibeusage init"
43
+ : "Run vibeusage init to reconcile hooks",
44
+ hookState,
45
+ });
46
+ },
47
+ async install(ctx) {
48
+ if (!(await isDir(ctx.claude.configDir))) {
49
+ return action(this, "skipped", false, "Config not found");
50
+ }
51
+ const result = await upsertClaudeHook({
52
+ settingsPath: ctx.claude.settingsPath,
53
+ hookCommand: ctx.claude.hookCommand,
54
+ });
55
+ return action(
56
+ this,
57
+ result.changed ? "installed" : "set",
58
+ Boolean(result.changed),
59
+ result.changed ? "Hooks installed" : "Hooks already installed",
60
+ );
61
+ },
62
+ async uninstall(ctx) {
63
+ if (!(await isFile(ctx.claude.settingsPath))) {
64
+ return action(this, "skipped", false, "settings.json not found");
65
+ }
66
+ const result = await removeClaudeHook({
67
+ settingsPath: ctx.claude.settingsPath,
68
+ hookCommand: ctx.claude.hookCommand,
69
+ });
70
+ if (result.removed) {
71
+ return action(this, "removed", true, ctx.claude.settingsPath);
72
+ }
73
+ if (result.skippedReason === "hook-missing") {
74
+ return action(this, "unchanged", false, "no change", {
75
+ skippedReason: result.skippedReason,
76
+ });
77
+ }
78
+ return action(this, "skipped", false, "settings.json not found");
79
+ },
80
+ renderStatusValue(probe) {
81
+ if (probe.status === "ready") return "set";
82
+ if (probe.status === "not_installed") return "unset";
83
+ return probe.status;
84
+ },
85
+ };
86
+
87
+ function baseProbe(descriptor, values) {
88
+ return {
89
+ name: descriptor.name,
90
+ summaryLabel: descriptor.summaryLabel,
91
+ statusLabel: descriptor.statusLabel,
92
+ configured: false,
93
+ ...values,
94
+ };
95
+ }
96
+
97
+ function action(descriptor, status, changed, detail, extras = {}) {
98
+ return {
99
+ name: descriptor.name,
100
+ label: descriptor.summaryLabel,
101
+ status,
102
+ changed,
103
+ detail,
104
+ ...extras,
105
+ };
106
+ }
@@ -0,0 +1,88 @@
1
+ const {
2
+ readCodexNotify,
3
+ upsertCodexNotify,
4
+ restoreCodexNotify,
5
+ } = require("../codex-config");
6
+ const { arraysEqual, isFile } = require("./utils");
7
+
8
+ module.exports = {
9
+ name: "codex",
10
+ summaryLabel: "Codex CLI",
11
+ statusLabel: "Codex notify",
12
+ async probe(ctx) {
13
+ const installed = await isFile(ctx.codex.configPath);
14
+ if (!installed) {
15
+ return baseProbe(this, { status: "not_installed", detail: "Config not found" });
16
+ }
17
+
18
+ const currentNotify = await readCodexNotify(ctx.codex.configPath);
19
+ const ready = arraysEqual(currentNotify, ctx.codex.notifyCmd);
20
+ return baseProbe(this, {
21
+ status: ready ? "ready" : "drifted",
22
+ detail: ready ? "Config already set" : "Run vibeusage init to reconcile notify",
23
+ configured: ready,
24
+ currentNotify,
25
+ });
26
+ },
27
+ async install(ctx) {
28
+ if (!(await isFile(ctx.codex.configPath))) {
29
+ return action(this, "skipped", false, "Config not found");
30
+ }
31
+ const result = await upsertCodexNotify({
32
+ codexConfigPath: ctx.codex.configPath,
33
+ notifyCmd: ctx.codex.notifyCmd,
34
+ notifyOriginalPath: ctx.codex.notifyOriginalPath,
35
+ });
36
+ return action(
37
+ this,
38
+ result.changed ? "updated" : "set",
39
+ Boolean(result.changed),
40
+ result.changed ? "Updated config" : "Config already set",
41
+ );
42
+ },
43
+ async uninstall(ctx) {
44
+ if (!(await isFile(ctx.codex.configPath))) {
45
+ return action(this, "skipped", false, "config.toml not found");
46
+ }
47
+ const result = await restoreCodexNotify({
48
+ codexConfigPath: ctx.codex.configPath,
49
+ notifyOriginalPath: ctx.codex.notifyOriginalPath,
50
+ notifyCmd: ctx.codex.notifyCmd,
51
+ });
52
+ if (result.restored) {
53
+ return action(this, "restored", true, ctx.codex.configPath);
54
+ }
55
+ if (result.skippedReason === "no-backup-not-installed") {
56
+ return action(this, "skipped", false, "no backup; not installed", {
57
+ skippedReason: result.skippedReason,
58
+ });
59
+ }
60
+ return action(this, "unchanged", false, "no change");
61
+ },
62
+ renderStatusValue(probe) {
63
+ if (probe.status === "ready") return JSON.stringify(probe.currentNotify || []);
64
+ if (probe.status === "not_installed") return "unset";
65
+ return probe.status;
66
+ },
67
+ };
68
+
69
+ function baseProbe(descriptor, values) {
70
+ return {
71
+ name: descriptor.name,
72
+ summaryLabel: descriptor.summaryLabel,
73
+ statusLabel: descriptor.statusLabel,
74
+ configured: false,
75
+ ...values,
76
+ };
77
+ }
78
+
79
+ function action(descriptor, status, changed, detail, extras = {}) {
80
+ return {
81
+ name: descriptor.name,
82
+ label: descriptor.summaryLabel,
83
+ status,
84
+ changed,
85
+ detail,
86
+ ...extras,
87
+ };
88
+ }
@@ -0,0 +1,76 @@
1
+ const os = require("node:os");
2
+ const path = require("node:path");
3
+
4
+ const { buildClaudeHookCommand } = require("../claude-config");
5
+ const {
6
+ resolveGeminiConfigDir,
7
+ resolveGeminiSettingsPath,
8
+ buildGeminiHookCommand,
9
+ } = require("../gemini-config");
10
+ const { resolveOpencodeConfigDir } = require("../opencode-config");
11
+ const { resolveOpenclawHookPaths } = require("../openclaw-hook");
12
+ const { resolveOpenclawSessionPluginPaths } = require("../openclaw-session-plugin");
13
+ const { resolveTrackerPaths } = require("../tracker-paths");
14
+
15
+ async function createIntegrationContext({
16
+ home = os.homedir(),
17
+ env = process.env,
18
+ trackerPaths = null,
19
+ notifyPath = null,
20
+ } = {}) {
21
+ const resolvedTrackerPaths = trackerPaths || (await resolveTrackerPaths({ home }));
22
+ const resolvedNotifyPath = notifyPath || path.join(resolvedTrackerPaths.binDir, "notify.cjs");
23
+
24
+ const codexHome = env.CODEX_HOME || path.join(home, ".codex");
25
+ const codeHome = env.CODE_HOME || path.join(home, ".code");
26
+ const claudeDir = path.join(home, ".claude");
27
+ const geminiConfigDir = resolveGeminiConfigDir({ home, env });
28
+ const opencodeConfigDir = resolveOpencodeConfigDir({ home, env });
29
+
30
+ return {
31
+ home,
32
+ env,
33
+ trackerPaths: resolvedTrackerPaths,
34
+ notifyPath: resolvedNotifyPath,
35
+ codex: {
36
+ configPath: path.join(codexHome, "config.toml"),
37
+ notifyCmd: ["/usr/bin/env", "node", resolvedNotifyPath],
38
+ notifyOriginalPath: path.join(resolvedTrackerPaths.trackerDir, "codex_notify_original.json"),
39
+ },
40
+ everyCode: {
41
+ configPath: path.join(codeHome, "config.toml"),
42
+ notifyCmd: ["/usr/bin/env", "node", resolvedNotifyPath, "--source=every-code"],
43
+ notifyOriginalPath: path.join(
44
+ resolvedTrackerPaths.trackerDir,
45
+ "code_notify_original.json",
46
+ ),
47
+ },
48
+ claude: {
49
+ configDir: claudeDir,
50
+ settingsPath: path.join(claudeDir, "settings.json"),
51
+ hookCommand: buildClaudeHookCommand(resolvedNotifyPath),
52
+ },
53
+ gemini: {
54
+ configDir: geminiConfigDir,
55
+ settingsPath: resolveGeminiSettingsPath({ configDir: geminiConfigDir }),
56
+ hookCommand: buildGeminiHookCommand(resolvedNotifyPath),
57
+ },
58
+ opencode: {
59
+ configDir: opencodeConfigDir,
60
+ },
61
+ openclawSession: resolveOpenclawSessionPluginPaths({
62
+ home,
63
+ trackerDir: resolvedTrackerPaths.trackerDir,
64
+ env,
65
+ }),
66
+ openclawLegacy: resolveOpenclawHookPaths({
67
+ home,
68
+ trackerDir: resolvedTrackerPaths.trackerDir,
69
+ env,
70
+ }),
71
+ };
72
+ }
73
+
74
+ module.exports = {
75
+ createIntegrationContext,
76
+ };
@@ -0,0 +1,88 @@
1
+ const {
2
+ readEveryCodeNotify,
3
+ upsertEveryCodeNotify,
4
+ restoreEveryCodeNotify,
5
+ } = require("../codex-config");
6
+ const { arraysEqual, isFile } = require("./utils");
7
+
8
+ module.exports = {
9
+ name: "every-code",
10
+ summaryLabel: "Every Code",
11
+ statusLabel: "Every Code notify",
12
+ async probe(ctx) {
13
+ const installed = await isFile(ctx.everyCode.configPath);
14
+ if (!installed) {
15
+ return baseProbe(this, { status: "not_installed", detail: "Config not found" });
16
+ }
17
+
18
+ const currentNotify = await readEveryCodeNotify(ctx.everyCode.configPath);
19
+ const ready = arraysEqual(currentNotify, ctx.everyCode.notifyCmd);
20
+ return baseProbe(this, {
21
+ status: ready ? "ready" : "drifted",
22
+ detail: ready ? "Config already set" : "Run vibeusage init to reconcile notify",
23
+ configured: ready,
24
+ currentNotify,
25
+ });
26
+ },
27
+ async install(ctx) {
28
+ if (!(await isFile(ctx.everyCode.configPath))) {
29
+ return action(this, "skipped", false, "Config not found");
30
+ }
31
+ const result = await upsertEveryCodeNotify({
32
+ codeConfigPath: ctx.everyCode.configPath,
33
+ notifyCmd: ctx.everyCode.notifyCmd,
34
+ notifyOriginalPath: ctx.everyCode.notifyOriginalPath,
35
+ });
36
+ return action(
37
+ this,
38
+ result.changed ? "updated" : "set",
39
+ Boolean(result.changed),
40
+ result.changed ? "Updated config" : "Config already set",
41
+ );
42
+ },
43
+ async uninstall(ctx) {
44
+ if (!(await isFile(ctx.everyCode.configPath))) {
45
+ return action(this, "skipped", false, "config.toml not found");
46
+ }
47
+ const result = await restoreEveryCodeNotify({
48
+ codeConfigPath: ctx.everyCode.configPath,
49
+ notifyOriginalPath: ctx.everyCode.notifyOriginalPath,
50
+ notifyCmd: ctx.everyCode.notifyCmd,
51
+ });
52
+ if (result.restored) {
53
+ return action(this, "restored", true, ctx.everyCode.configPath);
54
+ }
55
+ if (result.skippedReason === "no-backup-not-installed") {
56
+ return action(this, "skipped", false, "no backup; not installed", {
57
+ skippedReason: result.skippedReason,
58
+ });
59
+ }
60
+ return action(this, "unchanged", false, "no change");
61
+ },
62
+ renderStatusValue(probe) {
63
+ if (probe.status === "ready") return JSON.stringify(probe.currentNotify || []);
64
+ if (probe.status === "not_installed") return "unset";
65
+ return probe.status;
66
+ },
67
+ };
68
+
69
+ function baseProbe(descriptor, values) {
70
+ return {
71
+ name: descriptor.name,
72
+ summaryLabel: descriptor.summaryLabel,
73
+ statusLabel: descriptor.statusLabel,
74
+ configured: false,
75
+ ...values,
76
+ };
77
+ }
78
+
79
+ function action(descriptor, status, changed, detail, extras = {}) {
80
+ return {
81
+ name: descriptor.name,
82
+ label: descriptor.summaryLabel,
83
+ status,
84
+ changed,
85
+ detail,
86
+ ...extras,
87
+ };
88
+ }