token-furnace 0.1.1 → 0.1.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.
package/dist/cli.d.ts CHANGED
@@ -11,8 +11,11 @@ interface SessionRepository {
11
11
  * gone from disk are dropped. The repository keeps the rest as they are.
12
12
  */
13
13
  changed(files: SourceFile[]): SourceFile[];
14
- /** Every session held, in scan order: the order the analysis ranks ties by. */
15
- all(): SessionInput[];
14
+ /**
15
+ * Every session held, in scan order: the order the analysis ranks ties by. With a cutoff, each
16
+ * session keeps only the turns at or after it, so the report reads no turn the analysis skips.
17
+ */
18
+ all(cutoff?: Date): SessionInput[];
16
19
  /** Store one parsed file, with its size: the two fields that say when to reparse it. */
17
20
  save(session: SessionInput, size: number): void;
18
21
  }
package/dist/cli.js CHANGED
@@ -33,8 +33,12 @@ var MemorySessionRepository = class {
33
33
  for (const path of [...this.sessions.keys()]) if (!seen.has(path)) this.sessions.delete(path);
34
34
  return out;
35
35
  }
36
- all() {
37
- return [...this.sessions.values()].map((held) => held.session);
36
+ all(cutoff) {
37
+ const ms = cutoff?.getTime();
38
+ const filtered = ms !== void 0 && !Number.isNaN(ms);
39
+ return [...this.sessions.values()].map(
40
+ (held) => filtered ? { ...held.session, turns: held.session.turns.filter((turn) => turn.when.getTime() >= ms) } : held.session
41
+ );
38
42
  }
39
43
  save(session, size) {
40
44
  this.sessions.set(session.path, { session, size });
@@ -85,6 +89,7 @@ var SqliteSessionRepository = class {
85
89
  stmtInsertTurn = null;
86
90
  stmtAllFiles = null;
87
91
  stmtAllTurns = null;
92
+ stmtAllTurnsSince = null;
88
93
  constructor(dbPath) {
89
94
  this.dbPath = dbPath;
90
95
  this.ensureDb();
@@ -102,6 +107,9 @@ var SqliteSessionRepository = class {
102
107
  this.stmtAllTurns = this.db.prepare(
103
108
  "SELECT path, seq, project, session, model, at, base_in, cache_read, cache_write, output, tools, reads, wakes FROM turns ORDER BY path, seq;"
104
109
  );
110
+ this.stmtAllTurnsSince = this.db.prepare(
111
+ "SELECT path, seq, project, session, model, at, base_in, cache_read, cache_write, output, tools, reads, wakes FROM turns WHERE at >= ? ORDER BY path, seq;"
112
+ );
105
113
  }
106
114
  openDb(path) {
107
115
  const db = new DatabaseSync(path);
@@ -185,11 +193,13 @@ var SqliteSessionRepository = class {
185
193
  }
186
194
  return out;
187
195
  }
188
- all() {
196
+ all(cutoff) {
189
197
  this.ensureDb();
190
198
  if (!this.db || !this.stmtAllFiles || !this.stmtAllTurns) return [];
199
+ const ms = cutoff?.getTime();
200
+ const filtered = ms !== void 0 && !Number.isNaN(ms);
191
201
  const fileRows = this.stmtAllFiles.all();
192
- const turnRows = this.stmtAllTurns.iterate();
202
+ const turnRows = filtered && this.stmtAllTurnsSince ? this.stmtAllTurnsSince.iterate(ms) : this.stmtAllTurns.iterate();
193
203
  const turnsByPath = /* @__PURE__ */ new Map();
194
204
  for (const r of turnRows) {
195
205
  let list = turnsByPath.get(r.path);
@@ -1608,12 +1618,13 @@ var ReportService = class {
1608
1618
  const now = opts.now ?? /* @__PURE__ */ new Date();
1609
1619
  const live = this.live(source);
1610
1620
  if (!live.length) return null;
1621
+ const cutoff = new Date(now.getTime() - days * 864e5);
1611
1622
  return buildReport({
1612
1623
  now,
1613
1624
  days,
1614
1625
  weeklyBudget: opts.weeklyBudget,
1615
1626
  live,
1616
- sessions: this.repo.all().sort((a, b) => (this.order.get(a.path) ?? Infinity) - (this.order.get(b.path) ?? Infinity)),
1627
+ sessions: this.repo.all(cutoff).sort((a, b) => (this.order.get(a.path) ?? Infinity) - (this.order.get(b.path) ?? Infinity)),
1617
1628
  config: this.readConfig(live),
1618
1629
  planWeek: opts.planWeek ?? null,
1619
1630
  codexWeek: live.includes("codex") ? this.sources.find((s) => s.name === "codex")?.week?.(now) ?? null : null
@@ -1983,6 +1994,30 @@ LoopbackHostOnly = __decorateClass([
1983
1994
  Middleware({ type: "before", priority: 100 })
1984
1995
  ], LoopbackHostOnly);
1985
1996
  function reportController(app, options, page) {
1997
+ let quota = null;
1998
+ let fetching = null;
1999
+ const plan = (now) => quota && quota.resets > now ? Promise.resolve(quota) : planWeek(app.env, now, false);
2000
+ const refresh = () => {
2001
+ fetching ??= planWeek(app.env, app.now(), true).then((week) => {
2002
+ if (week && week.resets > app.now()) quota = week;
2003
+ }).catch(() => {
2004
+ }).finally(() => {
2005
+ fetching = null;
2006
+ });
2007
+ };
2008
+ async function build(query) {
2009
+ const now = app.now();
2010
+ const days = positiveInt(query.days, options.days);
2011
+ const source = query.source === "claude" || query.source === "codex" ? query.source : options.source;
2012
+ rescan(app);
2013
+ const report = app.reports.getRaw(days, source, {
2014
+ weeklyBudget: options.weeklyBudget,
2015
+ planWeek: await plan(now),
2016
+ now
2017
+ });
2018
+ if (options.planUsage) setImmediate(refresh);
2019
+ return report;
2020
+ }
1986
2021
  let ReportController = class {
1987
2022
  status(res) {
1988
2023
  if (!app.reports.hasScanned) warm(app);
@@ -1993,7 +2028,7 @@ function reportController(app, options, page) {
1993
2028
  warm(app);
1994
2029
  return res.status(202).json({ status: "scanning", progress: app.reports.progress });
1995
2030
  }
1996
- const report = await build(app, options, req.query);
2031
+ const report = await build(req.query);
1997
2032
  return res.type("application/json").send(JSON.stringify(report ? reportView(report, app.now()) : null));
1998
2033
  }
1999
2034
  dashboard(res) {
@@ -2004,7 +2039,7 @@ function reportController(app, options, page) {
2004
2039
  warm(app);
2005
2040
  return res.type("text/html").send(loadingPage());
2006
2041
  }
2007
- const report = await build(app, options, req.query);
2042
+ const report = await build(req.query);
2008
2043
  return res.type("text/html").send(report ? renderHtml(report, app.now(), page) : "No assistant turns.");
2009
2044
  }
2010
2045
  };
@@ -2037,17 +2072,6 @@ function warm(app) {
2037
2072
  function rescan(app) {
2038
2073
  if (Date.now() - app.reports.progress.updatedAt > 5e3) warm(app);
2039
2074
  }
2040
- async function build(app, options, query) {
2041
- const now = app.now();
2042
- const days = positiveInt(query.days, options.days);
2043
- const source = query.source === "claude" || query.source === "codex" ? query.source : options.source;
2044
- rescan(app);
2045
- return app.reports.getRaw(days, source, {
2046
- weeklyBudget: options.weeklyBudget,
2047
- planWeek: await planWeek(app.env, now, options.planUsage),
2048
- now
2049
- });
2050
- }
2051
2075
  function positiveInt(value, fallback) {
2052
2076
  const parsed = Number.parseInt(String(value ?? ""), 10);
2053
2077
  return parsed > 0 ? parsed : fallback;