devrage 0.5.5 → 0.5.6

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/commands/scan.ts
4
4
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
5
- import { dirname as dirname2, join as join10 } from "node:path";
5
+ import { dirname as dirname2, join as join11 } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
 
8
8
  // src/adapters/amp.ts
@@ -1689,23 +1689,444 @@ function asRecord5(value) {
1689
1689
  return value;
1690
1690
  }
1691
1691
 
1692
- // src/adapters/zed.ts
1693
- import { readdir as readdir7, readFile as readFile3 } from "node:fs/promises";
1692
+ // src/adapters/t3code.ts
1694
1693
  import { existsSync as existsSync4 } from "node:fs";
1695
1694
  import { homedir as homedir8 } from "node:os";
1696
- import { join as join8 } from "node:path";
1695
+ import { isAbsolute, join as join8, resolve } from "node:path";
1696
+ function t3codeAdapter() {
1697
+ return {
1698
+ name: "t3code",
1699
+ async *messages(options) {
1700
+ for (const location of discoverT3Databases()) {
1701
+ const db = await openT3Db(location.path);
1702
+ if (!db) {
1703
+ continue;
1704
+ }
1705
+ try {
1706
+ yield* queryUserMessages2(db, location, options);
1707
+ } finally {
1708
+ db.close();
1709
+ }
1710
+ }
1711
+ },
1712
+ async *usage(options) {
1713
+ const seen = /* @__PURE__ */ new Set();
1714
+ for (const location of discoverT3Databases()) {
1715
+ const db = await openT3Db(location.path);
1716
+ if (!db) {
1717
+ continue;
1718
+ }
1719
+ try {
1720
+ yield* queryUsageRecords2(db, location, seen, options);
1721
+ } finally {
1722
+ db.close();
1723
+ }
1724
+ }
1725
+ }
1726
+ };
1727
+ }
1728
+ function discoverT3Databases() {
1729
+ const locations = [];
1730
+ const seen = /* @__PURE__ */ new Set();
1731
+ const stateDir = stringValue6(process.env["T3CODE_STATE_DIR"]);
1732
+ if (stateDir) {
1733
+ addLocation(locations, seen, join8(resolveHomePath(stateDir), "state.sqlite"), "state");
1734
+ }
1735
+ for (const baseDir of uniqueStrings2([
1736
+ stringValue6(process.env["T3CODE_HOME"]),
1737
+ join8(homedir8(), ".t3")
1738
+ ])) {
1739
+ addLocation(
1740
+ locations,
1741
+ seen,
1742
+ join8(resolveHomePath(baseDir), "userdata", "state.sqlite"),
1743
+ "userdata"
1744
+ );
1745
+ addLocation(locations, seen, join8(resolveHomePath(baseDir), "dev", "state.sqlite"), "dev");
1746
+ }
1747
+ return locations;
1748
+ }
1749
+ function addLocation(locations, seen, dbPath, scope) {
1750
+ if (seen.has(dbPath) || !existsSync4(dbPath)) {
1751
+ return;
1752
+ }
1753
+ seen.add(dbPath);
1754
+ locations.push({ path: dbPath, scope });
1755
+ }
1756
+ function resolveHomePath(value) {
1757
+ if (value === "~") {
1758
+ return homedir8();
1759
+ }
1760
+ if (value.startsWith("~/") || value.startsWith("~\\")) {
1761
+ return join8(homedir8(), value.slice(2));
1762
+ }
1763
+ return isAbsolute(value) ? value : resolve(value);
1764
+ }
1765
+ async function openT3Db(dbPath) {
1766
+ try {
1767
+ const BetterSqlite3 = await import("better-sqlite3");
1768
+ const Ctor = BetterSqlite3.default ?? BetterSqlite3;
1769
+ return new Ctor(
1770
+ dbPath,
1771
+ { readonly: true, fileMustExist: true }
1772
+ );
1773
+ } catch {
1774
+ return null;
1775
+ }
1776
+ }
1777
+ function* queryUserMessages2(db, location, options) {
1778
+ if (!hasColumns(db, "projection_thread_messages", ["thread_id", "role", "text", "created_at"])) {
1779
+ return;
1780
+ }
1781
+ const orderColumn = hasColumns(db, "projection_thread_messages", ["message_id"]) ? "message_id" : "created_at";
1782
+ let query = `
1783
+ SELECT thread_id, created_at, text
1784
+ FROM projection_thread_messages
1785
+ WHERE role = 'user'
1786
+ `;
1787
+ const params = [];
1788
+ if (options?.since) {
1789
+ query += ` AND created_at >= ?`;
1790
+ params.push(options.since.toISOString());
1791
+ }
1792
+ query += ` ORDER BY created_at ASC, ${orderColumn} ASC`;
1793
+ let rows;
1794
+ try {
1795
+ rows = db.prepare(query).all(...params);
1796
+ } catch {
1797
+ return;
1798
+ }
1799
+ for (const row of rows) {
1800
+ const text = stringValue6(row.text);
1801
+ if (!text) {
1802
+ continue;
1803
+ }
1804
+ yield {
1805
+ text,
1806
+ timestamp: stringValue6(row.created_at),
1807
+ session: stringValue6(row.thread_id),
1808
+ project: location.scope
1809
+ };
1810
+ }
1811
+ }
1812
+ function* queryUsageRecords2(db, location, seen, options) {
1813
+ if (!hasColumns(db, "orchestration_events", [
1814
+ "event_id",
1815
+ "stream_id",
1816
+ "event_type",
1817
+ "occurred_at",
1818
+ "payload_json"
1819
+ ])) {
1820
+ return;
1821
+ }
1822
+ const threadInfo = readThreadInfo(db);
1823
+ const orderColumn = hasColumns(db, "orchestration_events", ["sequence"]) ? "sequence" : "event_id";
1824
+ let query = `
1825
+ SELECT event_id, stream_id, occurred_at, payload_json
1826
+ FROM orchestration_events
1827
+ WHERE event_type = 'thread.activity-appended'
1828
+ `;
1829
+ const params = [];
1830
+ if (options?.since) {
1831
+ query += ` AND occurred_at >= ?`;
1832
+ params.push(options.since.toISOString());
1833
+ }
1834
+ query += ` ORDER BY occurred_at ASC, ${orderColumn} ASC`;
1835
+ let rows;
1836
+ try {
1837
+ rows = db.prepare(query).all(...params);
1838
+ } catch {
1839
+ return;
1840
+ }
1841
+ for (const row of rows) {
1842
+ const payload = asRecord6(parseJson(row.payload_json));
1843
+ const activity = asRecord6(payload?.["activity"]);
1844
+ if (activity?.["kind"] !== "context-window.updated") {
1845
+ continue;
1846
+ }
1847
+ const usage2 = parseUsageSnapshot(activity["payload"]);
1848
+ if (!usage2 || !hasBillableUsage2(usage2)) {
1849
+ continue;
1850
+ }
1851
+ const threadId = stringValue6(payload?.["threadId"]) ?? stringValue6(row.stream_id);
1852
+ const timestamp = stringValue6(activity["createdAt"]) ?? stringValue6(row.occurred_at);
1853
+ const turnId = stringValue6(activity["turnId"]);
1854
+ const info = threadId ? threadInfo.get(threadId) : void 0;
1855
+ const provider = normalizeT3Provider(info?.provider, info?.model);
1856
+ const dedupeKey = t3UsageDedupeKey({
1857
+ scope: location.scope,
1858
+ threadId,
1859
+ turnId,
1860
+ provider,
1861
+ model: info?.model,
1862
+ usage: usage2
1863
+ });
1864
+ if (seen.has(dedupeKey)) {
1865
+ continue;
1866
+ }
1867
+ seen.add(dedupeKey);
1868
+ yield {
1869
+ agent: "t3code",
1870
+ provider,
1871
+ model: info?.model,
1872
+ timestamp,
1873
+ session: threadId,
1874
+ inputTokens: usage2.inputTokens,
1875
+ outputTokens: usage2.outputTokens,
1876
+ reasoningTokens: usage2.reasoningTokens,
1877
+ cacheReadTokens: usage2.cacheReadTokens,
1878
+ cacheWriteTokens: usage2.cacheWriteTokens
1879
+ };
1880
+ }
1881
+ }
1882
+ function normalizeT3Provider(provider, model) {
1883
+ const normalized = provider?.trim().toLowerCase();
1884
+ const key = normalized?.replace(/[^a-z0-9]/g, "");
1885
+ switch (key) {
1886
+ case "codex":
1887
+ return "openai";
1888
+ case "claudeagent":
1889
+ case "claudecode":
1890
+ return "anthropic";
1891
+ case "cursor":
1892
+ case "opencode":
1893
+ return void 0;
1894
+ default:
1895
+ if (key?.includes("codex")) {
1896
+ return "openai";
1897
+ }
1898
+ if (key?.includes("claude")) {
1899
+ return "anthropic";
1900
+ }
1901
+ if (normalized === "openai" || normalized === "anthropic") {
1902
+ return normalized;
1903
+ }
1904
+ return providerFromModel(model);
1905
+ }
1906
+ }
1907
+ function providerFromModel(model) {
1908
+ const slash = model?.indexOf("/") ?? -1;
1909
+ if (!model || slash <= 0) {
1910
+ return void 0;
1911
+ }
1912
+ return model.slice(0, slash);
1913
+ }
1914
+ function readThreadInfo(db) {
1915
+ const info = /* @__PURE__ */ new Map();
1916
+ readProjectionThreadModels(db, info);
1917
+ readProjectionThreadProviders(db, info);
1918
+ return info;
1919
+ }
1920
+ function readProjectionThreadModels(db, info) {
1921
+ if (!tableExists(db, "projection_threads")) {
1922
+ return;
1923
+ }
1924
+ const columns = tableColumns(db, "projection_threads");
1925
+ if (!columns.has("thread_id")) {
1926
+ return;
1927
+ }
1928
+ try {
1929
+ if (columns.has("model_selection_json")) {
1930
+ const rows = db.prepare("SELECT thread_id, model_selection_json FROM projection_threads").all();
1931
+ for (const row of rows) {
1932
+ const threadId = stringValue6(row.thread_id);
1933
+ const modelSelection = asRecord6(parseJson(row.model_selection_json));
1934
+ if (!threadId || !modelSelection) {
1935
+ continue;
1936
+ }
1937
+ const entry = info.get(threadId) ?? {};
1938
+ entry.model = stringValue6(modelSelection["model"]) ?? entry.model;
1939
+ entry.provider = stringValue6(modelSelection["provider"]) ?? stringValue6(modelSelection["instanceId"]) ?? entry.provider;
1940
+ info.set(threadId, entry);
1941
+ }
1942
+ return;
1943
+ }
1944
+ if (columns.has("model")) {
1945
+ const rows = db.prepare("SELECT thread_id, model FROM projection_threads").all();
1946
+ for (const row of rows) {
1947
+ const threadId = stringValue6(row.thread_id);
1948
+ const model = stringValue6(row.model);
1949
+ if (threadId && model) {
1950
+ info.set(threadId, { ...info.get(threadId), model });
1951
+ }
1952
+ }
1953
+ }
1954
+ } catch {
1955
+ return;
1956
+ }
1957
+ }
1958
+ function readProjectionThreadProviders(db, info) {
1959
+ if (!hasColumns(db, "projection_thread_sessions", ["thread_id", "provider_name"])) {
1960
+ return;
1961
+ }
1962
+ try {
1963
+ const rows = db.prepare("SELECT thread_id, provider_name FROM projection_thread_sessions").all();
1964
+ for (const row of rows) {
1965
+ const threadId = stringValue6(row.thread_id);
1966
+ const provider = stringValue6(row.provider_name);
1967
+ if (!threadId || !provider) {
1968
+ continue;
1969
+ }
1970
+ info.set(threadId, { ...info.get(threadId), provider });
1971
+ }
1972
+ } catch {
1973
+ return;
1974
+ }
1975
+ }
1976
+ function parseUsageSnapshot(value) {
1977
+ const usage2 = asRecord6(value);
1978
+ if (!usage2) {
1979
+ return null;
1980
+ }
1981
+ const lastInputTokens = tokenValue(usage2["lastInputTokens"] ?? usage2["last_input_tokens"]);
1982
+ const lastCachedInputTokens = tokenValue(
1983
+ usage2["lastCachedInputTokens"] ?? usage2["last_cached_input_tokens"]
1984
+ );
1985
+ const lastOutputTokens = tokenValue(usage2["lastOutputTokens"] ?? usage2["last_output_tokens"]);
1986
+ const lastReasoningOutputTokens = tokenValue(
1987
+ usage2["lastReasoningOutputTokens"] ?? usage2["last_reasoning_output_tokens"]
1988
+ );
1989
+ const hasLastDetails = [
1990
+ lastInputTokens,
1991
+ lastCachedInputTokens,
1992
+ lastOutputTokens,
1993
+ lastReasoningOutputTokens
1994
+ ].some((token) => token !== void 0);
1995
+ if (hasLastDetails) {
1996
+ return splitTokenUsage({
1997
+ inputTokens: lastInputTokens ?? 0,
1998
+ cachedInputTokens: lastCachedInputTokens ?? 0,
1999
+ outputTokens: lastOutputTokens ?? 0,
2000
+ reasoningOutputTokens: lastReasoningOutputTokens ?? 0
2001
+ });
2002
+ }
2003
+ const inputTokens = tokenValue(usage2["inputTokens"] ?? usage2["input_tokens"]);
2004
+ const cachedInputTokens = tokenValue(usage2["cachedInputTokens"] ?? usage2["cached_input_tokens"]);
2005
+ const outputTokens = tokenValue(usage2["outputTokens"] ?? usage2["output_tokens"]);
2006
+ const reasoningOutputTokens = tokenValue(
2007
+ usage2["reasoningOutputTokens"] ?? usage2["reasoning_output_tokens"]
2008
+ );
2009
+ const hasSnapshotDetails = [
2010
+ inputTokens,
2011
+ cachedInputTokens,
2012
+ outputTokens,
2013
+ reasoningOutputTokens
2014
+ ].some((token) => token !== void 0);
2015
+ if (!hasSnapshotDetails) {
2016
+ return null;
2017
+ }
2018
+ return splitTokenUsage({
2019
+ inputTokens: inputTokens ?? 0,
2020
+ cachedInputTokens: cachedInputTokens ?? 0,
2021
+ outputTokens: outputTokens ?? 0,
2022
+ reasoningOutputTokens: reasoningOutputTokens ?? 0
2023
+ });
2024
+ }
2025
+ function splitTokenUsage(input) {
2026
+ const reasoningTokens = Math.min(input.reasoningOutputTokens, input.outputTokens);
2027
+ return {
2028
+ inputTokens: Math.max(input.inputTokens - input.cachedInputTokens, 0),
2029
+ outputTokens: Math.max(input.outputTokens - reasoningTokens, 0),
2030
+ reasoningTokens,
2031
+ cacheReadTokens: input.cachedInputTokens,
2032
+ cacheWriteTokens: 0
2033
+ };
2034
+ }
2035
+ function hasBillableUsage2(usage2) {
2036
+ return usage2.inputTokens + usage2.outputTokens + usage2.reasoningTokens + usage2.cacheReadTokens + usage2.cacheWriteTokens > 0;
2037
+ }
2038
+ function t3UsageDedupeKey(input) {
2039
+ return JSON.stringify([
2040
+ input.scope,
2041
+ input.threadId ?? "",
2042
+ input.turnId ?? "",
2043
+ input.provider ?? "",
2044
+ input.model ?? "",
2045
+ input.usage.inputTokens,
2046
+ input.usage.outputTokens,
2047
+ input.usage.reasoningTokens,
2048
+ input.usage.cacheReadTokens,
2049
+ input.usage.cacheWriteTokens
2050
+ ]);
2051
+ }
2052
+ function hasColumns(db, table, requiredColumns) {
2053
+ const columns = tableColumns(db, table);
2054
+ return requiredColumns.every((column) => columns.has(column));
2055
+ }
2056
+ function tableExists(db, table) {
2057
+ try {
2058
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1").get(table);
2059
+ return Boolean(row);
2060
+ } catch {
2061
+ return false;
2062
+ }
2063
+ }
2064
+ function tableColumns(db, table) {
2065
+ if (!tableExists(db, table)) {
2066
+ return /* @__PURE__ */ new Set();
2067
+ }
2068
+ try {
2069
+ const rows = db.prepare(`PRAGMA table_info("${table}")`).all();
2070
+ return new Set(rows.flatMap((row) => stringValue6(row.name) ?? []));
2071
+ } catch {
2072
+ return /* @__PURE__ */ new Set();
2073
+ }
2074
+ }
2075
+ function parseJson(value) {
2076
+ if (typeof value !== "string") {
2077
+ return null;
2078
+ }
2079
+ try {
2080
+ return JSON.parse(value);
2081
+ } catch {
2082
+ return null;
2083
+ }
2084
+ }
2085
+ function tokenValue(value) {
2086
+ if (typeof value !== "number" || !Number.isFinite(value)) {
2087
+ return void 0;
2088
+ }
2089
+ return Math.max(Math.round(value), 0);
2090
+ }
2091
+ function uniqueStrings2(values) {
2092
+ const seen = /* @__PURE__ */ new Set();
2093
+ const unique = [];
2094
+ for (const value of values) {
2095
+ if (!value || seen.has(value)) {
2096
+ continue;
2097
+ }
2098
+ seen.add(value);
2099
+ unique.push(value);
2100
+ }
2101
+ return unique;
2102
+ }
2103
+ function stringValue6(value) {
2104
+ return typeof value === "string" && value.trim() ? value : void 0;
2105
+ }
2106
+ function asRecord6(value) {
2107
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2108
+ return null;
2109
+ }
2110
+ return value;
2111
+ }
2112
+
2113
+ // src/adapters/zed.ts
2114
+ import { readdir as readdir7, readFile as readFile3 } from "node:fs/promises";
2115
+ import { existsSync as existsSync5 } from "node:fs";
2116
+ import { homedir as homedir9 } from "node:os";
2117
+ import { join as join9 } from "node:path";
1697
2118
  function getZedPaths() {
1698
2119
  if (process.platform === "darwin") {
1699
- const base2 = join8(homedir8(), "Library", "Application Support", "Zed");
2120
+ const base2 = join9(homedir9(), "Library", "Application Support", "Zed");
1700
2121
  return {
1701
- conversations: join8(base2, "conversations"),
1702
- db: join8(base2, "db")
2122
+ conversations: join9(base2, "conversations"),
2123
+ db: join9(base2, "db")
1703
2124
  };
1704
2125
  }
1705
- const base = join8(process.env["XDG_DATA_HOME"] ?? join8(homedir8(), ".local", "share"), "zed");
2126
+ const base = join9(process.env["XDG_DATA_HOME"] ?? join9(homedir9(), ".local", "share"), "zed");
1706
2127
  return {
1707
- conversations: join8(base, "conversations"),
1708
- db: join8(base, "db")
2128
+ conversations: join9(base, "conversations"),
2129
+ db: join9(base, "db")
1709
2130
  };
1710
2131
  }
