squads-cli 0.6.2 → 0.7.0

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.
Files changed (112) hide show
  1. package/README.md +196 -1152
  2. package/dist/auth-YW3UPFSB.js +23 -0
  3. package/dist/autonomy-BWTVDEAT.js +102 -0
  4. package/dist/autonomy-BWTVDEAT.js.map +1 -0
  5. package/dist/chunk-3KCWNZWW.js +401 -0
  6. package/dist/chunk-3KCWNZWW.js.map +1 -0
  7. package/dist/chunk-67RO2HKR.js +174 -0
  8. package/dist/chunk-67RO2HKR.js.map +1 -0
  9. package/dist/chunk-7JVD7RD4.js +275 -0
  10. package/dist/chunk-7JVD7RD4.js.map +1 -0
  11. package/dist/chunk-BODLDQY7.js +452 -0
  12. package/dist/chunk-BODLDQY7.js.map +1 -0
  13. package/dist/chunk-FFFCFZ6A.js +121 -0
  14. package/dist/chunk-FFFCFZ6A.js.map +1 -0
  15. package/dist/chunk-FIWT2NMM.js +165 -0
  16. package/dist/chunk-FIWT2NMM.js.map +1 -0
  17. package/dist/chunk-L6GQCHDF.js +222 -0
  18. package/dist/chunk-L6GQCHDF.js.map +1 -0
  19. package/dist/{chunk-O7UV3FWI.js → chunk-LDM62TIX.js} +2 -2
  20. package/dist/chunk-LDM62TIX.js.map +1 -0
  21. package/dist/chunk-LOA3KWYJ.js +294 -0
  22. package/dist/chunk-LOA3KWYJ.js.map +1 -0
  23. package/dist/chunk-NA45DFXY.js +616 -0
  24. package/dist/chunk-NA45DFXY.js.map +1 -0
  25. package/dist/{chunk-4CMAEQQY.js → chunk-NQN6JPI7.js} +4 -3
  26. package/dist/chunk-NQN6JPI7.js.map +1 -0
  27. package/dist/chunk-OQJHPULO.js +103 -0
  28. package/dist/chunk-OQJHPULO.js.map +1 -0
  29. package/dist/chunk-QHNUMM4V.js +87 -0
  30. package/dist/chunk-QHNUMM4V.js.map +1 -0
  31. package/dist/chunk-RM6BWILN.js +74 -0
  32. package/dist/chunk-RM6BWILN.js.map +1 -0
  33. package/dist/chunk-WBR5J7EX.js +90 -0
  34. package/dist/chunk-WBR5J7EX.js.map +1 -0
  35. package/dist/chunk-Z2UKDBNL.js +162 -0
  36. package/dist/chunk-Z2UKDBNL.js.map +1 -0
  37. package/dist/cli.js +2136 -12600
  38. package/dist/cli.js.map +1 -1
  39. package/dist/context-M2A2DOFV.js +291 -0
  40. package/dist/context-M2A2DOFV.js.map +1 -0
  41. package/dist/context-feed-JMNW4GAM.js +391 -0
  42. package/dist/context-feed-JMNW4GAM.js.map +1 -0
  43. package/dist/cost-N37I4UTA.js +274 -0
  44. package/dist/cost-N37I4UTA.js.map +1 -0
  45. package/dist/create-554W5HNU.js +286 -0
  46. package/dist/create-554W5HNU.js.map +1 -0
  47. package/dist/daemon-XWPQPPPN.js +546 -0
  48. package/dist/daemon-XWPQPPPN.js.map +1 -0
  49. package/dist/dashboard-L7YKVQEB.js +945 -0
  50. package/dist/dashboard-L7YKVQEB.js.map +1 -0
  51. package/dist/dashboard-MFNRLCEE.js +794 -0
  52. package/dist/dashboard-MFNRLCEE.js.map +1 -0
  53. package/dist/doctor-RG75M5RO.js +346 -0
  54. package/dist/doctor-RG75M5RO.js.map +1 -0
  55. package/dist/env-config-KCLDBKYX.js +21 -0
  56. package/dist/exec-JQKBF7BL.js +197 -0
  57. package/dist/exec-JQKBF7BL.js.map +1 -0
  58. package/dist/feedback-KA2UYBZG.js +229 -0
  59. package/dist/feedback-KA2UYBZG.js.map +1 -0
  60. package/dist/github-UQTM5KMS.js +23 -0
  61. package/dist/goal-EOPC5ZCD.js +168 -0
  62. package/dist/goal-EOPC5ZCD.js.map +1 -0
  63. package/dist/health-3FZDOSR5.js +209 -0
  64. package/dist/health-3FZDOSR5.js.map +1 -0
  65. package/dist/history-TFVXJEDH.js +229 -0
  66. package/dist/history-TFVXJEDH.js.map +1 -0
  67. package/dist/index.js +1 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/init-UOWTNMIE.js +747 -0
  70. package/dist/init-UOWTNMIE.js.map +1 -0
  71. package/dist/kpi-2SQ2WCVT.js +413 -0
  72. package/dist/kpi-2SQ2WCVT.js.map +1 -0
  73. package/dist/learn-6ERTERAO.js +269 -0
  74. package/dist/learn-6ERTERAO.js.map +1 -0
  75. package/dist/list-KSOMUBMB.js +92 -0
  76. package/dist/list-KSOMUBMB.js.map +1 -0
  77. package/dist/login-ST6PAXYE.js +155 -0
  78. package/dist/login-ST6PAXYE.js.map +1 -0
  79. package/dist/memory-3CSNKXIL.js +562 -0
  80. package/dist/memory-3CSNKXIL.js.map +1 -0
  81. package/dist/progress-FKG4V2VH.js +202 -0
  82. package/dist/progress-FKG4V2VH.js.map +1 -0
  83. package/dist/providers-66PDCORB.js +65 -0
  84. package/dist/providers-66PDCORB.js.map +1 -0
  85. package/dist/results-2MJFLWEO.js +224 -0
  86. package/dist/results-2MJFLWEO.js.map +1 -0
  87. package/dist/run-72OQLH5A.js +2685 -0
  88. package/dist/run-72OQLH5A.js.map +1 -0
  89. package/dist/session-6H67XPAQ.js +64 -0
  90. package/dist/session-6H67XPAQ.js.map +1 -0
  91. package/dist/{chunk-NHGLXN2F.js → sessions-GVQIMN4W.js} +23 -459
  92. package/dist/sessions-GVQIMN4W.js.map +1 -0
  93. package/dist/{squad-parser-4BI3G4RS.js → squad-parser-CM3HOIWM.js} +2 -2
  94. package/dist/squad-parser-CM3HOIWM.js.map +1 -0
  95. package/dist/stats-ONZI557Q.js +335 -0
  96. package/dist/stats-ONZI557Q.js.map +1 -0
  97. package/dist/status-FYH42FTB.js +346 -0
  98. package/dist/status-FYH42FTB.js.map +1 -0
  99. package/dist/sync-HJZJNXHW.js +800 -0
  100. package/dist/sync-HJZJNXHW.js.map +1 -0
  101. package/dist/update-B4WMUOPO.js +83 -0
  102. package/dist/update-B4WMUOPO.js.map +1 -0
  103. package/dist/{update-ALJKFFM7.js → update-L7FGHN6W.js} +2 -2
  104. package/dist/update-L7FGHN6W.js.map +1 -0
  105. package/package.json +18 -10
  106. package/dist/chunk-4CMAEQQY.js.map +0 -1
  107. package/dist/chunk-NHGLXN2F.js.map +0 -1
  108. package/dist/chunk-O7UV3FWI.js.map +0 -1
  109. package/dist/sessions-6PB7ALCE.js +0 -16
  110. /package/dist/{sessions-6PB7ALCE.js.map → auth-YW3UPFSB.js.map} +0 -0
  111. /package/dist/{squad-parser-4BI3G4RS.js.map → env-config-KCLDBKYX.js.map} +0 -0
  112. /package/dist/{update-ALJKFFM7.js.map → github-UQTM5KMS.js.map} +0 -0
