vibeusage 0.3.0 → 0.3.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 (127) hide show
  1. package/README.md +19 -11
  2. package/README.zh-CN.md +10 -8
  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 +2 -2
  112. package/src/commands/status.js +22 -5
  113. package/src/commands/sync.js +100 -197
  114. package/src/commands/uninstall.js +0 -11
  115. package/src/lib/diagnostics.js +34 -9
  116. package/src/lib/doctor.js +24 -15
  117. package/src/lib/insforge-client.js +13 -9
  118. package/src/lib/integrations/context.js +0 -6
  119. package/src/lib/integrations/index.js +0 -2
  120. package/src/lib/openclaw-session-plugin.js +48 -138
  121. package/src/lib/openclaw-usage-ledger.js +237 -0
  122. package/src/lib/opencode-sqlite.js +113 -0
  123. package/src/lib/opencode-usage-audit.js +3 -2
  124. package/src/lib/rollout.js +229 -153
  125. package/src/lib/vibeusage-api.js +2 -2
  126. package/src/lib/integrations/openclaw-legacy.js +0 -123
  127. package/src/lib/openclaw-hook.js +0 -420
@@ -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"),
@@ -11,6 +11,10 @@ async function collectTrackerDiagnostics({
11
11
  codexHome = process.env.CODEX_HOME || path.join(home, ".codex"),
12
12
  codeHome = process.env.CODE_HOME || path.join(home, ".code"),
13
13
  } = {}) {
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");
14
18
  const integrationContext = await createIntegrationContext({
15
19
  home,
16
20
  env: {
@@ -39,6 +43,9 @@ async function collectTrackerDiagnostics({
39
43
  const autoRetry = await readJson(autoRetryPath);
40
44
  const probes = await probeIntegrations(integrationContext);
41
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);
42
49
 
43
50
  const queueSize = await safeStatSize(queuePath);
44
51
  const offsetBytes = Number(queueState.offset || 0);
@@ -54,7 +61,6 @@ async function collectTrackerDiagnostics({
54
61
  const geminiProbe = probeByName.get("gemini");
55
62
  const opencodeProbe = probeByName.get("opencode");
56
63
  const openclawSessionProbe = probeByName.get("openclaw-session");
57
- const openclawLegacyProbe = probeByName.get("openclaw-legacy");
58
64
 
59
65
  const codexNotify = Array.isArray(codexProbe?.currentNotify)
60
66
  ? codexProbe.currentNotify.map((value) => redactValue(value, home))
@@ -106,6 +112,25 @@ async function collectTrackerDiagnostics({
106
112
  pending_bytes: pendingBytes,
107
113
  updated_at: typeof queueState.updatedAt === "string" ? queueState.updatedAt : null,
108
114
  },
115
+ opencode: {
116
+ storage_dir: redactValue(opencodeStorageDir, home),
117
+ db_path: redactValue(opencodeDbPath, home),
118
+ sqlite_db_present: Boolean(opencodeDbStat?.isFile?.()),
119
+ sqlite_status:
120
+ typeof opencodeSqliteCursor.lastStatus === "string" && opencodeSqliteCursor.lastStatus.trim()
121
+ ? opencodeSqliteCursor.lastStatus.trim()
122
+ : "never_checked",
123
+ sqlite_last_checked_at:
124
+ typeof opencodeSqliteCursor.lastCheckedAt === "string"
125
+ ? opencodeSqliteCursor.lastCheckedAt
126
+ : null,
127
+ sqlite_cursor_updated_at:
128
+ typeof opencodeSqliteCursor.updatedAt === "string" ? opencodeSqliteCursor.updatedAt : null,
129
+ sqlite_error_code:
130
+ typeof opencodeSqliteCursor.lastErrorCode === "string"
131
+ ? opencodeSqliteCursor.lastErrorCode
132
+ : null,
133
+ },
109
134
  notify: {
110
135
  last_notify: lastNotify,
111
136
  last_openclaw_triggered_sync: lastOpenclawSync,
@@ -130,14 +155,6 @@ async function collectTrackerDiagnostics({
130
155
  typeof openclawSessionProbe?.detail === "string"
131
156
  ? redactError(openclawSessionProbe.detail, home)
132
157
  : null,
133
- openclaw_hook_status: openclawLegacyProbe?.status || "unknown",
134
- openclaw_hook_configured: Boolean(openclawLegacyProbe?.configured),
135
- openclaw_hook_linked: Boolean(openclawLegacyProbe?.linked),
136
- openclaw_hook_enabled: Boolean(openclawLegacyProbe?.enabled),
137
- openclaw_hook_detail:
138
- typeof openclawLegacyProbe?.detail === "string"
139
- ? redactError(openclawLegacyProbe.detail, home)
140
- : null,
141
158
  },
142
159
  upload: {
143
160
  last_success_at: lastSuccessAt,
@@ -194,6 +211,14 @@ async function safeStatSize(p) {
194
211
  }
195
212
  }
196
213
 
214
+ async function safeStat(p) {
215
+ try {
216
+ return await fs.stat(p);
217
+ } catch (_e) {
218
+ return null;
219
+ }
220
+ }
221
+
197
222
  async function safeReadText(p) {
198
223
  try {
199
224
  return await fs.readFile(p, "utf8");
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",
@@ -360,6 +346,29 @@ function buildDiagnosticsChecks(diagnostics) {
360
346
  meta: { last_error: uploadError ? uploadError.message || null : null },
361
347
  });
362
348
 
349
+ const opencode = diagnostics?.opencode || {};
350
+ const sqliteStatus =
351
+ typeof opencode.sqlite_status === "string" && opencode.sqlite_status.trim()
352
+ ? opencode.sqlite_status.trim()
353
+ : "never_checked";
354
+ let opencodeStatus = "warn";
355
+ if (sqliteStatus === "ok") opencodeStatus = "ok";
356
+ else if (sqliteStatus === "missing-sqlite3" || sqliteStatus === "query-failed") {
357
+ opencodeStatus = "fail";
358
+ }
359
+ checks.push({
360
+ id: "opencode.sqlite_support",
361
+ status: opencodeStatus,
362
+ detail: `OpenCode SQLite reader ${sqliteStatus}`,
363
+ critical: false,
364
+ meta: {
365
+ sqlite_status: sqliteStatus,
366
+ sqlite_db_present: Boolean(opencode.sqlite_db_present),
367
+ sqlite_last_checked_at: opencode.sqlite_last_checked_at || null,
368
+ sqlite_error_code: opencode.sqlite_error_code || null,
369
+ },
370
+ });
371
+
363
372
  return checks;
364
373
  }
365
374
 
@@ -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,
@@ -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() {
@@ -3,6 +3,7 @@ const path = require("node:path");
3
3
  const fs = require("node:fs/promises");
4
4
  const fssync = require("node:fs");
5
5
  const cp = require("node:child_process");
6
+ const { pathToFileURL } = require("node:url");
6
7
 
7
8
  const OPENCLAW_SESSION_PLUGIN_ID = "openclaw-session-sync";
8
9
  const OPENCLAW_SESSION_PLUGIN_DIRNAME = "openclaw-plugin";
@@ -103,7 +104,6 @@ async function ensureOpenclawSessionPluginFiles({
103
104
  buildSessionPluginIndex({
104
105
  trackerDir,
105
106
  packageName,
106
- openclawHome: openclawHome || path.join(os.homedir(), ".openclaw"),
107
107
  }),
108
108
  "utf8",
109
109
  );
@@ -337,144 +337,58 @@ function buildSessionPluginMeta() {
337
337
  )}\n`;
338
338
  }
339
339
 
340
- function buildSessionPluginIndex({ trackerDir, packageName = "vibeusage", openclawHome }) {
341
- const trackerBinPath = path.join(trackerDir, "app", "bin", "tracker.js");
342
- const fallbackPkg = packageName || "vibeusage";
343
- const safeOpenclawHome = openclawHome || path.join(os.homedir(), ".openclaw");
340
+ function buildSessionPluginIndex({ trackerDir }) {
341
+ const ledgerModuleUrl = pathToFileURL(
342
+ path.join(trackerDir, "app", "src", "lib", "openclaw-usage-ledger.js"),
343
+ ).href;
344
344
 
345
345
  return (
346
- `import fs from 'node:fs';\n` +
347
- `import path from 'node:path';\n` +
348
- `import cp from 'node:child_process';\n` +
346
+ `import { appendOpenclawUsageEvent } from ${JSON.stringify(ledgerModuleUrl)};\n` +
349
347
  `\n` +
350
348
  `const trackerDir = ${JSON.stringify(trackerDir)};\n` +
351
- `const trackerBinPath = ${JSON.stringify(trackerBinPath)};\n` +
352
- `const fallbackPkg = ${JSON.stringify(fallbackPkg)};\n` +
353
- `const openclawHome = ${JSON.stringify(safeOpenclawHome)};\n` +
354
- `const depsMarkerPath = path.join(trackerDir, 'app', 'node_modules', '@insforge', 'sdk', 'package.json');\n` +
355
- `const triggerStatePath = path.join(trackerDir, 'openclaw.session-sync.trigger-state.json');\n` +
356
- `const SESSION_TRIGGER_THROTTLE_MS = 15_000;\n` +
357
349
  `\n` +
358
350
  `export default function register(api) {\n` +
359
- ` api.on('agent_end', async (_event, ctx) => {\n` +
351
+ ` api.on('llm_output', async (event, ctx) => {\n` +
360
352
  ` try {\n` +
361
- ` const sessionKey = normalize(ctx && ctx.sessionKey);\n` +
362
- ` if (!sessionKey) return;\n` +
363
- `\n` +
364
- ` const agentId = normalize(ctx && ctx.agentId) || parseAgentId(sessionKey);\n` +
365
- ` if (!agentId) return;\n` +
366
- `\n` +
367
- ` const sessionInfo = resolveSessionInfo(agentId, sessionKey);\n` +
368
- ` const sessionId = normalize(sessionInfo && sessionInfo.sessionId);\n` +
369
- ` if (!sessionId) return;\n` +
370
- `\n` +
371
- ` if (!allowTrigger('agent_end', agentId, sessionId)) return;\n` +
372
- `\n` +
373
- ` spawnSync({\n` +
374
- ` args: ['sync', '--auto', '--from-openclaw'],\n` +
375
- ` env: buildSessionEnv({\n` +
376
- ` agentId,\n` +
377
- ` sessionId,\n` +
378
- ` sessionKey,\n` +
379
- ` sessionEntry: sessionInfo && sessionInfo.entry\n` +
380
- ` })\n` +
381
- ` });\n` +
382
- ` } catch (_) {}\n` +
383
- ` });\n` +
384
- `\n` +
385
- ` api.on('gateway_start', async () => {\n` +
386
- ` try {\n` +
387
- ` if (!allowTrigger('gateway_start', 'gateway', 'startup')) return;\n` +
388
- ` spawnSync({ args: ['sync', '--auto'] });\n` +
389
- ` } catch (_) {}\n` +
390
- ` });\n` +
391
- `\n` +
392
- ` api.on('gateway_stop', async () => {\n` +
393
- ` try {\n` +
394
- ` if (!allowTrigger('gateway_stop', 'gateway', 'stop')) return;\n` +
395
- ` spawnSync({ args: ['sync', '--auto'] });\n` +
353
+ ` const payload = buildPayload(event, ctx);\n` +
354
+ ` if (!payload) return;\n` +
355
+ ` await appendOpenclawUsageEvent({ trackerDir, payload });\n` +
396
356
  ` } catch (_) {}\n` +
397
357
  ` });\n` +
398
358
  `}\n` +
399
359
  `\n` +
400
- `function spawnSync({ args, env = {} }) {\n` +
401
- ` const hasLocalRuntime = fs.existsSync(trackerBinPath);\n` +
402
- ` const hasLocalDeps = fs.existsSync(depsMarkerPath);\n` +
403
- ` const argv = Array.isArray(args) && args.length > 0 ? args : ['sync', '--auto'];\n` +
404
- ` const cmd = hasLocalRuntime && hasLocalDeps\n` +
405
- ` ? [process.execPath, trackerBinPath, ...argv]\n` +
406
- ` : ['npx', '--yes', fallbackPkg, ...argv];\n` +
407
- ` const child = cp.spawn(cmd[0], cmd.slice(1), {\n` +
408
- ` detached: true,\n` +
409
- ` stdio: 'ignore',\n` +
410
- ` env: { ...process.env, ...env }\n` +
411
- ` });\n` +
412
- ` child.unref();\n` +
413
- `}\n` +
414
- `\n` +
415
- `function buildSessionEnv({ agentId, sessionId, sessionKey, sessionEntry }) {\n` +
416
- ` const out = {\n` +
417
- ` VIBEUSAGE_OPENCLAW_AGENT_ID: agentId,\n` +
418
- ` VIBEUSAGE_OPENCLAW_PREV_SESSION_ID: sessionId,\n` +
419
- ` VIBEUSAGE_OPENCLAW_HOME: openclawHome\n` +
420
- ` };\n` +
421
- ` const key = normalize(sessionKey);\n` +
422
- ` if (key) out.VIBEUSAGE_OPENCLAW_SESSION_KEY = key;\n` +
423
- ` const prevTotalTokens = toNonNegativeInt(sessionEntry && sessionEntry.totalTokens);\n` +
424
- ` const prevInputTokens = toNonNegativeInt(sessionEntry && sessionEntry.inputTokens);\n` +
425
- ` const prevOutputTokens = toNonNegativeInt(sessionEntry && sessionEntry.outputTokens);\n` +
426
- ` const prevModel = normalize(sessionEntry && sessionEntry.model);\n` +
427
- ` const prevUpdatedAt = toIso(sessionEntry && sessionEntry.updatedAt);\n` +
428
- ` if (prevTotalTokens != null) out.VIBEUSAGE_OPENCLAW_PREV_TOTAL_TOKENS = String(prevTotalTokens);\n` +
429
- ` if (prevInputTokens != null) out.VIBEUSAGE_OPENCLAW_PREV_INPUT_TOKENS = String(prevInputTokens);\n` +
430
- ` if (prevOutputTokens != null) out.VIBEUSAGE_OPENCLAW_PREV_OUTPUT_TOKENS = String(prevOutputTokens);\n` +
431
- ` if (prevModel) out.VIBEUSAGE_OPENCLAW_PREV_MODEL = prevModel;\n` +
432
- ` if (prevUpdatedAt) out.VIBEUSAGE_OPENCLAW_PREV_UPDATED_AT = prevUpdatedAt;\n` +
433
- ` return out;\n` +
434
- `}\n` +
360
+ `function buildPayload(event, ctx) {\n` +
361
+ ` const usage = normalizeUsage(event);\n` +
362
+ ` if (!usage) return null;\n` +
435
363
  `\n` +
436
- `function resolveSessionInfo(agentId, sessionKey) {\n` +
437
- ` const key = normalize(sessionKey);\n` +
438
- ` if (!key) return null;\n` +
439
- ` const sessionsPath = path.join(openclawHome, 'agents', agentId, 'sessions', 'sessions.json');\n` +
440
- ` try {\n` +
441
- ` const raw = fs.readFileSync(sessionsPath, 'utf8');\n` +
442
- ` const parsed = JSON.parse(raw);\n` +
443
- ` if (!parsed || typeof parsed !== 'object') return null;\n` +
444
- ` const entry = parsed[key];\n` +
445
- ` if (!entry || typeof entry !== 'object') return null;\n` +
446
- ` return {\n` +
447
- ` sessionKey: key,\n` +
448
- ` sessionId: normalize(entry.sessionId),\n` +
449
- ` entry\n` +
450
- ` };\n` +
451
- ` } catch (_) {}\n` +
452
- ` return null;\n` +
453
- `}\n` +
364
+ ` const sessionKey = normalize(ctx && ctx.sessionKey);\n` +
365
+ ` if (!sessionKey) return null;\n` +
454
366
  `\n` +
455
- `function parseAgentId(sessionKey) {\n` +
456
- ` const s = normalize(sessionKey);\n` +
457
- ` if (!s || !s.startsWith('agent:')) return null;\n` +
458
- ` const parts = s.split(':');\n` +
459
- ` return parts.length >= 2 ? normalize(parts[1]) : null;\n` +
367
+ ` return {\n` +
368
+ ` emittedAt: normalizeIso(event && (event.emittedAt || event.timestamp)) || new Date().toISOString(),\n` +
369
+ ` source: 'openclaw',\n` +
370
+ ` agentId: normalize(ctx && ctx.agentId),\n` +
371
+ ` sessionKey,\n` +
372
+ ` provider: normalize(event && event.provider) || normalize(ctx && ctx.provider),\n` +
373
+ ` model: normalize(event && event.model) || normalize(ctx && ctx.model),\n` +
374
+ ` channel: normalize(ctx && ctx.channel),\n` +
375
+ ` chatType: normalize(ctx && ctx.chatType),\n` +
376
+ ` trigger: normalize(ctx && ctx.trigger) || 'llm_output',\n` +
377
+ ` ...usage\n` +
378
+ ` };\n` +
460
379
  `}\n` +
461
380
  `\n` +
462
- `function allowTrigger(kind, scope, target) {\n` +
463
- ` const key = [kind, scope || 'na', target || 'na'].join(':');\n` +
464
- ` const now = Date.now();\n` +
465
- ` let state = {};\n` +
466
- ` try {\n` +
467
- ` state = JSON.parse(fs.readFileSync(triggerStatePath, 'utf8'));\n` +
468
- ` if (!state || typeof state !== 'object') state = {};\n` +
469
- ` } catch (_) {}\n` +
470
- ` const last = Number(state[key] || 0);\n` +
471
- ` if (Number.isFinite(last) && now - last < SESSION_TRIGGER_THROTTLE_MS) return false;\n` +
472
- ` state[key] = now;\n` +
473
- ` try {\n` +
474
- ` fs.mkdirSync(path.dirname(triggerStatePath), { recursive: true });\n` +
475
- ` fs.writeFileSync(triggerStatePath, JSON.stringify(state), 'utf8');\n` +
476
- ` } catch (_) {}\n` +
477
- ` return true;\n` +
381
+ `function normalizeUsage(event) {\n` +
382
+ ` const usage = event && typeof event.usage === 'object' ? event.usage : event || {};\n` +
383
+ ` const normalized = {\n` +
384
+ ` inputTokens: toNonNegativeInt(usage.inputTokens ?? usage.input_tokens ?? usage.input),\n` +
385
+ ` cachedInputTokens: toNonNegativeInt(usage.cachedInputTokens ?? usage.cached_input_tokens ?? usage.cacheRead ?? 0) + toNonNegativeInt(usage.cacheWrite ?? 0),\n` +
386
+ ` outputTokens: toNonNegativeInt(usage.outputTokens ?? usage.output_tokens ?? usage.output),\n` +
387
+ ` reasoningOutputTokens: toNonNegativeInt(usage.reasoningOutputTokens ?? usage.reasoning_output_tokens),\n` +
388
+ ` totalTokens: toNonNegativeInt(usage.totalTokens ?? usage.total_tokens)\n` +
389
+ ` };\n` +
390
+ ` const sum = normalized.inputTokens + normalized.cachedInputTokens + normalized.outputTokens + normalized.reasoningOutputTokens + normalized.totalTokens;\n` +
391
+ ` return sum > 0 ? normalized : null;\n` +
478
392
  `}\n` +
479
393
  `\n` +
480
394
  `function normalize(v) {\n` +
@@ -483,22 +397,18 @@ function buildSessionPluginIndex({ trackerDir, packageName = "vibeusage", opencl
483
397
  ` return s.length > 0 ? s : null;\n` +
484
398
  `}\n` +
485
399
  `\n` +
486
- `function toNonNegativeInt(v) {\n` +
487
- ` const n = Number(v);\n` +
488
- ` if (!Number.isFinite(n) || n < 0) return null;\n` +
489
- ` return Math.floor(n);\n` +
400
+ `function normalizeIso(v) {\n` +
401
+ ` const s = normalize(v);\n` +
402
+ ` if (!s) return null;\n` +
403
+ ` const ms = Date.parse(s);\n` +
404
+ ` if (!Number.isFinite(ms)) return null;\n` +
405
+ ` return new Date(ms).toISOString();\n` +
490
406
  `}\n` +
491
407
  `\n` +
492
- `function toIso(v) {\n` +
493
- ` if (typeof v === 'string') {\n` +
494
- ` const s = normalize(v);\n` +
495
- ` if (s && !Number.isNaN(Date.parse(s))) return s;\n` +
496
- ` }\n` +
497
- ` const n = Number(v);\n` +
498
- ` if (!Number.isFinite(n) || n <= 0) return null;\n` +
499
- ` const ms = n < 1e12 ? Math.floor(n * 1000) : Math.floor(n);\n` +
500
- ` const d = new Date(ms);\n` +
501
- ` return Number.isNaN(d.getTime()) ? null : d.toISOString();\n` +
408
+ `function toNonNegativeInt(v) {\n` +
409
+ ` const n = Number(v || 0);\n` +
410
+ ` if (!Number.isFinite(n) || n < 0) return 0;\n` +
411
+ ` return Math.floor(n);\n` +
502
412
  `}\n`
503
413
  );
504
414
  }