1711
2132
  function zedAdapter() {
@@ -1719,7 +2140,7 @@ function zedAdapter() {
1719
2140
  };
1720
2141
  }
1721
2142
  async function* parseTextThreads(dir, _options) {
1722
- if (!existsSync4(dir)) {
2143
+ if (!existsSync5(dir)) {
1723
2144
  return;
1724
2145
  }
1725
2146
  let files;
@@ -1730,7 +2151,7 @@ async function* parseTextThreads(dir, _options) {
1730
2151
  }
1731
2152
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
1732
2153
  for (const file of jsonFiles) {
1733
- const filePath = join8(dir, file);
2154
+ const filePath = join9(dir, file);
1734
2155
  const session = file.replace(".json", "");
1735
2156
  try {
1736
2157
  const raw = await readFile3(filePath, "utf-8");
@@ -1756,7 +2177,7 @@ async function* parseTextThreads(dir, _options) {
1756
2177
  }
1757
2178
  }
1758
2179
  async function* parseAgentThreads(dbDir, _options) {
1759
- if (!existsSync4(dbDir)) {
2180
+ if (!existsSync5(dbDir)) {
1760
2181
  return;
1761
2182
  }
1762
2183
  let dbFiles;
@@ -1777,7 +2198,7 @@ async function* parseAgentThreads(dbDir, _options) {
1777
2198
  return;
1778
2199
  }
1779
2200
  for (const dbFile of dbFiles) {
1780
- const dbPath = join8(dbDir, dbFile);
2201
+ const dbPath = join9(dbDir, dbFile);
1781
2202
  let db;
1782
2203
  try {
1783
2204
  db = new Database(dbPath, {
@@ -1828,6 +2249,7 @@ var ADAPTERS = {
1828
2249
  amp: ampAdapter,
1829
2250
  cline: clineAdapter,
1830
2251
  pi: piAdapter,
2252
+ t3code: t3codeAdapter,
1831
2253
  zed: zedAdapter
1832
2254
  };
1833
2255
  function createAdapter(name) {
@@ -1997,8 +2419,8 @@ function runPattern(_originalText, searchText, matches, seen) {
1997
2419
 
1998
2420
  // src/pricing/index.ts
1999
2421
  import { mkdir, readFile as readFile4, writeFile } from "node:fs/promises";
2000
- import { homedir as homedir9 } from "node:os";
2001
- import { dirname, join as join9 } from "node:path";
2422
+ import { homedir as homedir10 } from "node:os";
2423
+ import { dirname, join as join10 } from "node:path";
2002
2424
  var MODELS_DEV_URL = "https://models.dev/api.json";
2003
2425
  var CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
2004
2426
  var FETCH_TIMEOUT_MS = 2e3;
@@ -2098,16 +2520,16 @@ async function summarizeUsage(records, pricing) {
2098
2520
  }
2099
2521
  function getPricingCachePath() {
2100
2522
  if (process.env["XDG_CACHE_HOME"]) {
2101
- return join9(process.env["XDG_CACHE_HOME"], "devrage", "models.dev.json");
2523
+ return join10(process.env["XDG_CACHE_HOME"], "devrage", "models.dev.json");
2102
2524
  }
2103
2525
  if (process.platform === "darwin") {
2104
- return join9(homedir9(), "Library", "Caches", "devrage", "models.dev.json");
2526
+ return join10(homedir10(), "Library", "Caches", "devrage", "models.dev.json");
2105
2527
  }
2106
2528
  if (process.platform === "win32") {
2107
- const localAppData = process.env["LOCALAPPDATA"] ?? join9(homedir9(), "AppData", "Local");
2108
- return join9(localAppData, "devrage", "models.dev.json");
2529
+ const localAppData = process.env["LOCALAPPDATA"] ?? join10(homedir10(), "AppData", "Local");
2530
+ return join10(localAppData, "devrage", "models.dev.json");
2109
2531
  }
2110
- return join9(homedir9(), ".cache", "devrage", "models.dev.json");
2532
+ return join10(homedir10(), ".cache", "devrage", "models.dev.json");
2111
2533
  }
2112
2534
  function createCostAccumulator() {
2113
2535
  return {
@@ -2216,7 +2638,7 @@ async function readPricingCache(cachePath) {
2216
2638
  try {
2217
2639
  const raw = await readFile4(cachePath, "utf-8");
2218
2640
  const parsed = JSON.parse(raw);
2219
- const cache = asRecord6(parsed);
2641
+ const cache = asRecord7(parsed);
2220
2642
  if (cache?.["source"] !== "models.dev" || cache["schemaVersion"] !== 1 || typeof cache["fetchedAt"] !== "string" || !isModelsDevCatalog(cache["catalog"])) {
2221
2643
  return null;
2222
2644
  }
@@ -2357,10 +2779,10 @@ function inferProvider(model) {
2357
2779
  return void 0;
2358
2780
  }
2359
2781
  function getCatalogRates(catalog, provider, model) {
2360
- const root = asRecord6(catalog);
2361
- const providerEntry = asRecord6(root?.[provider]);
2362
- const models = asRecord6(providerEntry?.["models"]);
2363
- const modelEntry = asRecord6(models?.[model]);
2782
+ const root = asRecord7(catalog);
2783
+ const providerEntry = asRecord7(root?.[provider]);
2784
+ const models = asRecord7(providerEntry?.["models"]);
2785
+ const modelEntry = asRecord7(models?.[model]);
2364
2786
  return toRateTable(modelEntry?.["cost"]);
2365
2787
  }
2366
2788
  function selectContextRates(rates, record) {
@@ -2368,8 +2790,8 @@ function selectContextRates(rates, record) {
2368
2790
  let selected = rates;
2369
2791
  let selectedSize = 0;
2370
2792
  for (const tier of rates.tiers ?? []) {
2371
- const tierRecord = asRecord6(tier);
2372
- const tierInfo = asRecord6(tierRecord?.["tier"]);
2793
+ const tierRecord = asRecord7(tier);
2794
+ const tierInfo = asRecord7(tierRecord?.["tier"]);
2373
2795
  const size = typeof tierInfo?.["size"] === "number" ? tierInfo["size"] : 0;
2374
2796
  if (tierInfo?.["type"] !== "context" || contextTokens < size || size < selectedSize) {
2375
2797
  continue;
@@ -2386,7 +2808,7 @@ function selectContextRates(rates, record) {
2386
2808
  return selected;
2387
2809
  }
2388
2810
  function toRateTable(value) {
2389
- const record = asRecord6(value);
2811
+ const record = asRecord7(value);
2390
2812
  if (!record) {
2391
2813
  return null;
2392
2814
  }
@@ -2420,10 +2842,10 @@ function numberValue6(value) {
2420
2842
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2421
2843
  }
2422
2844
  function isModelsDevCatalog(value) {
2423
- const catalog = asRecord6(value);
2424
- const openai = asRecord6(catalog?.["openai"]);
2425
- const anthropic = asRecord6(catalog?.["anthropic"]);
2426
- return Boolean(asRecord6(openai?.["models"]) || asRecord6(anthropic?.["models"]));
2845
+ const catalog = asRecord7(value);
2846
+ const openai = asRecord7(catalog?.["openai"]);
2847
+ const anthropic = asRecord7(catalog?.["anthropic"]);
2848
+ return Boolean(asRecord7(openai?.["models"]) || asRecord7(anthropic?.["models"]));
2427
2849
  }
2428
2850
  function isFresh(fetchedAt, ttlMs) {
2429
2851
  const fetchedTime = new Date(fetchedAt).getTime();
@@ -2432,7 +2854,7 @@ function isFresh(fetchedAt, ttlMs) {
2432
2854
  function mergePricingSource(left, right) {
2433
2855
  return left === right ? left : "mixed";
2434
2856
  }
2435
- function asRecord6(value) {
2857
+ function asRecord7(value) {
2436
2858
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2437
2859
  return null;
2438
2860
  }
@@ -2537,7 +2959,7 @@ function parseArgs(args) {
2537
2959
  console.log(`devrage scan \u2014 scan sessions for profanity
2538
2960
 
2539
2961
  Options:
2540
- --agent, -a <name> Scan only a specific agent (claude, codex, cursor, opencode, amp, cline, pi, zed)
2962
+ --agent, -a <name> Scan only a specific agent (claude, codex, cursor, opencode, amp, cline, pi, t3code, zed)
2541
2963
  --since, -s <date> Only scan messages after this date (ISO 8601)
2542
2964
  --day, --days [n] Only scan the last n days (default: 1)
2543
2965
  --week Only scan the last 7 days
@@ -2578,7 +3000,7 @@ Usage:
2578
3000
  devrage cost [options]
2579
3001
 
2580
3002
  Options:
2581
- --agent, -a <name> Show only a specific agent (claude, codex, cursor, opencode, amp, pi)
3003
+ --agent, -a <name> Show only a specific agent (claude, codex, cursor, opencode, amp, pi, t3code)
2582
3004
  --refresh-prices Refresh models.dev pricing before estimating cost
2583
3005
  --since, -s <date> Only include usage after this date (ISO 8601)
2584
3006
  --day, --days [n] Only include the last n days (default: 1)
@@ -2792,7 +3214,7 @@ function printCostCommandUnavailable(options) {
2792
3214
  }
2793
3215
  async function writeCostHtmlReport(totals, options) {
2794
3216
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
2795
- const reportPath = join10(
3217
+ const reportPath = join11(
2796
3218
  dirname2(getPricingCachePath()),
2797
3219
  `cost-report-${safeTimestamp(generatedAt)}.html`
2798
3220
  );
@@ -3245,7 +3667,7 @@ async function main() {
3245
3667
  process.exit(0);
3246
3668
  }
3247
3669
  if (command === "--version") {
3248
- console.log("0.5.5");
3670
+ console.log("0.5.6");
3249
3671
  process.exit(0);
3250
3672
  }
3251
3673
  const parsed = parseCommand(args);