@@ -0,0 +1,616 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ calcCost,
4
+ detectProviderFromModel,
5
+ detectProvidersFromEnv,
6
+ getProviderDisplayName
7
+ } from "./chunk-LOA3KWYJ.js";
8
+
9
+ // src/lib/plan.ts
10
+ function detectPlan() {
11
+ const explicitPlan = process.env.SQUADS_PLAN_TYPE?.toLowerCase();
12
+ if (explicitPlan === "usage") {
13
+ return { plan: "usage", confidence: "explicit", reason: "SQUADS_PLAN_TYPE=usage" };
14
+ }
15
+ if (explicitPlan === "max") {
16
+ return { plan: "max", confidence: "explicit", reason: "SQUADS_PLAN_TYPE=max" };
17
+ }
18
+ const budgetSet = process.env.ANTHROPIC_BUDGET_DAILY || process.env.SQUADS_DAILY_BUDGET;
19
+ if (budgetSet) {
20
+ return { plan: "usage", confidence: "inferred", reason: `Budget set ($${budgetSet}/day)` };
21
+ }
22
+ const tier = parseInt(process.env.ANTHROPIC_TIER || "0", 10);
23
+ if (tier >= 4) {
24
+ return { plan: "max", confidence: "inferred", reason: `Tier ${tier} (high usage)` };
25
+ }
26
+ if (tier >= 1 && tier <= 2) {
27
+ return { plan: "usage", confidence: "inferred", reason: `Tier ${tier} (new user)` };
28
+ }
29
+ return { plan: "unknown", confidence: "inferred", reason: "Not configured" };
30
+ }
31
+ function getPlanType() {
32
+ return detectPlan().plan;
33
+ }
34
+ function isMaxPlan() {
35
+ return getPlanType() === "max";
36
+ }
37
+
38
+ // src/lib/costs.ts
39
+ var DEFAULT_DAILY_BUDGET = 200;
40
+ var DEFAULT_DAILY_CALL_LIMIT = 1e3;
41
+ var BRIDGE_URL = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
42
+ var FETCH_TIMEOUT_MS = 2e3;
43
+ async function fetchWithTimeout(url, options = {}, timeoutMs = FETCH_TIMEOUT_MS) {
44
+ const controller = new AbortController();
45
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
46
+ try {
47
+ const response = await fetch(url, { ...options, signal: controller.signal });
48
+ clearTimeout(timeoutId);
49
+ return response;
50
+ } catch (error) {
51
+ clearTimeout(timeoutId);
52
+ throw error;
53
+ }
54
+ }
55
+ function calcCost2(model, inputTokens, outputTokens) {
56
+ const provider = detectProviderFromModel(model);
57
+ return calcCost(provider, model, inputTokens, outputTokens);
58
+ }
59
+ async function fetchFromBridge(period = "day") {
60
+ try {
61
+ const response = await fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=${period}`, {
62
+ headers: { "Content-Type": "application/json" }
63
+ });
64
+ if (!response.ok) {
65
+ return null;
66
+ }
67
+ const data = await response.json();
68
+ const dailyBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || "") || DEFAULT_DAILY_BUDGET;
69
+ const totalCost = data.totals?.cost_usd || 0;
70
+ const bySquad = (data.by_squad || []).map((s) => ({
71
+ squad: s.squad,
72
+ calls: s.generations || 0,
73
+ inputTokens: s.input_tokens || 0,
74
+ outputTokens: s.output_tokens || 0,
75
+ cachedTokens: s.cached_tokens || 0,
76
+ cost: s.cost_usd || 0,
77
+ models: {}
78
+ }));
79
+ const totalCalls = bySquad.reduce((sum, s) => sum + s.calls, 0);
80
+ const dailyCallLimit = parseFloat(process.env.SQUADS_DAILY_CALL_LIMIT || "") || DEFAULT_DAILY_CALL_LIMIT;
81
+ const totalCachedTokens = bySquad.reduce((sum, s) => sum + s.cachedTokens, 0);
82
+ const totalInputTokens = bySquad.reduce((sum, s) => sum + s.inputTokens, 0);
83
+ const totalAllInput = totalInputTokens + totalCachedTokens;
84
+ const cacheHitRate = totalAllInput > 0 ? totalCachedTokens / totalAllInput * 100 : 0;
85
+ const detectedProviders = detectProvidersFromEnv();
86
+ const byProvider = detectedProviders.map((p) => ({
87
+ provider: p.provider,
88
+ displayName: getProviderDisplayName(p.provider),
89
+ calls: 0,
90
+ // Bridge doesn't track by provider yet
91
+ inputTokens: 0,
92
+ outputTokens: 0,
93
+ cost: p.provider === "anthropic" ? totalCost : 0,
94
+ // Assume all cost is Anthropic for now
95
+ plan: p.plan,
96
+ confidence: p.confidence,
97
+ reason: p.reason
98
+ }));
99
+ return {
100
+ totalCost,
101
+ dailyBudget,
102
+ usedPercent: totalCost / dailyBudget * 100,
103
+ idleBudget: dailyBudget - totalCost,
104
+ totalCalls,
105
+ dailyCallLimit,
106
+ callsPercent: totalCalls / dailyCallLimit * 100,
107
+ totalCachedTokens,
108
+ totalInputTokens,
109
+ cacheHitRate,
110
+ bySquad,
111
+ byProvider,
112
+ source: "postgres"
113
+ };
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+ async function fetchFromLangfuse(limit = 100) {
119
+ const publicKey = process.env.LANGFUSE_PUBLIC_KEY;
120
+ const secretKey = process.env.LANGFUSE_SECRET_KEY;
121
+ const host = process.env.LANGFUSE_HOST || process.env.LANGFUSE_BASE_URL || "https://us.cloud.langfuse.com";
122
+ if (!publicKey || !secretKey) {
123
+ return null;
124
+ }
125
+ try {
126
+ const auth = Buffer.from(`${publicKey}:${secretKey}`).toString("base64");
127
+ const url = `${host}/api/public/observations?limit=${limit}`;
128
+ const response = await fetchWithTimeout(url, {
129
+ headers: {
130
+ Authorization: `Basic ${auth}`,
131
+ "Content-Type": "application/json"
132
+ }
133
+ });
134
+ if (!response.ok) {
135
+ return null;
136
+ }
137
+ const data = await response.json();
138
+ const observations = data.data || [];
139
+ const bySquad = {};
140
+ for (const obs of observations) {
141
+ if (obs.type !== "GENERATION") continue;
142
+ const metadata = obs.metadata || {};
143
+ const squad = metadata.squad || "unknown";
144
+ const model = obs.model || "unknown";
145
+ const usage = obs.usage || {};
146
+ const inputTokens = usage.input || 0;
147
+ const outputTokens = usage.output || 0;
148
+ const cost = calcCost2(model, inputTokens, outputTokens);
149
+ if (!bySquad[squad]) {
150
+ bySquad[squad] = {
151
+ squad,
152
+ calls: 0,
153
+ inputTokens: 0,
154
+ outputTokens: 0,
155
+ cachedTokens: 0,
156
+ cost: 0,
157
+ models: {}
158
+ };
159
+ }
160
+ bySquad[squad].calls += 1;
161
+ bySquad[squad].inputTokens += inputTokens;
162
+ bySquad[squad].outputTokens += outputTokens;
163
+ bySquad[squad].cost += cost;
164
+ bySquad[squad].models[model] = (bySquad[squad].models[model] || 0) + 1;
165
+ }
166
+ const squadList = Object.values(bySquad).sort((a, b) => b.cost - a.cost);
167
+ const totalCost = squadList.reduce((sum, s) => sum + s.cost, 0);
168
+ const dailyBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || "") || DEFAULT_DAILY_BUDGET;
169
+ const totalCalls = squadList.reduce((sum, s) => sum + s.calls, 0);
170
+ const dailyCallLimit = parseFloat(process.env.SQUADS_DAILY_CALL_LIMIT || "") || DEFAULT_DAILY_CALL_LIMIT;
171
+ const totalCachedTokens = squadList.reduce((sum, s) => sum + s.cachedTokens, 0);
172
+ const totalInputTokens = squadList.reduce((sum, s) => sum + s.inputTokens, 0);
173
+ const totalAllInput = totalInputTokens + totalCachedTokens;
174
+ const cacheHitRate = totalAllInput > 0 ? totalCachedTokens / totalAllInput * 100 : 0;
175
+ const providerMap = {};
176
+ for (const obs of observations) {
177
+ if (obs.type !== "GENERATION") continue;
178
+ const model = obs.model || "unknown";
179
+ const provider = detectProviderFromModel(model);
180
+ const usage = obs.usage || {};
181
+ const inputTokens = usage.input || 0;
182
+ const outputTokens = usage.output || 0;
183
+ const cost = calcCost2(model, inputTokens, outputTokens);
184
+ if (!providerMap[provider]) {
185
+ const detection = detectProvidersFromEnv().find((p) => p.provider === provider);
186
+ providerMap[provider] = {
187
+ provider,
188
+ displayName: getProviderDisplayName(provider),
189
+ calls: 0,
190
+ inputTokens: 0,
191
+ outputTokens: 0,
192
+ cost: 0,
193
+ plan: detection?.plan,
194
+ confidence: detection?.confidence,
195
+ reason: detection?.reason
196
+ };
197
+ }
198
+ providerMap[provider].calls += 1;
199
+ providerMap[provider].inputTokens += inputTokens;
200
+ providerMap[provider].outputTokens += outputTokens;
201
+ providerMap[provider].cost += cost;
202
+ }
203
+ const byProvider = Object.values(providerMap).sort((a, b) => b.cost - a.cost);
204
+ return {
205
+ totalCost,
206
+ dailyBudget,
207
+ usedPercent: totalCost / dailyBudget * 100,
208
+ idleBudget: dailyBudget - totalCost,
209
+ totalCalls,
210
+ dailyCallLimit,
211
+ callsPercent: totalCalls / dailyCallLimit * 100,
212
+ totalCachedTokens,
213
+ totalInputTokens,
214
+ cacheHitRate,
215
+ bySquad: squadList,
216
+ byProvider,
217
+ source: "langfuse"
218
+ };
219
+ } catch {
220
+ return null;
221
+ }
222
+ }
223
+ async function fetchCostSummary(limit = 100, period = "day") {
224
+ const bridgeResult = await fetchFromBridge(period);
225
+ if (bridgeResult) {
226
+ return bridgeResult;
227
+ }
228
+ const langfuseResult = await fetchFromLangfuse(limit);
229
+ if (langfuseResult) {
230
+ return langfuseResult;
231
+ }
232
+ const defaultBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || "") || DEFAULT_DAILY_BUDGET;
233
+ const detectedProviders = detectProvidersFromEnv();
234
+ const byProvider = detectedProviders.map((p) => ({
235
+ provider: p.provider,
236
+ displayName: getProviderDisplayName(p.provider),
237
+ calls: 0,
238
+ inputTokens: 0,
239
+ outputTokens: 0,
240
+ cost: 0,
241
+ plan: p.plan,
242
+ confidence: p.confidence,
243
+ reason: p.reason
244
+ }));
245
+ return {
246
+ totalCost: 0,
247
+ dailyBudget: defaultBudget,
248
+ usedPercent: 0,
249
+ idleBudget: defaultBudget,
250
+ totalCalls: 0,
251
+ dailyCallLimit: DEFAULT_DAILY_CALL_LIMIT,
252
+ callsPercent: 0,
253
+ totalCachedTokens: 0,
254
+ totalInputTokens: 0,
255
+ cacheHitRate: 0,
256
+ bySquad: [],
257
+ byProvider,
258
+ source: "none"
259
+ };
260
+ }
261
+ async function fetchBridgeStats() {
262
+ try {
263
+ const [statsResponse, healthResponse, costResponse, weekResponse] = await Promise.all([
264
+ fetchWithTimeout(`${BRIDGE_URL}/stats`, {
265
+ headers: { "Content-Type": "application/json" }
266
+ }),
267
+ fetchWithTimeout(`${BRIDGE_URL}/health`, {
268
+ headers: { "Content-Type": "application/json" }
269
+ }),
270
+ fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=day`, {
271
+ headers: { "Content-Type": "application/json" }
272
+ }),
273
+ fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=week`, {
274
+ headers: { "Content-Type": "application/json" }
275
+ })
276
+ ]);
277
+ if (!statsResponse.ok) {
278
+ return null;
279
+ }
280
+ const [stats, health, costData, weekData] = await Promise.all([
281
+ statsResponse.json(),
282
+ healthResponse.ok ? healthResponse.json() : Promise.resolve({}),
283
+ costResponse.ok ? costResponse.json() : Promise.resolve({}),
284
+ weekResponse.ok ? weekResponse.json() : Promise.resolve({})
285
+ ]);
286
+ return {
287
+ status: stats.status || "unknown",
288
+ source: stats.source || "none",
289
+ today: {
290
+ generations: stats.today?.generations || 0,
291
+ inputTokens: stats.today?.input_tokens || 0,
292
+ outputTokens: stats.today?.output_tokens || 0,
293
+ costUsd: stats.today?.cost_usd || 0
294
+ },
295
+ week: weekData.totals ? {
296
+ generations: weekData.totals.generations || 0,
297
+ inputTokens: weekData.totals.input_tokens || 0,
298
+ outputTokens: weekData.totals.output_tokens || 0,
299
+ costUsd: weekData.totals.cost_usd || 0,
300
+ byModel: (weekData.by_model || []).map((m) => ({
301
+ model: m.model || "unknown",
302
+ generations: m.generations || 0,
303
+ costUsd: m.cost_usd || 0
304
+ }))
305
+ } : void 0,
306
+ budget: {
307
+ daily: stats.budget?.daily || DEFAULT_DAILY_BUDGET,
308
+ used: stats.budget?.used || 0,
309
+ remaining: stats.budget?.remaining || DEFAULT_DAILY_BUDGET,
310
+ usedPct: stats.budget?.used_pct || 0
311
+ },
312
+ bySquad: (stats.by_squad || []).map((s) => ({
313
+ squad: s.squad || "unknown",
314
+ costUsd: s.cost_usd || 0,
315
+ generations: s.generations || 0
316
+ })),
317
+ byModel: (costData.by_model || []).map((m) => ({
318
+ model: m.model || "unknown",
319
+ generations: m.generations || 0,
320
+ costUsd: m.cost_usd || 0
321
+ })),
322
+ health: {
323
+ postgres: health.postgres || "unknown",
324
+ redis: health.redis || "unknown",
325
+ langfuse: health.langfuse || "unknown"
326
+ }
327
+ };
328
+ } catch {
329
+ return null;
330
+ }
331
+ }
332
+ async function fetchQuotaInfo() {
333
+ const bridgeUrl = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
334
+ try {
335
+ const response = await fetch(`${bridgeUrl}/api/autonomy/score`);
336
+ if (!response.ok) return null;
337
+ const data = await response.json();
338
+ return {
339
+ monthlyUsed: data.execution_stats.monthly_used,
340
+ monthlyQuota: data.execution_stats.monthly_quota,
341
+ quotaPct: data.execution_stats.quota_pct,
342
+ autonomyScore: data.overall_score,
343
+ confidenceLevel: data.confidence_level,
344
+ learningCount: data.execution_stats.learning_count
345
+ };
346
+ } catch {
347
+ return null;
348
+ }
349
+ }
350
+ async function fetchRateLimits() {
351
+ try {
352
+ const response = await fetchWithTimeout(`${BRIDGE_URL}/api/rate-limits`, {
353
+ headers: { "Content-Type": "application/json" }
354
+ });
355
+ if (!response.ok) {
356
+ return { limits: {}, source: "none" };
357
+ }
358
+ const data = await response.json();
359
+ const rateLimits = data.rate_limits || {};
360
+ const limits = {};
361
+ for (const [key, value] of Object.entries(rateLimits)) {
362
+ limits[key] = {
363
+ model: value.model || key,
364
+ requestsLimit: value.requests_limit || 0,
365
+ requestsRemaining: value.requests_remaining || 0,
366
+ requestsReset: value.requests_reset,
367
+ tokensLimit: value.tokens_limit || 0,
368
+ tokensRemaining: value.tokens_remaining || 0,
369
+ tokensReset: value.tokens_reset,
370
+ inputTokensLimit: value.input_tokens_limit,
371
+ inputTokensRemaining: value.input_tokens_remaining,
372
+ outputTokensLimit: value.output_tokens_limit,
373
+ outputTokensRemaining: value.output_tokens_remaining,
374
+ capturedAt: value.captured_at || (/* @__PURE__ */ new Date()).toISOString()
375
+ };
376
+ }
377
+ return { limits, source: "proxy" };
378
+ } catch {
379
+ return { limits: {}, source: "none" };
380
+ }
381
+ }
382
+ async function fetchInsights(period = "week") {
383
+ try {
384
+ const response = await fetchWithTimeout(`${BRIDGE_URL}/api/insights?period=${period}`, {
385
+ headers: { "Content-Type": "application/json" }
386
+ });
387
+ if (!response.ok) {
388
+ return {
389
+ period,
390
+ days: period === "day" ? 1 : period === "week" ? 7 : 30,
391
+ taskMetrics: [],
392
+ qualityMetrics: [],
393
+ topTools: [],
394
+ toolFailureRate: 0,
395
+ source: "none"
396
+ };
397
+ }
398
+ const data = await response.json();
399
+ return {
400
+ period: data.period || period,
401
+ days: data.days || 7,
402
+ taskMetrics: (data.task_metrics || []).map((t) => ({
403
+ squad: t.squad,
404
+ tasksTotal: t.tasks_total || 0,
405
+ tasksCompleted: t.tasks_completed || 0,
406
+ tasksFailed: t.tasks_failed || 0,
407
+ successRate: t.success_rate || 0,
408
+ totalRetries: t.total_retries || 0,
409
+ tasksWithRetries: t.tasks_with_retries || 0,
410
+ avgRetries: t.avg_retries || 0,
411
+ avgDurationMs: t.avg_duration_ms || 0,
412
+ avgTokens: t.avg_tokens || 0,
413
+ avgCost: t.avg_cost || 0,
414
+ avgContextPct: t.avg_context_pct || 0,
415
+ maxContextTokens: t.max_context_tokens || 0
416
+ })),
417
+ qualityMetrics: (data.quality_metrics || []).map((q) => ({
418
+ squad: q.squad,
419
+ feedbackCount: q.feedback_count || 0,
420
+ avgQuality: q.avg_quality || 0,
421
+ helpfulPct: q.helpful_pct || 0,
422
+ fixRequiredPct: q.fix_required_pct || 0
423
+ })),
424
+ topTools: (data.top_tools || []).map((t) => ({
425
+ toolName: t.tool_name,
426
+ usageCount: t.usage_count || 0,
427
+ successRate: t.success_rate || 0,
428
+ avgDurationMs: t.avg_duration_ms || 0
429
+ })),
430
+ toolFailureRate: data.tool_failure_rate || 0,
431
+ source: "bridge"
432
+ };
433
+ } catch {
434
+ return {
435
+ period,
436
+ days: period === "day" ? 1 : period === "week" ? 7 : 30,
437
+ taskMetrics: [],
438
+ qualityMetrics: [],
439
+ topTools: [],
440
+ toolFailureRate: 0,
441
+ source: "none"
442
+ };
443
+ }
444
+ }
445
+ async function fetchNpmStats(packageName = process.env.SQUADS_NPM_PACKAGE || "squads-cli") {
446
+ try {
447
+ const [dayRes, weekRes, monthRes] = await Promise.all([
448
+ fetch(`https://api.npmjs.org/downloads/point/last-day/${packageName}`),
449
+ fetch(`https://api.npmjs.org/downloads/point/last-week/${packageName}`),
450
+ fetch(`https://api.npmjs.org/downloads/point/last-month/${packageName}`)
451
+ ]);
452
+ if (!dayRes.ok || !weekRes.ok || !monthRes.ok) return null;
453
+ const [dayData, weekData, monthData] = await Promise.all([
454
+ dayRes.json(),
455
+ weekRes.json(),
456
+ monthRes.json()
457
+ ]);
458
+ const avgWeeklyFromMonth = monthData.downloads / 4;
459
+ const weekOverWeek = avgWeeklyFromMonth > 0 ? Math.round((weekData.downloads - avgWeeklyFromMonth) / avgWeeklyFromMonth * 100) : 0;
460
+ return {
461
+ package: packageName,
462
+ downloads: {
463
+ lastDay: dayData.downloads,
464
+ lastWeek: weekData.downloads,
465
+ lastMonth: monthData.downloads
466
+ },
467
+ weekOverWeek
468
+ };
469
+ } catch {
470
+ return null;
471
+ }
472
+ }
473
+ var OPUS_WEIGHT = 5;
474
+ var SONNET_WEIGHT = 1;
475
+ var HAIKU_WEIGHT = 0.25;
476
+ var DEFAULT_WEEKLY_WEIGHTED_LIMIT = 4e6;
477
+ var MAX5_SESSION_TOKEN_LIMIT = 2e6;
478
+ async function fetchClaudeCodeCapacity() {
479
+ const { readFile } = await import("fs/promises");
480
+ const { homedir } = await import("os");
481
+ const { join } = await import("path");
482
+ try {
483
+ const cacheFile = join(homedir(), ".claude", "stats-cache.json");
484
+ const content = await readFile(cacheFile, "utf-8");
485
+ const data = JSON.parse(content);
486
+ const now = /* @__PURE__ */ new Date();
487
+ const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
488
+ const weekStart = weekAgo.toISOString().split("T")[0];
489
+ let weeklyOpus = 0;
490
+ if (data.dailyModelTokens) {
491
+ for (const day of data.dailyModelTokens) {
492
+ if (day.date >= weekStart) {
493
+ for (const [model, tokens] of Object.entries(day.tokensByModel)) {
494
+ if (model.includes("opus")) {
495
+ weeklyOpus += tokens;
496
+ }
497
+ }
498
+ }
499
+ }
500
+ }
501
+ const weeklyLimit = parseInt(process.env.SQUADS_WEEKLY_LIMIT || "", 10) || DEFAULT_WEEKLY_WEIGHTED_LIMIT;
502
+ const today = now.toISOString().split("T")[0];
503
+ let sessionTokens = 0;
504
+ if (data.dailyModelTokens) {
505
+ const todayData = data.dailyModelTokens.find((d) => d.date === today);
506
+ if (todayData) {
507
+ sessionTokens = Object.values(todayData.tokensByModel).reduce((a, b) => a + b, 0);
508
+ }
509
+ }
510
+ const daysUntilSunday = (7 - now.getDay()) % 7 || 7;
511
+ const nextSunday = new Date(now);
512
+ nextSunday.setDate(now.getDate() + daysUntilSunday);
513
+ nextSunday.setHours(21, 59, 0, 0);
514
+ const sessionReset = new Date(now);
515
+ sessionReset.setHours(18, 59, 0, 0);
516
+ if (sessionReset < now) {
517
+ sessionReset.setDate(sessionReset.getDate() + 1);
518
+ }
519
+ let sonnetTokens = 0;
520
+ let haikuTokens = 0;
521
+ if (data.dailyModelTokens) {
522
+ for (const day of data.dailyModelTokens) {
523
+ if (day.date >= weekStart) {
524
+ for (const [model, tokens] of Object.entries(day.tokensByModel)) {
525
+ if (model.includes("sonnet")) {
526
+ sonnetTokens += tokens;
527
+ } else if (model.includes("haiku")) {
528
+ haikuTokens += tokens;
529
+ }
530
+ }
531
+ }
532
+ }
533
+ }
534
+ const weeklyWeighted = Math.round(
535
+ weeklyOpus * OPUS_WEIGHT + sonnetTokens * SONNET_WEIGHT + haikuTokens * HAIKU_WEIGHT
536
+ );
537
+ return {
538
+ weeklyTokensUsed: weeklyWeighted,
539
+ // Now weighted, not raw
540
+ weeklyTokensLimit: weeklyLimit,
541
+ weeklyCapacityPct: Math.round(weeklyWeighted / weeklyLimit * 100),
542
+ sessionTokensUsed: sessionTokens,
543
+ sessionTokensLimit: MAX5_SESSION_TOKEN_LIMIT,
544
+ sessionCapacityPct: Math.round(sessionTokens / MAX5_SESSION_TOKEN_LIMIT * 100),
545
+ opusTokensUsed: weeklyOpus,
546
+ sonnetTokensUsed: sonnetTokens,
547
+ haikuTokensUsed: haikuTokens,
548
+ weeklyResetDate: nextSunday.toLocaleDateString("en-US", { month: "short", day: "numeric" }),
549
+ sessionResetTime: sessionReset.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })
550
+ };
551
+ } catch {
552
+ return null;
553
+ }
554
+ }
555
+ function calculateROIMetrics(costs, goalsCompleted, commits, prsMerged, hoursTracked = 0) {
556
+ const totalCost = costs?.totalCost || 0;
557
+ const costPerGoal = goalsCompleted > 0 ? totalCost / goalsCompleted : 0;
558
+ const costPerCommit = commits > 0 ? totalCost / commits : 0;
559
+ const costPerPR = prsMerged > 0 ? totalCost / prsMerged : 0;
560
+ const GOAL_VALUE = parseFloat(process.env.SQUADS_GOAL_VALUE || "100");
561
+ const PR_VALUE = parseFloat(process.env.SQUADS_PR_VALUE || "200");
562
+ const COMMIT_VALUE = parseFloat(process.env.SQUADS_COMMIT_VALUE || "25");
563
+ const estimatedValue = goalsCompleted * GOAL_VALUE + prsMerged * PR_VALUE + commits * COMMIT_VALUE;
564
+ const roiMultiplier = totalCost > 0 ? estimatedValue / totalCost : 0;
565
+ const now = /* @__PURE__ */ new Date();
566
+ const hoursElapsed = hoursTracked > 0 ? hoursTracked : Math.max(now.getHours() + now.getMinutes() / 60, 1);
567
+ const costPerHour = totalCost / hoursElapsed;
568
+ return {
569
+ totalCostUsd: totalCost,
570
+ costPerGoal,
571
+ costPerCommit,
572
+ costPerPR,
573
+ estimatedValueUsd: estimatedValue,
574
+ roiMultiplier,
575
+ dailyProjectedCost: costPerHour * 24,
576
+ weeklyProjectedCost: costPerHour * 24 * 7,
577
+ monthlyProjectedCost: costPerHour * 24 * 30,
578
+ hoursTracked: hoursElapsed,
579
+ costPerHour
580
+ };
581
+ }
582
+ function calculateSquadCostProjections(bridgeStats, _history) {
583
+ if (!bridgeStats || bridgeStats.bySquad.length === 0) {
584
+ return [];
585
+ }
586
+ const now = /* @__PURE__ */ new Date();
587
+ const hoursElapsed = Math.max(now.getHours() + now.getMinutes() / 60, 1);
588
+ return bridgeStats.bySquad.map((squad) => {
589
+ const hourlyRate = squad.costUsd / hoursElapsed;
590
+ return {
591
+ squad: squad.squad,
592
+ currentDailyCost: squad.costUsd,
593
+ projectedDailyCost: hourlyRate * 24,
594
+ projectedWeeklyCost: hourlyRate * 24 * 7,
595
+ projectedMonthlyCost: hourlyRate * 24 * 30,
596
+ costTrend: "stable",
597
+ trendPct: 0
598
+ };
599
+ });
600
+ }
601
+
602
+ export {
603
+ detectPlan,
604
+ getPlanType,
605
+ isMaxPlan,
606
+ fetchCostSummary,
607
+ fetchBridgeStats,
608
+ fetchQuotaInfo,
609
+ fetchRateLimits,
610
+ fetchInsights,
611
+ fetchNpmStats,
612
+ fetchClaudeCodeCapacity,
613
+ calculateROIMetrics,
614
+ calculateSquadCostProjections
615
+ };
616
+ //# sourceMappingURL=chunk-NA45DFXY.js.map