cost-katana-cli 2.0.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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +343 -0
  3. package/bin/cost-katana.js +20 -0
  4. package/dist/commands/agent-inspect.d.ts +3 -0
  5. package/dist/commands/agent-inspect.d.ts.map +1 -0
  6. package/dist/commands/agent-inspect.js +630 -0
  7. package/dist/commands/agent-inspect.js.map +1 -0
  8. package/dist/commands/analytics.d.ts +3 -0
  9. package/dist/commands/analytics.d.ts.map +1 -0
  10. package/dist/commands/analytics.js +375 -0
  11. package/dist/commands/analytics.js.map +1 -0
  12. package/dist/commands/analyze.d.ts +3 -0
  13. package/dist/commands/analyze.d.ts.map +1 -0
  14. package/dist/commands/analyze.js +286 -0
  15. package/dist/commands/analyze.js.map +1 -0
  16. package/dist/commands/ask.d.ts +3 -0
  17. package/dist/commands/ask.d.ts.map +1 -0
  18. package/dist/commands/ask.js +137 -0
  19. package/dist/commands/ask.js.map +1 -0
  20. package/dist/commands/audit-firewall.d.ts +3 -0
  21. package/dist/commands/audit-firewall.d.ts.map +1 -0
  22. package/dist/commands/audit-firewall.js +737 -0
  23. package/dist/commands/audit-firewall.js.map +1 -0
  24. package/dist/commands/budget.d.ts +3 -0
  25. package/dist/commands/budget.d.ts.map +1 -0
  26. package/dist/commands/budget.js +283 -0
  27. package/dist/commands/budget.js.map +1 -0
  28. package/dist/commands/bulk-optimize.d.ts +3 -0
  29. package/dist/commands/bulk-optimize.d.ts.map +1 -0
  30. package/dist/commands/bulk-optimize.js +863 -0
  31. package/dist/commands/bulk-optimize.js.map +1 -0
  32. package/dist/commands/chat.d.ts +3 -0
  33. package/dist/commands/chat.d.ts.map +1 -0
  34. package/dist/commands/chat.js +292 -0
  35. package/dist/commands/chat.js.map +1 -0
  36. package/dist/commands/check-cache.d.ts +3 -0
  37. package/dist/commands/check-cache.d.ts.map +1 -0
  38. package/dist/commands/check-cache.js +267 -0
  39. package/dist/commands/check-cache.js.map +1 -0
  40. package/dist/commands/compare.d.ts +3 -0
  41. package/dist/commands/compare.d.ts.map +1 -0
  42. package/dist/commands/compare.js +131 -0
  43. package/dist/commands/compare.js.map +1 -0
  44. package/dist/commands/config.d.ts +3 -0
  45. package/dist/commands/config.d.ts.map +1 -0
  46. package/dist/commands/config.js +421 -0
  47. package/dist/commands/config.js.map +1 -0
  48. package/dist/commands/craft-workflow.d.ts +3 -0
  49. package/dist/commands/craft-workflow.d.ts.map +1 -0
  50. package/dist/commands/craft-workflow.js +844 -0
  51. package/dist/commands/craft-workflow.js.map +1 -0
  52. package/dist/commands/debug-prompt.d.ts +3 -0
  53. package/dist/commands/debug-prompt.d.ts.map +1 -0
  54. package/dist/commands/debug-prompt.js +614 -0
  55. package/dist/commands/debug-prompt.js.map +1 -0
  56. package/dist/commands/diff-prompts.d.ts +3 -0
  57. package/dist/commands/diff-prompts.d.ts.map +1 -0
  58. package/dist/commands/diff-prompts.js +553 -0
  59. package/dist/commands/diff-prompts.js.map +1 -0
  60. package/dist/commands/high-cost-prompts.d.ts +3 -0
  61. package/dist/commands/high-cost-prompts.d.ts.map +1 -0
  62. package/dist/commands/high-cost-prompts.js +719 -0
  63. package/dist/commands/high-cost-prompts.js.map +1 -0
  64. package/dist/commands/init.d.ts +3 -0
  65. package/dist/commands/init.d.ts.map +1 -0
  66. package/dist/commands/init.js +325 -0
  67. package/dist/commands/init.js.map +1 -0
  68. package/dist/commands/key.d.ts +3 -0
  69. package/dist/commands/key.d.ts.map +1 -0
  70. package/dist/commands/key.js +574 -0
  71. package/dist/commands/key.js.map +1 -0
  72. package/dist/commands/list-models.d.ts +3 -0
  73. package/dist/commands/list-models.d.ts.map +1 -0
  74. package/dist/commands/list-models.js +154 -0
  75. package/dist/commands/list-models.js.map +1 -0
  76. package/dist/commands/models.d.ts +3 -0
  77. package/dist/commands/models.d.ts.map +1 -0
  78. package/dist/commands/models.js +107 -0
  79. package/dist/commands/models.js.map +1 -0
  80. package/dist/commands/optimize.d.ts +3 -0
  81. package/dist/commands/optimize.d.ts.map +1 -0
  82. package/dist/commands/optimize.js +345 -0
  83. package/dist/commands/optimize.js.map +1 -0
  84. package/dist/commands/project.d.ts +3 -0
  85. package/dist/commands/project.d.ts.map +1 -0
  86. package/dist/commands/project.js +475 -0
  87. package/dist/commands/project.js.map +1 -0
  88. package/dist/commands/prompt-metrics.d.ts +3 -0
  89. package/dist/commands/prompt-metrics.d.ts.map +1 -0
  90. package/dist/commands/prompt-metrics.js +665 -0
  91. package/dist/commands/prompt-metrics.js.map +1 -0
  92. package/dist/commands/replay-session.d.ts +3 -0
  93. package/dist/commands/replay-session.d.ts.map +1 -0
  94. package/dist/commands/replay-session.js +615 -0
  95. package/dist/commands/replay-session.js.map +1 -0
  96. package/dist/commands/retry-log.d.ts +3 -0
  97. package/dist/commands/retry-log.d.ts.map +1 -0
  98. package/dist/commands/retry-log.js +686 -0
  99. package/dist/commands/retry-log.js.map +1 -0
  100. package/dist/commands/rewrite-prompt.d.ts +3 -0
  101. package/dist/commands/rewrite-prompt.d.ts.map +1 -0
  102. package/dist/commands/rewrite-prompt.js +802 -0
  103. package/dist/commands/rewrite-prompt.js.map +1 -0
  104. package/dist/commands/set-budget.d.ts +3 -0
  105. package/dist/commands/set-budget.d.ts.map +1 -0
  106. package/dist/commands/set-budget.js +909 -0
  107. package/dist/commands/set-budget.js.map +1 -0
  108. package/dist/commands/simulate-cost.d.ts +3 -0
  109. package/dist/commands/simulate-cost.d.ts.map +1 -0
  110. package/dist/commands/simulate-cost.js +873 -0
  111. package/dist/commands/simulate-cost.js.map +1 -0
  112. package/dist/commands/suggest-models.d.ts +3 -0
  113. package/dist/commands/suggest-models.d.ts.map +1 -0
  114. package/dist/commands/suggest-models.js +674 -0
  115. package/dist/commands/suggest-models.js.map +1 -0
  116. package/dist/commands/test.d.ts +3 -0
  117. package/dist/commands/test.d.ts.map +1 -0
  118. package/dist/commands/test.js +187 -0
  119. package/dist/commands/test.js.map +1 -0
  120. package/dist/commands/trace-workflow.d.ts +3 -0
  121. package/dist/commands/trace-workflow.d.ts.map +1 -0
  122. package/dist/commands/trace-workflow.js +651 -0
  123. package/dist/commands/trace-workflow.js.map +1 -0
  124. package/dist/commands/trace.d.ts +3 -0
  125. package/dist/commands/trace.d.ts.map +1 -0
  126. package/dist/commands/trace.js +468 -0
  127. package/dist/commands/trace.js.map +1 -0
  128. package/dist/commands/track.d.ts +3 -0
  129. package/dist/commands/track.d.ts.map +1 -0
  130. package/dist/commands/track.js +404 -0
  131. package/dist/commands/track.js.map +1 -0
  132. package/dist/index.d.ts +4 -0
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +169 -0
  135. package/dist/index.js.map +1 -0
  136. package/dist/utils/config.d.ts +46 -0
  137. package/dist/utils/config.d.ts.map +1 -0
  138. package/dist/utils/config.js +321 -0
  139. package/dist/utils/config.js.map +1 -0
  140. package/dist/utils/logger.d.ts +21 -0
  141. package/dist/utils/logger.d.ts.map +1 -0
  142. package/dist/utils/logger.js +101 -0
  143. package/dist/utils/logger.js.map +1 -0
  144. package/dist/utils/models.d.ts +22 -0
  145. package/dist/utils/models.d.ts.map +1 -0
  146. package/dist/utils/models.js +2251 -0
  147. package/dist/utils/models.js.map +1 -0
  148. package/package.json +107 -0
