storyforge 0.4.20 → 0.4.21

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.
@@ -75,6 +75,47 @@ function listJsonlFiles(rootDir) {
75
75
  }
76
76
  return out;
77
77
  }
78
+ function parseClaudeFileDirect(file, seen) {
79
+ let content;
80
+ try {
81
+ content = fs.readFileSync(file, "utf-8");
82
+ } catch {
83
+ return [];
84
+ }
85
+ const out = [];
86
+ for (const raw of content.split(/\r?\n/)) {
87
+ if (!raw.trim()) continue;
88
+ let obj;
89
+ try {
90
+ obj = JSON.parse(raw);
91
+ } catch {
92
+ continue;
93
+ }
94
+ const usage = obj?.message?.usage;
95
+ if (!usage) continue;
96
+ const messageId = obj?.message?.id;
97
+ if (messageId) {
98
+ if (seen.has(messageId)) continue;
99
+ seen.add(messageId);
100
+ }
101
+ const model = String(obj?.message?.model ?? "").toLowerCase();
102
+ if (!model) continue;
103
+ const tsRaw = obj?.timestamp ?? obj?.ts;
104
+ const ts = tsRaw ? new Date(tsRaw).getTime() : Date.now();
105
+ if (!Number.isFinite(ts)) continue;
106
+ out.push({
107
+ ts,
108
+ model,
109
+ usage: {
110
+ input: usage.input_tokens ?? 0,
111
+ cacheRead: usage.cache_read_input_tokens ?? 0,
112
+ cacheCreate: usage.cache_creation_input_tokens ?? 0,
113
+ output: usage.output_tokens ?? 0
114
+ }
115
+ });
116
+ }
117
+ return out;
118
+ }
78
119
  function parseCodexFile(file) {
79
120
  let content;
80
121
  try {
@@ -115,11 +156,17 @@ function parseCodexFile(file) {
115
156
  return out;
116
157
  }
117
158
  async function gatherCliUsage() {
159
+ const CCUSAGE_TIMEOUT_MS = 8e3;
118
160
  const claudeEntries = [];
119
161
  let claudeFileCount = 0;
120
162
  let claudeError;
121
163
  try {
122
- const blocks = await loadSessionBlockData();
164
+ const blocks = await Promise.race([
165
+ loadSessionBlockData(),
166
+ new Promise(
167
+ (_, reject) => setTimeout(() => reject(new Error(`ccusage timed out after ${CCUSAGE_TIMEOUT_MS}ms (LiteLLM pricing fetch likely blocked)`)), CCUSAGE_TIMEOUT_MS)
168
+ )
169
+ ]);
123
170
  for (const block of blocks) {
124
171
  const entries = block.entries ?? [];
125
172
  for (const e of entries) {
@@ -142,8 +189,15 @@ async function gatherCliUsage() {
142
189
  }
143
190
  claudeFileCount = listJsonlFiles(path.join(os.homedir(), ".claude", "projects")).length;
144
191
  } catch (err) {
145
- claudeError = err.message ?? String(err);
146
- console.error("[cli-usage] ccusage loader failed:", claudeError);
192
+ claudeError = `ccusage unavailable, using fallback parser (${err.message?.slice(0, 80) ?? "unknown"})`;
193
+ console.error("[cli-usage] ccusage loader failed, falling back:", claudeError);
194
+ const claudeDir = path.join(os.homedir(), ".claude", "projects");
195
+ const claudeFiles = listJsonlFiles(claudeDir);
196
+ claudeFileCount = claudeFiles.length;
197
+ const seen = /* @__PURE__ */ new Set();
198
+ for (const f of claudeFiles) {
199
+ claudeEntries.push(...parseClaudeFileDirect(f, seen));
200
+ }
147
201
  }
148
202
  const codexDir = path.join(os.homedir(), ".codex", "sessions");
149
203
  const codexFiles = listJsonlFiles(codexDir);
package/dist/index.js CHANGED
@@ -841,7 +841,7 @@ async function devCommand(options) {
841
841
  }
842
842
  void (async () => {
843
843
  try {
844
- const { gatherCliUsage, CLI_USAGE_PARSER_VERSION } = await import("./cli-usage-7QODAG26.js");
844
+ const { gatherCliUsage, CLI_USAGE_PARSER_VERSION } = await import("./cli-usage-P3LK7WEE.js");
845
845
  const report = await gatherCliUsage();
846
846
  const g = global;
847
847
  g.__forgeCliUsageCache = { at: Date.now(), ver: CLI_USAGE_PARSER_VERSION, data: report };
@@ -877,7 +877,7 @@ async function devCommand(options) {
877
877
  const pathname = url.pathname;
878
878
  if (pathname === "/api/cli-usage") {
879
879
  try {
880
- const { gatherCliUsage, CLI_USAGE_PARSER_VERSION } = await import("./cli-usage-7QODAG26.js");
880
+ const { gatherCliUsage, CLI_USAGE_PARSER_VERSION } = await import("./cli-usage-P3LK7WEE.js");
881
881
  const g = global;
882
882
  const now = Date.now();
883
883
  if (g.__forgeCliUsageCache && g.__forgeCliUsageCache.ver === CLI_USAGE_PARSER_VERSION && now - g.__forgeCliUsageCache.at < 3e4) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyforge",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "description": "StoryForge — local bridge for the Forge video production web app. Zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "bin": {