devrage 0.0.5 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -654,6 +654,7 @@ async function* parseCodexUsageJsonl(filePath, context) {
654
654
  });
655
655
  let model;
656
656
  let previousTotal = null;
657
+ let previousUsageSignature = null;
657
658
  for await (const line of rl) {
658
659
  if (!line.trim()) {
659
660
  continue;
@@ -669,13 +670,31 @@ async function* parseCodexUsageJsonl(filePath, context) {
669
670
  continue;
670
671
  }
671
672
  const info = asRecord3(payload["info"]);
672
- const total = parseCodexTokenUsage(info?.["total_token_usage"]);
673
- if (!total) {
673
+ if (!info) {
674
674
  continue;
675
675
  }
676
- const delta = previousTotal ? subtractCodexUsage(total, previousTotal) : total;
677
- previousTotal = total;
678
- if (!hasPositiveUsage(delta)) {
676
+ const lastUsageValue = info["last_token_usage"];
677
+ const lastUsage = parseCodexTokenUsage(lastUsageValue);
678
+ const total = parseCodexTokenUsage(info["total_token_usage"]);
679
+ let usage2 = null;
680
+ if (lastUsageValue !== void 0) {
681
+ if (lastUsage && hasBillableUsage(lastUsage)) {
682
+ const signature = codexUsageSignature(lastUsage, total);
683
+ if (signature !== previousUsageSignature) {
684
+ usage2 = lastUsage;
685
+ }
686
+ previousUsageSignature = signature;
687
+ }
688
+ } else if (total) {
689
+ const delta = previousTotal ? subtractCodexUsage(total, previousTotal) : total;
690
+ if (hasBillableUsage(delta)) {
691
+ usage2 = delta;
692
+ }
693
+ }
694
+ if (total && hasBillableUsage(total)) {
695
+ previousTotal = total;
696
+ }
697
+ if (!usage2) {
679
698
  continue;
680
699
  }
681
700
  const timestamp = stringValue2(entry["timestamp"]);
@@ -685,17 +704,17 @@ async function* parseCodexUsageJsonl(filePath, context) {
685
704
  continue;
686
705
  }
687
706
  }
688
- const reasoningTokens = Math.min(delta.reasoningOutputTokens, delta.outputTokens);
707
+ const reasoningTokens = Math.min(usage2.reasoningOutputTokens, usage2.outputTokens);
689
708
  yield {
690
709
  agent: "codex",
691
710
  provider: "openai",
692
711
  model,
693
712
  timestamp,
694
713
  session: context.session,
695
- inputTokens: Math.max(delta.inputTokens - delta.cachedInputTokens, 0),
696
- outputTokens: Math.max(delta.outputTokens - reasoningTokens, 0),
714
+ inputTokens: Math.max(usage2.inputTokens - usage2.cachedInputTokens, 0),
715
+ outputTokens: Math.max(usage2.outputTokens - reasoningTokens, 0),
697
716
  reasoningTokens,
698
- cacheReadTokens: delta.cachedInputTokens,
717
+ cacheReadTokens: usage2.cachedInputTokens,
699
718
  cacheWriteTokens: 0
700
719
  };
701
720
  } catch {
@@ -707,14 +726,23 @@ function parseCodexTokenUsage(value) {
707
726
  if (!usage2) {
708
727
  return null;
709
728
  }
710
- const parsed = {
729
+ const hasUsageField = [
730
+ "input_tokens",
731
+ "cached_input_tokens",
732
+ "output_tokens",
733
+ "reasoning_output_tokens",
734
+ "total_tokens"
735
+ ].some((key) => typeof usage2[key] === "number" && Number.isFinite(usage2[key]));
736
+ if (!hasUsageField) {
737
+ return null;
738
+ }
739
+ return {
711
740
  inputTokens: numberValue2(usage2["input_tokens"]),
712
741
  cachedInputTokens: numberValue2(usage2["cached_input_tokens"]),
713
742
  outputTokens: numberValue2(usage2["output_tokens"]),
714
743
  reasoningOutputTokens: numberValue2(usage2["reasoning_output_tokens"]),
715
744
  totalTokens: numberValue2(usage2["total_tokens"])
716
745
  };
717
- return hasPositiveUsage(parsed) ? parsed : null;
718
746
  }
719
747
  function subtractCodexUsage(current, previous) {
720
748
  return {
@@ -728,8 +756,20 @@ function subtractCodexUsage(current, previous) {
728
756
  totalTokens: Math.max(current.totalTokens - previous.totalTokens, 0)
729
757
  };
730
758
  }
731
- function hasPositiveUsage(usage2) {
732
- return usage2.inputTokens + usage2.cachedInputTokens + usage2.outputTokens + usage2.totalTokens > 0;
759
+ function hasBillableUsage(usage2) {
760
+ return usage2.inputTokens + usage2.cachedInputTokens + usage2.outputTokens + usage2.reasoningOutputTokens > 0;
761
+ }
762
+ function codexUsageSignature(usage2, total) {
763
+ return [
764
+ usage2.inputTokens,
765
+ usage2.cachedInputTokens,
766
+ usage2.outputTokens,
767
+ usage2.reasoningOutputTokens,
768
+ total?.inputTokens ?? "",
769
+ total?.cachedInputTokens ?? "",
770
+ total?.outputTokens ?? "",
771
+ total?.reasoningOutputTokens ?? ""
772
+ ].join(":");
733
773
  }
734
774
  function numberValue2(value) {
735
775
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -805,10 +845,12 @@ async function discoverCursorStateStores() {
805
845
  return stores;
806
846
  }
807
847
  function getCursorUserDirs() {
848
+ const configHome = envOrDefault("XDG_CONFIG_HOME", join5(homedir5(), ".config"));
849
+ const appData = envOrDefault("APPDATA", join5(homedir5(), "AppData", "Roaming"));
808
850
  return uniqueStrings([
809
851
  join5(homedir5(), "Library", "Application Support", "Cursor", "User"),
810
- join5(process.env["XDG_CONFIG_HOME"] ?? join5(homedir5(), ".config"), "Cursor", "User"),
811
- join5(process.env["APPDATA"] ?? join5(homedir5(), "AppData", "Roaming"), "Cursor", "User")
852
+ join5(configHome, "Cursor", "User"),
853
+ join5(appData, "Cursor", "User")
812
854
  ]);
813
855
  }
814
856
  async function* parseCursorStore(store, options) {
@@ -820,36 +862,40 @@ async function* parseCursorStore(store, options) {
820
862
  const rows = readStateRows(db);
821
863
  const seen = /* @__PURE__ */ new Set();
822
864
  for (const row of rows) {
823
- if (!isCandidateKey(row.key)) {
824
- continue;
825
- }
826
- const parsed = parseJsonValue(row.value);
827
- if (parsed === void 0) {
828
- continue;
829
- }
830
- for (const message of extractCursorMessages(parsed, row.key)) {
831
- const text = message.text.trim();
832
- if (!isLikelyMessageText(text)) {
865
+ try {
866
+ if (!isCandidateKey(row.key)) {
833
867
  continue;
834
868
  }
835
- if (options?.since && message.timestamp) {
836
- const timestamp = new Date(message.timestamp);
837
- if (Number.isFinite(timestamp.getTime()) && timestamp < options.since) {
869
+ const parsed = parseJsonValue(row.value);
870
+ if (parsed === void 0) {
871
+ continue;
872
+ }
873
+ for (const message of extractCursorMessages(parsed, row.key)) {
874
+ const text = message.text.trim();
875
+ if (!isLikelyMessageText(text)) {
838
876
  continue;
839
877
  }
878
+ if (options?.since && message.timestamp) {
879
+ const timestamp = new Date(message.timestamp);
880
+ if (Number.isFinite(timestamp.getTime()) && timestamp < options.since) {
881
+ continue;
882
+ }
883
+ }
884
+ const session = message.session ?? `${store.scope}:${row.key}`;
885
+ const dedupeKey = `${session}\0${message.timestamp ?? ""}\0${text}`;
886
+ if (seen.has(dedupeKey)) {
887
+ continue;
888
+ }
889
+ seen.add(dedupeKey);
890
+ yield {
891
+ text,
892
+ timestamp: message.timestamp,
893
+ session,
894
+ project: store.project
895
+ };
840
896
  }
841
- const session = message.session ?? `${store.scope}:${row.key}`;
842
- const dedupeKey = `${session}\0${message.timestamp ?? ""}\0${text}`;
843
- if (seen.has(dedupeKey)) {
844
- continue;
845
- }
846
- seen.add(dedupeKey);
847
- yield {
848
- text,
849
- timestamp: message.timestamp,
850
- session,
851
- project: store.project
852
- };
897
+ } catch {
898
+ continue;
853
899
  }
854
900
  }
855
901
  } finally {
@@ -866,26 +912,30 @@ async function* parseCursorUsageStore(store, options) {
866
912
  const composerModels = collectComposerModels(rows);
867
913
  const seen = /* @__PURE__ */ new Set();
868
914
  for (const row of rows) {
869
- if (!row.key.startsWith("bubbleId:")) {
870
- continue;
871
- }
872
- const parsed = parseJsonValue(row.value);
873
- const usage2 = extractCursorBubbleUsage(parsed, row.key, composerModels);
874
- if (!usage2) {
875
- continue;
876
- }
877
- if (options?.since && usage2.timestamp) {
878
- const timestamp = new Date(usage2.timestamp);
879
- if (Number.isFinite(timestamp.getTime()) && timestamp < options.since) {
915
+ try {
916
+ if (!row.key.startsWith("bubbleId:")) {
880
917
  continue;
881
918
  }
882
- }
883
- const dedupeKey = `${usage2.session ?? ""}\0${usage2.timestamp ?? ""}\0${usage2.model ?? ""}\0${usage2.inputTokens}\0${usage2.outputTokens}`;
884
- if (seen.has(dedupeKey)) {
919
+ const parsed = parseJsonValue(row.value);
920
+ const usage2 = extractCursorBubbleUsage(parsed, row.key, composerModels);
921
+ if (!usage2) {
922
+ continue;
923
+ }
924
+ if (options?.since && usage2.timestamp) {
925
+ const timestamp = new Date(usage2.timestamp);
926
+ if (Number.isFinite(timestamp.getTime()) && timestamp < options.since) {
927
+ continue;
928
+ }
929
+ }
930
+ const dedupeKey = `${usage2.session ?? ""}\0${usage2.timestamp ?? ""}\0${usage2.model ?? ""}\0${usage2.inputTokens}\0${usage2.outputTokens}`;
931
+ if (seen.has(dedupeKey)) {
932
+ continue;
933
+ }
934
+ seen.add(dedupeKey);
935
+ yield usage2;
936
+ } catch {
885
937
  continue;
886
938
  }
887
- seen.add(dedupeKey);
888
- yield usage2;
889
939
  }
890
940
  } finally {
891
941
  db.close();
@@ -907,18 +957,22 @@ function readStateRows(db) {
907
957
  const rows = [];
908
958
  try {
909
959
  const tables = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all();
910
- const availableTables = new Set(tables.map((table) => table.name));
960
+ const availableTables = new Set(tables.flatMap((table) => stringValue3(table.name) ?? []));
911
961
  for (const table of STATE_TABLES) {
912
962
  if (!availableTables.has(table)) {
913
963
  continue;
914
964
  }
915
965
  const columns = db.prepare(`PRAGMA table_info("${table}")`).all();
916
- const columnNames = new Set(columns.map((column) => column.name));
966
+ const columnNames = new Set(columns.flatMap((column) => stringValue3(column.name) ?? []));
917
967
  if (!columnNames.has("key") || !columnNames.has("value")) {
918
968
  continue;
919
969
  }
920
970
  const tableRows = db.prepare(`SELECT key, value FROM "${table}"`).all();
921
- rows.push(...tableRows);
971
+ rows.push(
972
+ ...tableRows.flatMap(
973
+ (row) => typeof row.key === "string" ? [{ key: row.key, value: row.value }] : []
974
+ )
975
+ );
922
976
  }
923
977
  } catch {
924
978
  return rows;
@@ -1250,6 +1304,10 @@ function uniqueMessages(messages) {
1250
1304
  function uniqueStrings(values) {
1251
1305
  return Array.from(new Set(values));
1252
1306
  }
1307
+ function envOrDefault(name, fallback) {
1308
+ const value = process.env[name];
1309
+ return value && value.trim() ? value : fallback;
1310
+ }
1253
1311
  function numberValue3(value) {
1254
1312
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
1255
1313
  }
@@ -3088,6 +3146,7 @@ var COMMANDS = {
3088
3146
  cost,
3089
3147
  scan
3090
3148
  };
3149
+ var OPTIONS_WITH_VALUES = /* @__PURE__ */ new Set(["--agent", "-a", "--since", "-s"]);
3091
3150
  function usage() {
3092
3151
  console.log(`devrage \u2014 count how many times you swear at your coding agents
3093
3152
 
@@ -3116,16 +3175,35 @@ async function main() {
3116
3175
  process.exit(0);
3117
3176
  }
3118
3177
  if (command === "--version") {
3119
- console.log("0.0.5");
3178
+ console.log("0.5.3");
3120
3179
  process.exit(0);
3121
3180
  }
3122
- const handler = command ? COMMANDS[command] : void 0;
3123
- if (handler) {
3124
- await handler(args.slice(1));
3181
+ const parsed = parseCommand(args);
3182
+ if (parsed) {
3183
+ await parsed.handler(parsed.args);
3125
3184
  } else {
3126
3185
  await scan(args);
3127
3186
  }
3128
3187
  }
3188
+ function parseCommand(args) {
3189
+ for (let index = 0; index < args.length; index++) {
3190
+ const arg = args[index];
3191
+ if (!arg) {
3192
+ continue;
3193
+ }
3194
+ const handler = COMMANDS[arg];
3195
+ if (handler) {
3196
+ return {
3197
+ handler,
3198
+ args: [...args.slice(0, index), ...args.slice(index + 1)]
3199
+ };
3200
+ }
3201
+ if (OPTIONS_WITH_VALUES.has(arg) && index + 1 < args.length) {
3202
+ index++;
3203
+ }
3204
+ }
3205
+ return null;
3206
+ }
3129
3207
  main().catch((err) => {
3130
3208
  console.error(err);
3131
3209
  process.exit(1);