@@ -0,0 +1,686 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.retryLogCommand = retryLogCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const logger_1 = require("../utils/logger");
9
+ const config_1 = require("../utils/config");
10
+ const axios_1 = __importDefault(require("axios"));
11
+ function retryLogCommand(program) {
12
+ const retryGroup = program
13
+ .command('retry-log')
14
+ .description('🔁 Show which prompts failed and retried');
15
+ // Main retry-log command
16
+ retryGroup
17
+ .option('--format <format>', 'Output format (table, json, csv)', 'table')
18
+ .option('--export <path>', 'Export retry data to file')
19
+ .option('-v, --verbose', 'Show detailed retry information')
20
+ .action(async (options) => {
21
+ try {
22
+ await handleRetryLog(options);
23
+ }
24
+ catch (error) {
25
+ logger_1.logger.error('Retry log command failed:', error);
26
+ process.exit(1);
27
+ }
28
+ });
29
+ // Show retry logs by time range
30
+ retryGroup
31
+ .command('range')
32
+ .description('🔁 Show retry logs for a specific time range')
33
+ .option('-r, --range <range>', 'Time range (1d, 7d, 30d, 90d)', '7d')
34
+ .option('--format <format>', 'Output format (table, json, csv)', 'table')
35
+ .option('--export <path>', 'Export retry data to file')
36
+ .option('-v, --verbose', 'Show detailed retry information')
37
+ .option('--include-successful', 'Include successful retries')
38
+ .option('--include-failed', 'Include failed retries')
39
+ .option('--include-timeout', 'Include timeout retries')
40
+ .option('--include-rate-limit', 'Include rate limit retries')
41
+ .action(async (options) => {
42
+ try {
43
+ await handleRetryLogByRange(options);
44
+ }
45
+ catch (error) {
46
+ logger_1.logger.error('Retry log by range failed:', error);
47
+ process.exit(1);
48
+ }
49
+ });
50
+ // Show retry logs by request ID
51
+ retryGroup
52
+ .command('id <requestId>')
53
+ .description('🔁 Show detailed retry log for a specific request')
54
+ .option('--format <format>', 'Output format (table, json, csv)', 'table')
55
+ .option('--export <path>', 'Export retry data to file')
56
+ .option('-v, --verbose', 'Show detailed retry information')
57
+ .action(async (requestId, options) => {
58
+ try {
59
+ await handleRetryLogById(requestId, options);
60
+ }
61
+ catch (error) {
62
+ logger_1.logger.error('Retry log by ID failed:', error);
63
+ process.exit(1);
64
+ }
65
+ });
66
+ // Show retry logs by failure type
67
+ retryGroup
68
+ .command('failure <failureType>')
69
+ .description('🔁 Show retry logs for a specific failure type')
70
+ .option('-r, --range <range>', 'Time range (1d, 7d, 30d, 90d)', '7d')
71
+ .option('--format <format>', 'Output format (table, json, csv)', 'table')
72
+ .option('--export <path>', 'Export retry data to file')
73
+ .option('-v, --verbose', 'Show detailed retry information')
74
+ .action(async (failureType, options) => {
75
+ try {
76
+ await handleRetryLogByFailureType(failureType, options);
77
+ }
78
+ catch (error) {
79
+ logger_1.logger.error('Retry log by failure type failed:', error);
80
+ process.exit(1);
81
+ }
82
+ });
83
+ // Show retry logs by model
84
+ retryGroup
85
+ .command('model <modelName>')
86
+ .description('🔁 Show retry logs for a specific model')
87
+ .option('-r, --range <range>', 'Time range (1d, 7d, 30d, 90d)', '7d')
88
+ .option('--format <format>', 'Output format (table, json, csv)', 'table')
89
+ .option('--export <path>', 'Export retry data to file')
90
+ .option('-v, --verbose', 'Show detailed retry information')
91
+ .action(async (modelName, options) => {
92
+ try {
93
+ await handleRetryLogByModel(modelName, options);
94
+ }
95
+ catch (error) {
96
+ logger_1.logger.error('Retry log by model failed:', error);
97
+ process.exit(1);
98
+ }
99
+ });
100
+ // Show retry statistics
101
+ retryGroup
102
+ .command('stats')
103
+ .description('📊 Show retry statistics and patterns')
104
+ .option('-r, --range <range>', 'Time range (1d, 7d, 30d, 90d)', '7d')
105
+ .option('--format <format>', 'Output format (table, json, csv)', 'table')
106
+ .option('--export <path>', 'Export statistics data to file')
107
+ .option('-v, --verbose', 'Show detailed statistics')
108
+ .action(async (options) => {
109
+ try {
110
+ await handleRetryLogStats(options);
111
+ }
112
+ catch (error) {
113
+ logger_1.logger.error('Retry log stats failed:', error);
114
+ process.exit(1);
115
+ }
116
+ });
117
+ }
118
+ async function handleRetryLog(options) {
119
+ console.log(chalk_1.default.cyan.bold('\n🔁 Retry Log & Failure Analysis'));
120
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
121
+ console.log(chalk_1.default.yellow('Available commands:'));
122
+ console.log(chalk_1.default.white(' costkatana retry-log range Show retry logs by time range'));
123
+ console.log(chalk_1.default.white(' costkatana retry-log id <requestId> Show retry log for specific request'));
124
+ console.log(chalk_1.default.white(' costkatana retry-log failure <failureType> Show retry logs by failure type'));
125
+ console.log(chalk_1.default.white(' costkatana retry-log model <modelName> Show retry logs by model'));
126
+ console.log(chalk_1.default.white(' costkatana retry-log stats Show retry statistics'));
127
+ console.log(chalk_1.default.gray('\nExamples:'));
128
+ console.log(chalk_1.default.white(' costkatana retry-log range --range 7d'));
129
+ console.log(chalk_1.default.white(' costkatana retry-log id req-12345'));
130
+ console.log(chalk_1.default.white(' costkatana retry-log failure timeout'));
131
+ console.log(chalk_1.default.white(' costkatana retry-log model gpt-4'));
132
+ console.log(chalk_1.default.white(' costkatana retry-log stats --range 30d'));
133
+ console.log(chalk_1.default.gray('\nRetry Information:'));
134
+ console.log(chalk_1.default.white(' • Request ID and failure cause'));
135
+ console.log(chalk_1.default.white(' • Retry attempts and strategy used'));
136
+ console.log(chalk_1.default.white(' • Final outcome (success/failure)'));
137
+ console.log(chalk_1.default.white(' • Failure patterns and trends'));
138
+ console.log(chalk_1.default.white(' • Retry success rates by type'));
139
+ console.log(chalk_1.default.white(' • Performance impact analysis'));
140
+ console.log(chalk_1.default.gray('\nFailure Types:'));
141
+ console.log(chalk_1.default.white(' • 5xx - Server errors'));
142
+ console.log(chalk_1.default.white(' • timeout - Request timeouts'));
143
+ console.log(chalk_1.default.white(' • rate-limit - Rate limiting'));
144
+ console.log(chalk_1.default.white(' • quota-exceeded - Quota limits'));
145
+ console.log(chalk_1.default.white(' • network - Network connectivity'));
146
+ console.log(chalk_1.default.white(' • invalid-request - Malformed requests'));
147
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
148
+ }
149
+ async function handleRetryLogByRange(options) {
150
+ logger_1.logger.info(`🔁 Fetching retry logs for range: ${options.range || '7d'}`);
151
+ try {
152
+ const range = options.range || '7d';
153
+ const retryData = await getRetryLogsByRange(range, options);
154
+ displayRetryLogs(retryData, options);
155
+ }
156
+ catch (error) {
157
+ logger_1.logger.error('Failed to fetch retry logs by range:', error);
158
+ process.exit(1);
159
+ }
160
+ }
161
+ async function getRetryLogsByRange(range, options) {
162
+ const baseUrl = config_1.configManager.get('baseUrl');
163
+ const apiKey = config_1.configManager.get('apiKey');
164
+ if (!baseUrl || !apiKey) {
165
+ console.log(chalk_1.default.red.bold('\n❌ Configuration Missing'));
166
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
167
+ if (!apiKey) {
168
+ console.log(chalk_1.default.yellow('• API Key is not set'));
169
+ }
170
+ if (!baseUrl) {
171
+ console.log(chalk_1.default.yellow('• Base URL is not set'));
172
+ }
173
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
174
+ console.log(chalk_1.default.cyan('To set up your configuration, run:'));
175
+ console.log(chalk_1.default.white(' cost-katana init'));
176
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
177
+ throw new Error('Configuration incomplete. Please run "cost-katana init" to set up your API key and base URL.');
178
+ }
179
+ try {
180
+ const params = new URLSearchParams();
181
+ params.append('range', range);
182
+ if (options.includeSuccessful)
183
+ params.append('includeSuccessful', 'true');
184
+ if (options.includeFailed)
185
+ params.append('includeFailed', 'true');
186
+ if (options.includeTimeout)
187
+ params.append('includeTimeout', 'true');
188
+ if (options.includeRateLimit)
189
+ params.append('includeRateLimit', 'true');
190
+ const response = await axios_1.default.get(`${baseUrl}/api/retry-log/range?${params}`, {
191
+ headers: {
192
+ 'Authorization': `Bearer ${apiKey}`,
193
+ 'Content-Type': 'application/json',
194
+ },
195
+ timeout: 30000,
196
+ });
197
+ if (response.status !== 200) {
198
+ throw new Error(`API returned status ${response.status}`);
199
+ }
200
+ if (response.data.success && response.data.data) {
201
+ return response.data.data;
202
+ }
203
+ else {
204
+ throw new Error(response.data.message || 'Invalid response format');
205
+ }
206
+ }
207
+ catch (error) {
208
+ if (error.response) {
209
+ throw new Error(`API Error: ${error.response.status} - ${error.response.data?.message || 'Unknown error'}`);
210
+ }
211
+ else if (error.request) {
212
+ throw new Error('No response received from API');
213
+ }
214
+ else {
215
+ throw new Error(`Request failed: ${error.message}`);
216
+ }
217
+ }
218
+ }
219
+ function displayRetryLogs(retryLogs, options) {
220
+ const format = options.format || 'table';
221
+ if (format === 'json') {
222
+ console.log(JSON.stringify(retryLogs, null, 2));
223
+ return;
224
+ }
225
+ else if (format === 'csv') {
226
+ console.log('Request ID,Failure Cause,Retry Attempts,Strategy,Final Outcome,Model,Timestamp');
227
+ retryLogs.logs.forEach((log) => {
228
+ console.log(`"${log.requestId}","${log.failureCause}","${log.retryAttempts}","${log.strategy}","${log.finalOutcome}","${log.model}","${log.timestamp}"`);
229
+ });
230
+ return;
231
+ }
232
+ console.log(chalk_1.default.cyan.bold('\n🔁 Retry Log Analysis'));
233
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
234
+ // Summary Statistics
235
+ console.log(chalk_1.default.yellow.bold('\n📊 Summary Statistics'));
236
+ console.log(chalk_1.default.gray('─'.repeat(50)));
237
+ console.log(chalk_1.default.white('Total Retries:'), chalk_1.default.cyan(retryLogs.summary.totalRetries.toLocaleString()));
238
+ console.log(chalk_1.default.white('Successful Retries:'), chalk_1.default.green(retryLogs.summary.successfulRetries.toLocaleString()));
239
+ console.log(chalk_1.default.white('Failed Retries:'), chalk_1.default.red(retryLogs.summary.failedRetries.toLocaleString()));
240
+ console.log(chalk_1.default.white('Success Rate:'), chalk_1.default.green(`${(retryLogs.summary.successRate * 100).toFixed(1)}%`));
241
+ console.log(chalk_1.default.white('Average Retry Attempts:'), chalk_1.default.cyan(retryLogs.summary.avgRetryAttempts.toFixed(1)));
242
+ // Failure Type Breakdown
243
+ if (retryLogs.failureBreakdown) {
244
+ console.log(chalk_1.default.yellow.bold('\n🚨 Failure Type Breakdown'));
245
+ console.log(chalk_1.default.gray('─'.repeat(50)));
246
+ Object.entries(retryLogs.failureBreakdown).forEach(([type, data]) => {
247
+ const color = data.count > 0 ? chalk_1.default.red : chalk_1.default.gray;
248
+ console.log(chalk_1.default.white(`${type}:`), color(`${data.count} (${(data.percentage * 100).toFixed(1)}%)`));
249
+ if (data.avgRetries) {
250
+ console.log(chalk_1.default.gray(` Avg Retries: ${data.avgRetries.toFixed(1)}`));
251
+ }
252
+ });
253
+ }
254
+ // Retry Logs
255
+ if (retryLogs.logs && retryLogs.logs.length > 0) {
256
+ console.log(chalk_1.default.yellow.bold('\n📋 Retry Logs'));
257
+ console.log(chalk_1.default.gray('─'.repeat(50)));
258
+ retryLogs.logs.forEach((log, index) => {
259
+ console.log(chalk_1.default.white(`\n${index + 1}. ${log.requestId}`));
260
+ console.log(chalk_1.default.gray(' ─'.repeat(40)));
261
+ // Failure information
262
+ const failureColor = log.failureCause === 'timeout' ? chalk_1.default.yellow :
263
+ log.failureCause === 'rate-limit' ? chalk_1.default.magenta : chalk_1.default.red;
264
+ console.log(chalk_1.default.white(' 🚨 Failure:'), failureColor(log.failureCause));
265
+ // Retry attempts
266
+ console.log(chalk_1.default.white(' 🔄 Attempts:'), chalk_1.default.cyan(log.retryAttempts));
267
+ console.log(chalk_1.default.white(' 🎯 Strategy:'), chalk_1.default.cyan(log.strategy));
268
+ // Final outcome
269
+ const outcomeColor = log.finalOutcome === 'success' ? chalk_1.default.green : chalk_1.default.red;
270
+ console.log(chalk_1.default.white(' 📊 Outcome:'), outcomeColor(log.finalOutcome));
271
+ // Model and timestamp
272
+ console.log(chalk_1.default.white(' 🤖 Model:'), chalk_1.default.cyan(log.model));
273
+ console.log(chalk_1.default.white(' ⏰ Time:'), chalk_1.default.cyan(new Date(log.timestamp).toLocaleString()));
274
+ // Additional details if verbose
275
+ if (options.verbose && log.details) {
276
+ console.log(chalk_1.default.white(' 📝 Details:'));
277
+ console.log(chalk_1.default.gray(` Error: ${log.details.error}`));
278
+ console.log(chalk_1.default.gray(` Response Time: ${log.details.responseTime}ms`));
279
+ console.log(chalk_1.default.gray(` Retry Delay: ${log.details.retryDelay}ms`));
280
+ }
281
+ });
282
+ }
283
+ else {
284
+ console.log(chalk_1.default.yellow('\nNo retry logs found for the specified range.'));
285
+ }
286
+ // Recommendations
287
+ if (retryLogs.recommendations && retryLogs.recommendations.length > 0) {
288
+ console.log(chalk_1.default.yellow.bold('\n💡 Recommendations'));
289
+ console.log(chalk_1.default.gray('─'.repeat(50)));
290
+ retryLogs.recommendations.forEach((rec, index) => {
291
+ console.log(chalk_1.default.white(`${index + 1}. ${rec.type}:`));
292
+ console.log(chalk_1.default.gray(` ${rec.description}`));
293
+ if (rec.impact) {
294
+ console.log(chalk_1.default.gray(` Impact: ${rec.impact}`));
295
+ }
296
+ });
297
+ }
298
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
299
+ }
300
+ async function handleRetryLogById(requestId, options) {
301
+ logger_1.logger.info(`🔁 Fetching retry log for request: ${requestId}`);
302
+ try {
303
+ const retryData = await getRetryLogById(requestId, options);
304
+ displayRetryLogDetail(retryData, options);
305
+ }
306
+ catch (error) {
307
+ logger_1.logger.error('Failed to fetch retry log by ID:', error);
308
+ process.exit(1);
309
+ }
310
+ }
311
+ async function getRetryLogById(requestId, options) {
312
+ const baseUrl = config_1.configManager.get('baseUrl');
313
+ const apiKey = config_1.configManager.get('apiKey');
314
+ if (!baseUrl || !apiKey) {
315
+ console.log(chalk_1.default.red.bold('\n❌ Configuration Missing'));
316
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
317
+ if (!apiKey) {
318
+ console.log(chalk_1.default.yellow('• API Key is not set'));
319
+ }
320
+ if (!baseUrl) {
321
+ console.log(chalk_1.default.yellow('• Base URL is not set'));
322
+ }
323
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
324
+ console.log(chalk_1.default.cyan('To set up your configuration, run:'));
325
+ console.log(chalk_1.default.white(' cost-katana init'));
326
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
327
+ throw new Error('Configuration incomplete. Please run "cost-katana init" to set up your API key and base URL.');
328
+ }
329
+ try {
330
+ const response = await axios_1.default.get(`${baseUrl}/api/retry-log/${requestId}`, {
331
+ headers: {
332
+ 'Authorization': `Bearer ${apiKey}`,
333
+ 'Content-Type': 'application/json',
334
+ },
335
+ timeout: 30000,
336
+ });
337
+ if (response.status !== 200) {
338
+ throw new Error(`API returned status ${response.status}`);
339
+ }
340
+ if (response.data.success && response.data.data) {
341
+ return response.data.data;
342
+ }
343
+ else {
344
+ throw new Error(response.data.message || 'Invalid response format');
345
+ }
346
+ }
347
+ catch (error) {
348
+ if (error.response) {
349
+ throw new Error(`API Error: ${error.response.status} - ${error.response.data?.message || 'Unknown error'}`);
350
+ }
351
+ else if (error.request) {
352
+ throw new Error('No response received from API');
353
+ }
354
+ else {
355
+ throw new Error(`Request failed: ${error.message}`);
356
+ }
357
+ }
358
+ }
359
+ function displayRetryLogDetail(retryLog, options) {
360
+ const format = options.format || 'table';
361
+ if (format === 'json') {
362
+ console.log(JSON.stringify(retryLog, null, 2));
363
+ return;
364
+ }
365
+ else if (format === 'csv') {
366
+ console.log('Attempt,Status,Error,Response Time,Retry Delay,Strategy');
367
+ retryLog.attempts.forEach((attempt, index) => {
368
+ console.log(`"${index + 1}","${attempt.status}","${attempt.error}","${attempt.responseTime}","${attempt.retryDelay}","${attempt.strategy}"`);
369
+ });
370
+ return;
371
+ }
372
+ console.log(chalk_1.default.cyan.bold(`\n🔁 Retry Log Detail: ${retryLog.requestId}`));
373
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
374
+ // Request Information
375
+ console.log(chalk_1.default.yellow.bold('\n📋 Request Information'));
376
+ console.log(chalk_1.default.gray('─'.repeat(50)));
377
+ console.log(chalk_1.default.white('Request ID:'), chalk_1.default.cyan(retryLog.requestId));
378
+ console.log(chalk_1.default.white('Model:'), chalk_1.default.cyan(retryLog.model));
379
+ console.log(chalk_1.default.white('Provider:'), chalk_1.default.cyan(retryLog.provider));
380
+ console.log(chalk_1.default.white('Initial Failure:'), chalk_1.default.red(retryLog.initialFailure));
381
+ console.log(chalk_1.default.white('Final Outcome:'), retryLog.finalOutcome === 'success' ? chalk_1.default.green(retryLog.finalOutcome) : chalk_1.default.red(retryLog.finalOutcome));
382
+ console.log(chalk_1.default.white('Total Attempts:'), chalk_1.default.cyan(retryLog.totalAttempts));
383
+ console.log(chalk_1.default.white('Total Duration:'), chalk_1.default.cyan(`${retryLog.totalDuration}ms`));
384
+ // Retry Attempts Timeline
385
+ console.log(chalk_1.default.yellow.bold('\n⏱️ Retry Attempts Timeline'));
386
+ console.log(chalk_1.default.gray('─'.repeat(50)));
387
+ retryLog.attempts.forEach((attempt, index) => {
388
+ console.log(chalk_1.default.white(`\nAttempt ${index + 1}:`));
389
+ console.log(chalk_1.default.gray(' ─'.repeat(30)));
390
+ const statusColor = attempt.status === 'success' ? chalk_1.default.green :
391
+ attempt.status === 'retry' ? chalk_1.default.yellow : chalk_1.default.red;
392
+ console.log(chalk_1.default.white(' Status:'), statusColor(attempt.status));
393
+ console.log(chalk_1.default.white(' Time:'), chalk_1.default.cyan(new Date(attempt.timestamp).toLocaleString()));
394
+ console.log(chalk_1.default.white(' Response Time:'), chalk_1.default.cyan(`${attempt.responseTime}ms`));
395
+ if (attempt.error) {
396
+ console.log(chalk_1.default.white(' Error:'), chalk_1.default.red(attempt.error));
397
+ }
398
+ if (attempt.retryDelay) {
399
+ console.log(chalk_1.default.white(' Retry Delay:'), chalk_1.default.cyan(`${attempt.retryDelay}ms`));
400
+ }
401
+ console.log(chalk_1.default.white(' Strategy:'), chalk_1.default.cyan(attempt.strategy));
402
+ });
403
+ // Performance Analysis
404
+ if (retryLog.performance) {
405
+ console.log(chalk_1.default.yellow.bold('\n📊 Performance Analysis'));
406
+ console.log(chalk_1.default.gray('─'.repeat(50)));
407
+ console.log(chalk_1.default.white('Average Response Time:'), chalk_1.default.cyan(`${retryLog.performance.avgResponseTime}ms`));
408
+ console.log(chalk_1.default.white('Total Cost:'), chalk_1.default.green(`$${retryLog.performance.totalCost.toFixed(4)}`));
409
+ console.log(chalk_1.default.white('Cost per Attempt:'), chalk_1.default.cyan(`$${retryLog.performance.costPerAttempt.toFixed(4)}`));
410
+ console.log(chalk_1.default.white('Success Rate:'), chalk_1.default.green(`${(retryLog.performance.successRate * 100).toFixed(1)}%`));
411
+ }
412
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
413
+ }
414
+ async function handleRetryLogByFailureType(failureType, options) {
415
+ logger_1.logger.info(`🔁 Fetching retry logs for failure type: ${failureType}`);
416
+ try {
417
+ const range = options.range || '7d';
418
+ const retryData = await getRetryLogsByFailureType(failureType, range, options);
419
+ displayRetryLogs(retryData, options);
420
+ }
421
+ catch (error) {
422
+ logger_1.logger.error('Failed to fetch retry logs by failure type:', error);
423
+ process.exit(1);
424
+ }
425
+ }
426
+ async function getRetryLogsByFailureType(failureType, range, options) {
427
+ const baseUrl = config_1.configManager.get('baseUrl');
428
+ const apiKey = config_1.configManager.get('apiKey');
429
+ if (!baseUrl || !apiKey) {
430
+ console.log(chalk_1.default.red.bold('\n❌ Configuration Missing'));
431
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
432
+ if (!apiKey) {
433
+ console.log(chalk_1.default.yellow('• API Key is not set'));
434
+ }
435
+ if (!baseUrl) {
436
+ console.log(chalk_1.default.yellow('• Base URL is not set'));
437
+ }
438
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
439
+ console.log(chalk_1.default.cyan('To set up your configuration, run:'));
440
+ console.log(chalk_1.default.white(' cost-katana init'));
441
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
442
+ throw new Error('Configuration incomplete. Please run "cost-katana init" to set up your API key and base URL.');
443
+ }
444
+ try {
445
+ const params = new URLSearchParams();
446
+ params.append('failureType', failureType);
447
+ params.append('range', range);
448
+ const response = await axios_1.default.get(`${baseUrl}/api/retry-log/failure-type?${params}`, {
449
+ headers: {
450
+ 'Authorization': `Bearer ${apiKey}`,
451
+ 'Content-Type': 'application/json',
452
+ },
453
+ timeout: 30000,
454
+ });
455
+ if (response.status !== 200) {
456
+ throw new Error(`API returned status ${response.status}`);
457
+ }
458
+ if (response.data.success && response.data.data) {
459
+ return response.data.data;
460
+ }
461
+ else {
462
+ throw new Error(response.data.message || 'Invalid response format');
463
+ }
464
+ }
465
+ catch (error) {
466
+ if (error.response) {
467
+ throw new Error(`API Error: ${error.response.status} - ${error.response.data?.message || 'Unknown error'}`);
468
+ }
469
+ else if (error.request) {
470
+ throw new Error('No response received from API');
471
+ }
472
+ else {
473
+ throw new Error(`Request failed: ${error.message}`);
474
+ }
475
+ }
476
+ }
477
+ async function handleRetryLogByModel(modelName, options) {
478
+ logger_1.logger.info(`🔁 Fetching retry logs for model: ${modelName}`);
479
+ try {
480
+ const range = options.range || '7d';
481
+ const retryData = await getRetryLogsByModel(modelName, range, options);
482
+ displayRetryLogs(retryData, options);
483
+ }
484
+ catch (error) {
485
+ logger_1.logger.error('Failed to fetch retry logs by model:', error);
486
+ process.exit(1);
487
+ }
488
+ }
489
+ async function getRetryLogsByModel(modelName, range, options) {
490
+ const baseUrl = config_1.configManager.get('baseUrl');
491
+ const apiKey = config_1.configManager.get('apiKey');
492
+ if (!baseUrl || !apiKey) {
493
+ console.log(chalk_1.default.red.bold('\n❌ Configuration Missing'));
494
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
495
+ if (!apiKey) {
496
+ console.log(chalk_1.default.yellow('• API Key is not set'));
497
+ }
498
+ if (!baseUrl) {
499
+ console.log(chalk_1.default.yellow('• Base URL is not set'));
500
+ }
501
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
502
+ console.log(chalk_1.default.cyan('To set up your configuration, run:'));
503
+ console.log(chalk_1.default.white(' cost-katana init'));
504
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
505
+ throw new Error('Configuration incomplete. Please run "cost-katana init" to set up your API key and base URL.');
506
+ }
507
+ try {
508
+ const params = new URLSearchParams();
509
+ params.append('model', modelName);
510
+ params.append('range', range);
511
+ const response = await axios_1.default.get(`${baseUrl}/api/retry-log/model?${params}`, {
512
+ headers: {
513
+ 'Authorization': `Bearer ${apiKey}`,
514
+ 'Content-Type': 'application/json',
515
+ },
516
+ timeout: 30000,
517
+ });
518
+ if (response.status !== 200) {
519
+ throw new Error(`API returned status ${response.status}`);
520
+ }
521
+ if (response.data.success && response.data.data) {
522
+ return response.data.data;
523
+ }
524
+ else {
525
+ throw new Error(response.data.message || 'Invalid response format');
526
+ }
527
+ }
528
+ catch (error) {
529
+ if (error.response) {
530
+ throw new Error(`API Error: ${error.response.status} - ${error.response.data?.message || 'Unknown error'}`);
531
+ }
532
+ else if (error.request) {
533
+ throw new Error('No response received from API');
534
+ }
535
+ else {
536
+ throw new Error(`Request failed: ${error.message}`);
537
+ }
538
+ }
539
+ }
540
+ async function handleRetryLogStats(options) {
541
+ logger_1.logger.info('📊 Generating retry statistics...');
542
+ try {
543
+ const range = options.range || '7d';
544
+ const statsData = await getRetryLogStats(range, options);
545
+ displayRetryLogStats(statsData, options);
546
+ }
547
+ catch (error) {
548
+ logger_1.logger.error('Failed to generate retry statistics:', error);
549
+ process.exit(1);
550
+ }
551
+ }
552
+ async function getRetryLogStats(range, options) {
553
+ const baseUrl = config_1.configManager.get('baseUrl');
554
+ const apiKey = config_1.configManager.get('apiKey');
555
+ if (!baseUrl || !apiKey) {
556
+ console.log(chalk_1.default.red.bold('\n❌ Configuration Missing'));
557
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
558
+ if (!apiKey) {
559
+ console.log(chalk_1.default.yellow('• API Key is not set'));
560
+ }
561
+ if (!baseUrl) {
562
+ console.log(chalk_1.default.yellow('• Base URL is not set'));
563
+ }
564
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
565
+ console.log(chalk_1.default.cyan('To set up your configuration, run:'));
566
+ console.log(chalk_1.default.white(' cost-katana init'));
567
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
568
+ throw new Error('Configuration incomplete. Please run "cost-katana init" to set up your API key and base URL.');
569
+ }
570
+ try {
571
+ const params = new URLSearchParams();
572
+ params.append('range', range);
573
+ const response = await axios_1.default.get(`${baseUrl}/api/retry-log/stats?${params}`, {
574
+ headers: {
575
+ 'Authorization': `Bearer ${apiKey}`,
576
+ 'Content-Type': 'application/json',
577
+ },
578
+ timeout: 30000,
579
+ });
580
+ if (response.status !== 200) {
581
+ throw new Error(`API returned status ${response.status}`);
582
+ }
583
+ if (response.data.success && response.data.data) {
584
+ return response.data.data;
585
+ }
586
+ else {
587
+ throw new Error(response.data.message || 'Invalid response format');
588
+ }
589
+ }
590
+ catch (error) {
591
+ if (error.response) {
592
+ throw new Error(`API Error: ${error.response.status} - ${error.response.data?.message || 'Unknown error'}`);
593
+ }
594
+ else if (error.request) {
595
+ throw new Error('No response received from API');
596
+ }
597
+ else {
598
+ throw new Error(`Request failed: ${error.message}`);
599
+ }
600
+ }
601
+ }
602
+ function displayRetryLogStats(stats, options) {
603
+ const format = options.format || 'table';
604
+ if (format === 'json') {
605
+ console.log(JSON.stringify(stats, null, 2));
606
+ return;
607
+ }
608
+ else if (format === 'csv') {
609
+ console.log('Metric,Value,Unit,Change');
610
+ Object.entries(stats.summary).forEach(([key, value]) => {
611
+ console.log(`"${key}","${value.value}","${value.unit || ''}","${value.change || ''}"`);
612
+ });
613
+ return;
614
+ }
615
+ console.log(chalk_1.default.cyan.bold('\n📊 Retry Statistics & Patterns'));
616
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
617
+ // Overall Statistics
618
+ console.log(chalk_1.default.yellow.bold('\n📈 Overall Statistics'));
619
+ console.log(chalk_1.default.gray('─'.repeat(50)));
620
+ console.log(chalk_1.default.white('Total Requests:'), chalk_1.default.cyan(stats.summary.totalRequests.toLocaleString()));
621
+ console.log(chalk_1.default.white('Retried Requests:'), chalk_1.default.yellow(stats.summary.retriedRequests.toLocaleString()));
622
+ console.log(chalk_1.default.white('Retry Rate:'), chalk_1.default.cyan(`${(stats.summary.retryRate * 100).toFixed(1)}%`));
623
+ console.log(chalk_1.default.white('Successful Retries:'), chalk_1.default.green(stats.summary.successfulRetries.toLocaleString()));
624
+ console.log(chalk_1.default.white('Failed Retries:'), chalk_1.default.red(stats.summary.failedRetries.toLocaleString()));
625
+ console.log(chalk_1.default.white('Retry Success Rate:'), chalk_1.default.green(`${(stats.summary.retrySuccessRate * 100).toFixed(1)}%`));
626
+ console.log(chalk_1.default.white('Average Retry Attempts:'), chalk_1.default.cyan(stats.summary.avgRetryAttempts.toFixed(1)));
627
+ // Failure Type Analysis
628
+ if (stats.failureTypes) {
629
+ console.log(chalk_1.default.yellow.bold('\n🚨 Failure Type Analysis'));
630
+ console.log(chalk_1.default.gray('─'.repeat(50)));
631
+ Object.entries(stats.failureTypes).forEach(([type, data]) => {
632
+ console.log(chalk_1.default.white(`\n${type}:`));
633
+ console.log(chalk_1.default.gray(` Count: ${data.count.toLocaleString()}`));
634
+ console.log(chalk_1.default.gray(` Percentage: ${(data.percentage * 100).toFixed(1)}%`));
635
+ console.log(chalk_1.default.gray(` Success Rate: ${(data.successRate * 100).toFixed(1)}%`));
636
+ console.log(chalk_1.default.gray(` Avg Retries: ${data.avgRetries.toFixed(1)}`));
637
+ console.log(chalk_1.default.gray(` Avg Response Time: ${data.avgResponseTime}ms`));
638
+ });
639
+ }
640
+ // Model Performance
641
+ if (stats.modelPerformance) {
642
+ console.log(chalk_1.default.yellow.bold('\n🤖 Model Performance'));
643
+ console.log(chalk_1.default.gray('─'.repeat(50)));
644
+ Object.entries(stats.modelPerformance).forEach(([model, data]) => {
645
+ console.log(chalk_1.default.white(`\n${model}:`));
646
+ console.log(chalk_1.default.gray(` Retry Rate: ${(data.retryRate * 100).toFixed(1)}%`));
647
+ console.log(chalk_1.default.gray(` Success Rate: ${(data.successRate * 100).toFixed(1)}%`));
648
+ console.log(chalk_1.default.gray(` Avg Retries: ${data.avgRetries.toFixed(1)}`));
649
+ console.log(chalk_1.default.gray(` Avg Response Time: ${data.avgResponseTime}ms`));
650
+ });
651
+ }
652
+ // Time-based Patterns
653
+ if (stats.timePatterns) {
654
+ console.log(chalk_1.default.yellow.bold('\n⏰ Time-based Patterns'));
655
+ console.log(chalk_1.default.gray('─'.repeat(50)));
656
+ console.log(chalk_1.default.white('Peak Retry Hours:'), chalk_1.default.cyan(stats.timePatterns.peakHours.join(', ')));
657
+ console.log(chalk_1.default.white('Lowest Retry Hours:'), chalk_1.default.cyan(stats.timePatterns.lowestHours.join(', ')));
658
+ console.log(chalk_1.default.white('Weekly Pattern:'), chalk_1.default.cyan(stats.timePatterns.weeklyPattern));
659
+ console.log(chalk_1.default.white('Trend:'), chalk_1.default.cyan(stats.timePatterns.trend));
660
+ }
661
+ // Cost Impact
662
+ if (stats.costImpact) {
663
+ console.log(chalk_1.default.yellow.bold('\n💰 Cost Impact'));
664
+ console.log(chalk_1.default.gray('─'.repeat(50)));
665
+ console.log(chalk_1.default.white('Additional Cost from Retries:'), chalk_1.default.red(`$${stats.costImpact.additionalCost.toFixed(2)}`));
666
+ console.log(chalk_1.default.white('Cost Increase:'), chalk_1.default.red(`${(stats.costImpact.costIncrease * 100).toFixed(1)}%`));
667
+ console.log(chalk_1.default.white('Average Cost per Retry:'), chalk_1.default.cyan(`$${stats.costImpact.avgCostPerRetry.toFixed(4)}`));
668
+ }
669
+ // Recommendations
670
+ if (stats.recommendations && stats.recommendations.length > 0) {
671
+ console.log(chalk_1.default.yellow.bold('\n💡 Recommendations'));
672
+ console.log(chalk_1.default.gray('─'.repeat(50)));
673
+ stats.recommendations.forEach((rec, index) => {
674
+ console.log(chalk_1.default.white(`${index + 1}. ${rec.type}:`));
675
+ console.log(chalk_1.default.gray(` ${rec.description}`));
676
+ if (rec.impact) {
677
+ console.log(chalk_1.default.gray(` Impact: ${rec.impact}`));
678
+ }
679
+ if (rec.implementation) {
680
+ console.log(chalk_1.default.gray(` Implementation: ${rec.implementation}`));
681
+ }
682
+ });
683
+ }
684
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
685
+ }
686
+ //# sourceMappingURL=retry-log.js.map