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
@@ -4,6 +4,7 @@ const path = require("node:path");
4
4
  const readline = require("node:readline");
5
5
 
6
6
  const { ensureDir } = require("./fs");
7
+ const { readOpencodeSqliteRows } = require("./opencode-sqlite");
7
8
  const { hashRepoRoot, resolveGitHubPublicStatus } = require("./vibeusage-public-repo");
8
9
 
9
10
  const DEFAULT_SOURCE = "codex";
@@ -398,12 +399,14 @@ async function parseGeminiIncremental({
398
399
 
399
400
  async function parseOpencodeIncremental({
400
401
  messageFiles,
402
+ opencodeDbPath,
401
403
  cursors,
402
404
  queuePath,
403
405
  projectQueuePath,
404
406
  onProgress,
405
407
  source,
406
408
  publicRepoResolver,
409
+ readSqliteRows,
407
410
  }) {
408
411
  await ensureDir(path.dirname(queuePath));
409
412
  let filesProcessed = 0;
@@ -419,9 +422,13 @@ async function parseOpencodeIncremental({
419
422
  const projectMetaCache = projectEnabled ? new Map() : null;
420
423
  const publicRepoCache = projectEnabled ? new Map() : null;
421
424
  const opencodeState = normalizeOpencodeState(cursors?.opencode);
425
+ const opencodeSqliteState = normalizeOpencodeSqliteState(cursors?.opencodeSqlite);
422
426
  const messageIndex = opencodeState.messages;
423
427
  const touchedBuckets = new Set();
424
428
  const defaultSource = normalizeSourceInput(source) || "opencode";
429
+ let sqliteStatus = opencodeSqliteState.lastStatus || "never_checked";
430
+ let sqliteCheckedAt = opencodeSqliteState.lastCheckedAt || null;
431
+ let sqliteErrorCode = opencodeSqliteState.lastErrorCode || null;
425
432
 
426
433
  if (!cursors.files || typeof cursors.files !== "object") {
427
434
  cursors.files = {};
@@ -522,93 +529,88 @@ async function parseOpencodeIncremental({
522
529
  }
523
530
  }
524
531
 
525
- const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
526
- const projectBucketsQueued = projectEnabled
527
- ? await enqueueTouchedProjectBuckets({ projectQueuePath, projectState, projectTouchedBuckets })
528
- : 0;
529
- hourlyState.updatedAt = new Date().toISOString();
530
- cursors.hourly = hourlyState;
531
- opencodeState.updatedAt = new Date().toISOString();
532
- cursors.opencode = opencodeState;
533
- if (projectState) {
534
- projectState.updatedAt = new Date().toISOString();
535
- cursors.projectHourly = projectState;
536
- }
537
-
538
- return { filesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
539
- }
540
-
541
- async function parseOpenclawIncremental({
542
- sessionFiles,
543
- cursors,
544
- queuePath,
545
- projectQueuePath,
546
- onProgress,
547
- source,
548
- }) {
549
- await ensureDir(path.dirname(queuePath));
550
- let filesProcessed = 0;
551
- let eventsAggregated = 0;
552
-
553
- const cb = typeof onProgress === "function" ? onProgress : null;
554
- const files = Array.isArray(sessionFiles) ? sessionFiles : [];
555
- const totalFiles = files.length;
556
- const hourlyState = normalizeHourlyState(cursors?.hourly);
557
- const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
558
- const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
559
- const projectTouchedBuckets = projectEnabled ? new Set() : null;
560
- const touchedBuckets = new Set();
561
- const defaultSource = normalizeSourceInput(source) || "openclaw";
562
-
563
- if (!cursors.files || typeof cursors.files !== "object") {
564
- cursors.files = {};
565
- }
566
-
567
- for (let idx = 0; idx < files.length; idx++) {
568
- const entry = files[idx];
569
- const filePath = typeof entry === "string" ? entry : entry?.path;
570
- if (!filePath) continue;
571
- const fileSource =
572
- typeof entry === "string"
573
- ? defaultSource
574
- : normalizeSourceInput(entry?.source) || defaultSource;
575
- const st = await fs.stat(filePath).catch(() => null);
576
- if (!st || !st.isFile()) continue;
577
-
578
- const key = filePath;
579
- const prev = cursors.files[key] || null;
580
- const inode = st.ino || 0;
581
- const startOffset = prev && prev.inode === inode ? prev.offset || 0 : 0;
582
-
583
- const result = await parseOpenclawSessionFile({
584
- filePath,
585
- startOffset,
586
- hourlyState,
587
- touchedBuckets,
588
- source: fileSource,
589
- projectState,
590
- projectTouchedBuckets,
532
+ if (typeof opencodeDbPath === "string" && opencodeDbPath.length > 0) {
533
+ const readRows =
534
+ typeof readSqliteRows === "function" ? readSqliteRows : readOpencodeSqliteRows;
535
+ const sqliteResult = await readRows({
536
+ dbPath: opencodeDbPath,
537
+ lastTimeCreated: opencodeSqliteState.lastTimeCreated,
538
+ expectedInode: opencodeSqliteState.inode,
591
539
  });
592
540
 
593
- cursors.files[key] = {
594
- inode,
595
- offset: result.endOffset,
596
- updatedAt: new Date().toISOString(),
597
- };
598
-
599
- filesProcessed += 1;
600
- eventsAggregated += result.eventsAggregated;
541
+ sqliteStatus =
542
+ typeof sqliteResult?.status === "string" && sqliteResult.status.trim()
543
+ ? sqliteResult.status.trim()
544
+ : "query-failed";
545
+ sqliteCheckedAt =
546
+ typeof sqliteResult?.checkedAt === "string" && sqliteResult.checkedAt.trim()
547
+ ? sqliteResult.checkedAt.trim()
548
+ : new Date().toISOString();
549
+ sqliteErrorCode =
550
+ typeof sqliteResult?.errorCode === "string" && sqliteResult.errorCode.trim()
551
+ ? sqliteResult.errorCode.trim()
552
+ : typeof sqliteResult?.error?.code === "string" && sqliteResult.error.code.trim()
553
+ ? sqliteResult.error.code.trim()
554
+ : null;
555
+
556
+ opencodeSqliteState.lastStatus = sqliteStatus;
557
+ opencodeSqliteState.lastCheckedAt = sqliteCheckedAt;
558
+ opencodeSqliteState.lastErrorCode = sqliteErrorCode;
559
+ opencodeSqliteState.updatedAt = new Date().toISOString();
560
+
561
+ if (sqliteStatus === "ok") {
562
+ const sameDb =
563
+ !sqliteResult.cursorReset &&
564
+ opencodeSqliteState.inode &&
565
+ sqliteResult.inode &&
566
+ opencodeSqliteState.inode === sqliteResult.inode;
567
+ const prevProcessedIds = new Set(sameDb ? opencodeSqliteState.lastProcessedIds : []);
568
+ const rawRows = Array.isArray(sqliteResult.rows) ? sqliteResult.rows : [];
569
+ const rows = prevProcessedIds.size
570
+ ? rawRows.filter((row) => !prevProcessedIds.has(row?.id))
571
+ : rawRows;
572
+ const knownMessageKeys = new Set([
573
+ ...Object.keys(messageIndex || {}),
574
+ ...Object.values(cursors.files || {})
575
+ .map((entry) => entry?.messageKey)
576
+ .filter((entry) => typeof entry === "string" && entry.trim()),
577
+ ]);
578
+
579
+ for (const row of rows) {
580
+ const result = await parseOpencodeSqliteRow({
581
+ row,
582
+ messageIndex,
583
+ knownMessageKeys,
584
+ hourlyState,
585
+ touchedBuckets,
586
+ source: defaultSource,
587
+ projectState,
588
+ projectTouchedBuckets,
589
+ projectMetaCache,
590
+ publicRepoCache,
591
+ publicRepoResolver,
592
+ });
593
+ eventsAggregated += result.eventsAggregated;
594
+ }
601
595
 
602
- if (cb) {
603
- cb({
604
- index: idx + 1,
605
- total: totalFiles,
606
- filePath,
607
- filesProcessed,
608
- eventsAggregated,
609
- bucketsQueued: touchedBuckets.size,
610
- });
596
+ const lastTimeCreated = sameDb ? opencodeSqliteState.lastTimeCreated : 0;
597
+ const maxTime =
598
+ rawRows.length > 0
599
+ ? Math.max(
600
+ lastTimeCreated,
601
+ ...rawRows.map((row) =>
602
+ Number.isFinite(Number(row?.time_created)) ? Number(row.time_created) : 0,
603
+ ),
604
+ )
605
+ : lastTimeCreated;
606
+ opencodeSqliteState.lastTimeCreated = maxTime;
607
+ opencodeSqliteState.lastProcessedIds = rawRows
608
+ .filter((row) => Number(row?.time_created) === maxTime)
609
+ .map((row) => row?.id)
610
+ .filter((value) => typeof value === "string" && value.trim());
611
+ opencodeSqliteState.inode = sqliteResult.inode || opencodeSqliteState.inode || 0;
611
612
  }
613
+ cursors.opencodeSqlite = opencodeSqliteState;
612
614
  }
613
615
 
614
616
  const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
@@ -617,80 +619,22 @@ async function parseOpenclawIncremental({
617
619
  : 0;
618
620
  hourlyState.updatedAt = new Date().toISOString();
619
621
  cursors.hourly = hourlyState;
622
+ opencodeState.updatedAt = new Date().toISOString();
623
+ cursors.opencode = opencodeState;
620
624
  if (projectState) {
621
625
  projectState.updatedAt = new Date().toISOString();
622
626
  cursors.projectHourly = projectState;
623
627
  }
624
628
 
625
- return { filesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
626
- }
627
-
628
- async function parseOpenclawSessionFile({
629
- filePath,
630
- startOffset,
631
- hourlyState,
632
- touchedBuckets,
633
- source,
634
- projectState,
635
- projectTouchedBuckets,
636
- }) {
637
- const st = await fs.stat(filePath);
638
- const endOffset = st.size;
639
- if (startOffset >= endOffset) return { endOffset, eventsAggregated: 0 };
640
-
641
- const stream = fssync.createReadStream(filePath, { encoding: "utf8", start: startOffset });
642
- const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
643
-
644
- let eventsAggregated = 0;
645
- for await (const line of rl) {
646
- if (!line) continue;
647
- // Fast-path filter: OpenClaw assistant messages include message.usage.totalTokens.
648
- if (!line.includes('"usage"') || !line.includes("totalTokens")) continue;
649
-
650
- let obj;
651
- try {
652
- obj = JSON.parse(line);
653
- } catch (_e) {
654
- continue;
655
- }
656
-
657
- if (obj?.type !== "message") continue;
658
- const msg = obj?.message;
659
- if (!msg || typeof msg !== "object") continue;
660
-
661
- const usage = msg.usage;
662
- if (!usage || typeof usage !== "object") continue;
663
-
664
- const tokenTimestamp = typeof obj?.timestamp === "string" ? obj.timestamp : null;
665
- if (!tokenTimestamp) continue;
666
-
667
- const model = normalizeModelInput(msg.model) || DEFAULT_MODEL;
668
-
669
- const delta = {
670
- input_tokens: Number(usage.input || 0),
671
- cached_input_tokens: Number((usage.cacheRead || 0) + (usage.cacheWrite || 0)),
672
- output_tokens: Number(usage.output || 0),
673
- reasoning_output_tokens: 0,
674
- total_tokens: Number(usage.totalTokens || 0),
675
- };
676
-
677
- if (isAllZeroUsage(delta)) continue;
678
-
679
- const bucketStart = toUtcHalfHourStart(tokenTimestamp);
680
- if (!bucketStart) continue;
681
-
682
- const bucket = getHourlyBucket(hourlyState, source, model, bucketStart);
683
- addTotals(bucket.totals, delta);
684
- touchedBuckets.add(bucketKey(source, model, bucketStart));
685
-
686
- // Project-level OpenClaw attribution is not supported yet (no stable cwd info).
687
- // If OpenClaw later records cwd per event, we can mirror rollout's project logic.
688
- eventsAggregated += 1;
689
- }
690
-
691
- rl.close();
692
- stream.close?.();
693
- return { endOffset, eventsAggregated };
629
+ return {
630
+ filesProcessed,
631
+ eventsAggregated,
632
+ bucketsQueued,
633
+ projectBucketsQueued,
634
+ sqliteStatus,
635
+ sqliteCheckedAt,
636
+ sqliteErrorCode,
637
+ };
694
638
  }
695
639
 
696
640
  async function parseRolloutFile({
@@ -1062,6 +1006,107 @@ async function parseOpencodeMessageFile({
1062
1006
  return { messageKey, lastTotals: currentTotals, eventsAggregated: 1, shouldUpdate: true };
1063
1007
  }
1064
1008
 
1009
+ async function parseOpencodeSqliteRow({
1010
+ row,
1011
+ messageIndex,
1012
+ knownMessageKeys,
1013
+ hourlyState,
1014
+ touchedBuckets,
1015
+ source,
1016
+ projectState,
1017
+ projectTouchedBuckets,
1018
+ projectMetaCache,
1019
+ publicRepoCache,
1020
+ publicRepoResolver,
1021
+ }) {
1022
+ if (!row || typeof row !== "object") {
1023
+ return { eventsAggregated: 0 };
1024
+ }
1025
+
1026
+ let msg;
1027
+ try {
1028
+ msg = JSON.parse(String(row.data || ""));
1029
+ } catch (_e) {
1030
+ return { eventsAggregated: 0 };
1031
+ }
1032
+
1033
+ const rowRole =
1034
+ normalizeMessageKeyPart(row.role) ||
1035
+ normalizeMessageKeyPart(msg?.role) ||
1036
+ normalizeMessageKeyPart(msg?.type);
1037
+ if (rowRole && rowRole !== "assistant") {
1038
+ return { eventsAggregated: 0 };
1039
+ }
1040
+
1041
+ const messageKey = deriveOpencodeMessageKey(msg, null);
1042
+ if (!messageKey || knownMessageKeys.has(messageKey)) {
1043
+ return { eventsAggregated: 0 };
1044
+ }
1045
+
1046
+ const currentTotals = normalizeOpencodeTokens(msg?.tokens);
1047
+ if (!currentTotals || isAllZeroUsage(currentTotals)) {
1048
+ return { eventsAggregated: 0 };
1049
+ }
1050
+
1051
+ const timestampMs = coerceEpochMs(msg?.time?.completed) || coerceEpochMs(msg?.time?.created);
1052
+ if (!timestampMs) {
1053
+ return { eventsAggregated: 0 };
1054
+ }
1055
+
1056
+ const bucketStart = toUtcHalfHourStart(new Date(timestampMs).toISOString());
1057
+ if (!bucketStart) {
1058
+ return { eventsAggregated: 0 };
1059
+ }
1060
+
1061
+ const model = normalizeModelInput(msg?.modelID || msg?.model || msg?.modelId) || DEFAULT_MODEL;
1062
+ const bucket = getHourlyBucket(hourlyState, source, model, bucketStart);
1063
+ addTotals(bucket.totals, currentTotals);
1064
+ touchedBuckets.add(bucketKey(source, model, bucketStart));
1065
+
1066
+ const projectWorktree =
1067
+ typeof row.project_worktree === "string" && row.project_worktree.trim()
1068
+ ? row.project_worktree.trim()
1069
+ : null;
1070
+ if (
1071
+ projectWorktree &&
1072
+ projectState &&
1073
+ projectTouchedBuckets &&
1074
+ projectMetaCache &&
1075
+ publicRepoCache
1076
+ ) {
1077
+ const projectContext = await resolveProjectContextForPath({
1078
+ startDir: projectWorktree,
1079
+ projectMetaCache,
1080
+ publicRepoCache,
1081
+ publicRepoResolver,
1082
+ projectState,
1083
+ });
1084
+ const projectRef = projectContext?.projectRef || null;
1085
+ const projectKey = projectContext?.projectKey || null;
1086
+ if (projectKey) {
1087
+ const projectBucket = getProjectBucket(
1088
+ projectState,
1089
+ projectKey,
1090
+ source,
1091
+ bucketStart,
1092
+ projectRef,
1093
+ );
1094
+ addTotals(projectBucket.totals, currentTotals);
1095
+ projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
1096
+ }
1097
+ }
1098
+
1099
+ if (messageIndex && typeof messageIndex === "object") {
1100
+ messageIndex[messageKey] = {
1101
+ lastTotals: currentTotals,
1102
+ updatedAt: new Date().toISOString(),
1103
+ };
1104
+ }
1105
+ knownMessageKeys.add(messageKey);
1106
+
1107
+ return { eventsAggregated: 1 };
1108
+ }
1109
+
1065
1110
  async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets }) {
1066
1111
  if (!touchedBuckets || touchedBuckets.size === 0) return 0;
1067
1112
 
@@ -1559,6 +1604,32 @@ function normalizeOpencodeState(raw) {
1559
1604
  };
1560
1605
  }
1561
1606
 
1607
+ function normalizeOpencodeSqliteState(raw) {
1608
+ const state = raw && typeof raw === "object" ? raw : {};
1609
+ return {
1610
+ lastTimeCreated: Number.isFinite(Number(state.lastTimeCreated))
1611
+ ? Number(state.lastTimeCreated)
1612
+ : 0,
1613
+ lastProcessedIds: Array.isArray(state.lastProcessedIds)
1614
+ ? state.lastProcessedIds
1615
+ .filter((value) => typeof value === "string" && value.trim())
1616
+ .map((value) => value.trim())
1617
+ : [],
1618
+ inode: Number.isFinite(Number(state.inode)) ? Number(state.inode) : 0,
1619
+ updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : null,
1620
+ lastStatus:
1621
+ typeof state.lastStatus === "string" && state.lastStatus.trim() ? state.lastStatus.trim() : null,
1622
+ lastCheckedAt:
1623
+ typeof state.lastCheckedAt === "string" && state.lastCheckedAt.trim()
1624
+ ? state.lastCheckedAt.trim()
1625
+ : null,
1626
+ lastErrorCode:
1627
+ typeof state.lastErrorCode === "string" && state.lastErrorCode.trim()
1628
+ ? state.lastErrorCode.trim()
1629
+ : null,
1630
+ };
1631
+ }
1632
+
1562
1633
  function normalizeMessageKeyPart(value) {
1563
1634
  if (typeof value !== "string") return "";
1564
1635
  return value.trim();
@@ -2227,5 +2298,10 @@ module.exports = {
2227
2298
  parseClaudeIncremental,
2228
2299
  parseGeminiIncremental,
2229
2300
  parseOpencodeIncremental,
2230
- parseOpenclawIncremental,
2301
+ normalizeHourlyState,
2302
+ getHourlyBucket,
2303
+ addTotals,
2304
+ bucketKey,
2305
+ enqueueTouchedBuckets,
2306
+ toUtcHalfHourStart,
2231
2307
  };
@@ -7,7 +7,7 @@ const {
7
7
  } = require("../shared/vibeusage-function-contract.cjs");
8
8
 
9
9
  async function signInWithPassword({ baseUrl, email, password }) {
10
- const client = createInsforgeClient({ baseUrl });
10
+ const client = await createInsforgeClient({ baseUrl });
11
11
  const { data, error } = await client.auth.signInWithPassword({ email, password });
12
12
  if (error) throw normalizeSdkError(error, "Sign-in failed");
13
13
 
@@ -121,7 +121,7 @@ module.exports = {
121
121
  };
122
122
 
123
123
  async function invokeFunction({ baseUrl, accessToken, slug, method, body, errorPrefix }) {
124
- const client = createInsforgeClient({ baseUrl, accessToken });
124
+ const client = await createInsforgeClient({ baseUrl, accessToken });
125
125
  const { data, error } = await client.functions.invoke(slug, { method, body });
126
126
  if (error) throw normalizeSdkError(error, errorPrefix);
127
127
  return data;
@@ -1,123 +0,0 @@
1
- const { probeOpenclawHookState, removeOpenclawHookConfig } = require("../openclaw-hook");
2
-
3
- module.exports = {
4
- name: "openclaw-legacy",
5
- summaryLabel: "OpenClaw Hook (legacy)",
6
- statusLabel: "OpenClaw hook (legacy)",
7
- async probe(ctx) {
8
- const state = await probeOpenclawHookState({
9
- home: ctx.home,
10
- trackerDir: ctx.trackerPaths.trackerDir,
11
- env: ctx.env,
12
- });
13
- if (state?.skippedReason === "openclaw-config-missing") {
14
- return baseProbe(this, { status: "not_installed", detail: "OpenClaw config not found" });
15
- }
16
- if (state?.skippedReason === "openclaw-config-unreadable") {
17
- return baseProbe(this, {
18
- status: "unreadable",
19
- detail: state.error
20
- ? `OpenClaw config unreadable: ${state.error}`
21
- : "OpenClaw config unreadable",
22
- linked: Boolean(state.linked),
23
- enabled: Boolean(state.enabled),
24
- });
25
- }
26
- if (state?.configured || state?.linked || state?.enabled) {
27
- return baseProbe(this, {
28
- status: "unsupported_legacy",
29
- detail: "Legacy OpenClaw hook detected; run vibeusage init",
30
- linked: Boolean(state.linked),
31
- enabled: Boolean(state.enabled),
32
- });
33
- }
34
- return baseProbe(this, { status: "not_installed", detail: "Legacy hook not installed" });
35
- },
36
- async install(ctx) {
37
- const state = await probeOpenclawHookState({
38
- home: ctx.home,
39
- trackerDir: ctx.trackerPaths.trackerDir,
40
- env: ctx.env,
41
- });
42
- if (state?.skippedReason === "openclaw-config-unreadable") {
43
- return action(
44
- this,
45
- "skipped",
46
- false,
47
- state.error ? `OpenClaw config unreadable: ${state.error}` : "OpenClaw config unreadable",
48
- { skippedReason: state.skippedReason },
49
- );
50
- }
51
- if (!(state?.configured || state?.linked || state?.enabled)) {
52
- return action(this, "unchanged", false, "no change");
53
- }
54
- const result = await removeOpenclawHookConfig({
55
- home: ctx.home,
56
- trackerDir: ctx.trackerPaths.trackerDir,
57
- env: ctx.env,
58
- });
59
- if (result?.removed) {
60
- return action(this, "updated", true, "Removed legacy command hook");
61
- }
62
- if (result?.skippedReason === "openclaw-config-unreadable") {
63
- return action(
64
- this,
65
- "skipped",
66
- false,
67
- result.error ? `OpenClaw config unreadable: ${result.error}` : "OpenClaw config unreadable",
68
- { skippedReason: result.skippedReason },
69
- );
70
- }
71
- return action(this, "unchanged", false, "no change");
72
- },
73
- async uninstall(ctx) {
74
- const result = await removeOpenclawHookConfig({
75
- home: ctx.home,
76
- trackerDir: ctx.trackerPaths.trackerDir,
77
- env: ctx.env,
78
- });
79
- if (result?.removed) {
80
- return action(this, "removed", true, result.openclawConfigPath);
81
- }
82
- if (result?.skippedReason === "openclaw-config-missing") {
83
- return action(this, "skipped", false, "openclaw config not found", {
84
- skippedReason: result.skippedReason,
85
- });
86
- }
87
- if (result?.skippedReason === "openclaw-config-unreadable") {
88
- return action(
89
- this,
90
- "skipped",
91
- false,
92
- result.error ? `openclaw config unreadable: ${result.error}` : "openclaw config unreadable",
93
- { skippedReason: result.skippedReason },
94
- );
95
- }
96
- return action(this, "unchanged", false, "no change");
97
- },
98
- renderStatusValue(probe) {
99
- if (probe.status === "not_installed") return "unset";
100
- return probe.status;
101
- },
102
- };
103
-
104
- function baseProbe(descriptor, values) {
105
- return {
106
- name: descriptor.name,
107
- summaryLabel: descriptor.summaryLabel,
108
- statusLabel: descriptor.statusLabel,
109
- configured: false,
110
- ...values,
111
- };
112
- }
113
-
114
- function action(descriptor, status, changed, detail, extras = {}) {
115
- return {
116
- name: descriptor.name,
117
- label: descriptor.summaryLabel,
118
- status,
119
- changed,
120
- detail,
121
- ...extras,
122
- };
123
- }