storyforge 0.4.15 → 0.4.17

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.
@@ -69,7 +69,7 @@ function listJsonlFiles(rootDir) {
69
69
  }
70
70
  return out;
71
71
  }
72
- function parseClaudeFile(file) {
72
+ function parseClaudeFile(file, seenMessageIds) {
73
73
  let content;
74
74
  try {
75
75
  content = fs.readFileSync(file, "utf-8");
@@ -87,6 +87,11 @@ function parseClaudeFile(file) {
87
87
  }
88
88
  const usage = obj?.message?.usage;
89
89
  if (!usage) continue;
90
+ const messageId = obj?.message?.id;
91
+ if (messageId) {
92
+ if (seenMessageIds.has(messageId)) continue;
93
+ seenMessageIds.add(messageId);
94
+ }
90
95
  const model = String(obj?.message?.model ?? "").toLowerCase();
91
96
  if (!model) continue;
92
97
  const tsRaw = obj?.timestamp ?? obj?.ts;
@@ -153,6 +158,7 @@ function startOfTodayMs(now = Date.now()) {
153
158
  d.setHours(0, 0, 0, 0);
154
159
  return d.getTime();
155
160
  }
161
+ var CLI_USAGE_PARSER_VERSION = 2;
156
162
  async function gatherCliUsage() {
157
163
  const home = os.homedir();
158
164
  const claudeDir = path.join(home, ".claude", "projects");
@@ -161,8 +167,9 @@ async function gatherCliUsage() {
161
167
  const codexFiles = listJsonlFiles(codexDir);
162
168
  let skipped = 0;
163
169
  const lines = [];
170
+ const seenMessageIds = /* @__PURE__ */ new Set();
164
171
  for (const f of claudeFiles) {
165
- const parsed = parseClaudeFile(f);
172
+ const parsed = parseClaudeFile(f, seenMessageIds);
166
173
  if (parsed.length === 0) skipped++;
167
174
  else lines.push(...parsed);
168
175
  }
@@ -233,5 +240,6 @@ async function gatherCliUsage() {
233
240
  };
234
241
  }
235
242
  export {
243
+ CLI_USAGE_PARSER_VERSION,
236
244
  gatherCliUsage
237
245
  };
package/dist/index.js CHANGED
@@ -866,8 +866,16 @@ async function devCommand(options) {
866
866
  const pathname = url.pathname;
867
867
  if (pathname === "/api/cli-usage") {
868
868
  try {
869
- const { gatherCliUsage } = await import("./cli-usage-OFFQXJQN.js");
869
+ const { gatherCliUsage, CLI_USAGE_PARSER_VERSION } = await import("./cli-usage-LUATWFYP.js");
870
+ const g = global;
871
+ const now = Date.now();
872
+ if (g.__forgeCliUsageCache && g.__forgeCliUsageCache.ver === CLI_USAGE_PARSER_VERSION && now - g.__forgeCliUsageCache.at < 3e4) {
873
+ res.writeHead(200, { ...CORS_HEADERS, "Content-Type": "application/json" });
874
+ res.end(JSON.stringify(g.__forgeCliUsageCache.data));
875
+ return;
876
+ }
870
877
  const report = await gatherCliUsage();
878
+ g.__forgeCliUsageCache = { at: now, ver: CLI_USAGE_PARSER_VERSION, data: report };
871
879
  res.writeHead(200, { ...CORS_HEADERS, "Content-Type": "application/json" });
872
880
  res.end(JSON.stringify(report));
873
881
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyforge",
3
- "version": "0.4.15",
3
+ "version": "0.4.17",
4
4
  "description": "StoryForge — local bridge for the Forge video production web app. Zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "bin": {