@wengine-ai/claude-code-router 2.0.53 → 2.0.54

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 (58) hide show
  1. package/package.json +5 -4
  2. package/LICENSE +0 -21
  3. package/dist/cli.d.ts +0 -2
  4. package/dist/cli.js +0 -1193
  5. package/dist/cli.js.map +0 -1
  6. package/dist/index.html +0 -225
  7. package/dist/tiktoken_bg.wasm +0 -0
  8. package/dist/utils/activateCommand.d.ts +0 -4
  9. package/dist/utils/activateCommand.js +0 -24
  10. package/dist/utils/activateCommand.js.map +0 -1
  11. package/dist/utils/claudeSettings.d.ts +0 -12
  12. package/dist/utils/claudeSettings.js +0 -188
  13. package/dist/utils/claudeSettings.js.map +0 -1
  14. package/dist/utils/codeCommand.d.ts +0 -13
  15. package/dist/utils/codeCommand.js +0 -88
  16. package/dist/utils/codeCommand.js.map +0 -1
  17. package/dist/utils/createEnvVariables.d.ts +0 -5
  18. package/dist/utils/createEnvVariables.js +0 -83
  19. package/dist/utils/createEnvVariables.js.map +0 -1
  20. package/dist/utils/index.d.ts +0 -15
  21. package/dist/utils/index.js +0 -271
  22. package/dist/utils/index.js.map +0 -1
  23. package/dist/utils/installCommand.d.ts +0 -9
  24. package/dist/utils/installCommand.js +0 -47
  25. package/dist/utils/installCommand.js.map +0 -1
  26. package/dist/utils/modelSelector.d.ts +0 -1
  27. package/dist/utils/modelSelector.js +0 -402
  28. package/dist/utils/modelSelector.js.map +0 -1
  29. package/dist/utils/preset/commands.d.ts +0 -8
  30. package/dist/utils/preset/commands.js +0 -267
  31. package/dist/utils/preset/commands.js.map +0 -1
  32. package/dist/utils/preset/export.d.ts +0 -11
  33. package/dist/utils/preset/export.js +0 -97
  34. package/dist/utils/preset/export.js.map +0 -1
  35. package/dist/utils/preset/index.d.ts +0 -8
  36. package/dist/utils/preset/index.js +0 -32
  37. package/dist/utils/preset/index.js.map +0 -1
  38. package/dist/utils/preset/install-github.d.ts +0 -13
  39. package/dist/utils/preset/install-github.js +0 -161
  40. package/dist/utils/preset/install-github.js.map +0 -1
  41. package/dist/utils/preset/install.d.ts +0 -19
  42. package/dist/utils/preset/install.js +0 -239
  43. package/dist/utils/preset/install.js.map +0 -1
  44. package/dist/utils/processCheck.d.ts +0 -17
  45. package/dist/utils/processCheck.js +0 -161
  46. package/dist/utils/processCheck.js.map +0 -1
  47. package/dist/utils/prompt/schema-input.d.ts +0 -26
  48. package/dist/utils/prompt/schema-input.js +0 -183
  49. package/dist/utils/prompt/schema-input.js.map +0 -1
  50. package/dist/utils/status.d.ts +0 -1
  51. package/dist/utils/status.js +0 -28
  52. package/dist/utils/status.js.map +0 -1
  53. package/dist/utils/statusline.d.ts +0 -61
  54. package/dist/utils/statusline.js +0 -924
  55. package/dist/utils/statusline.js.map +0 -1
  56. package/dist/utils/update.d.ts +0 -18
  57. package/dist/utils/update.js +0 -74
  58. package/dist/utils/update.js.map +0 -1
@@ -1,924 +0,0 @@
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.parseStatusLineData = parseStatusLineData;
7
- const promises_1 = __importDefault(require("node:fs/promises"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- const child_process_1 = require("child_process");
10
- const node_os_1 = require("node:os");
11
- const claude_code_router_shared_1 = require("@wengine-ai/claude-code-router-shared");
12
- const json5_1 = __importDefault(require("json5"));
13
- // ANSI Color codes
14
- const COLORS = {
15
- reset: "\x1b[0m",
16
- bold: "\x1b[1m",
17
- dim: "\x1b[2m",
18
- // Standard colors
19
- black: "\x1b[30m",
20
- red: "\x1b[31m",
21
- green: "\x1b[32m",
22
- yellow: "\x1b[33m",
23
- blue: "\x1b[34m",
24
- magenta: "\x1b[35m",
25
- cyan: "\x1b[36m",
26
- white: "\x1b[37m",
27
- // Bright colors
28
- bright_black: "\x1b[90m",
29
- bright_red: "\x1b[91m",
30
- bright_green: "\x1b[92m",
31
- bright_yellow: "\x1b[93m",
32
- bright_blue: "\x1b[94m",
33
- bright_magenta: "\x1b[95m",
34
- bright_cyan: "\x1b[96m",
35
- bright_white: "\x1b[97m",
36
- // Background colors
37
- bg_black: "\x1b[40m",
38
- bg_red: "\x1b[41m",
39
- bg_green: "\x1b[42m",
40
- bg_yellow: "\x1b[43m",
41
- bg_blue: "\x1b[44m",
42
- bg_magenta: "\x1b[45m",
43
- bg_cyan: "\x1b[46m",
44
- bg_white: "\x1b[47m",
45
- // Bright background colors
46
- bg_bright_black: "\x1b[100m",
47
- bg_bright_red: "\x1b[101m",
48
- bg_bright_green: "\x1b[102m",
49
- bg_bright_yellow: "\x1b[103m",
50
- bg_bright_blue: "\x1b[104m",
51
- bg_bright_magenta: "\x1b[105m",
52
- bg_bright_cyan: "\x1b[106m",
53
- bg_bright_white: "\x1b[107m",
54
- };
55
- // Use TrueColor (24-bit color) to support hexadecimal colors
56
- const TRUE_COLOR_PREFIX = "\x1b[38;2;";
57
- const TRUE_COLOR_BG_PREFIX = "\x1b[48;2;";
58
- // Convert hexadecimal color to RGB format
59
- function hexToRgb(hex) {
60
- // Remove # and spaces
61
- hex = hex.replace(/^#/, '').trim();
62
- // Handle shorthand form (#RGB -> #RRGGBB)
63
- if (hex.length === 3) {
64
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
65
- }
66
- if (hex.length !== 6) {
67
- return null;
68
- }
69
- const r = parseInt(hex.substring(0, 2), 16);
70
- const g = parseInt(hex.substring(2, 4), 16);
71
- const b = parseInt(hex.substring(4, 6), 16);
72
- // Validate RGB values
73
- if (isNaN(r) || isNaN(g) || isNaN(b) || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
74
- return null;
75
- }
76
- return { r, g, b };
77
- }
78
- // Get color code
79
- function getColorCode(colorName) {
80
- // Check if it's a named ANSI color from COLORS dictionary
81
- if (COLORS[colorName]) {
82
- return COLORS[colorName];
83
- }
84
- // Check if it's a hexadecimal color
85
- if (colorName.startsWith('#') || /^[0-9a-fA-F]{6}$/.test(colorName) || /^[0-9a-fA-F]{3}$/.test(colorName)) {
86
- const rgb = hexToRgb(colorName);
87
- if (rgb) {
88
- return `${TRUE_COLOR_PREFIX}${rgb.r};${rgb.g};${rgb.b}m`;
89
- }
90
- }
91
- // Default to empty string
92
- return "";
93
- }
94
- // Variable replacement function, supports {{var}} format variable replacement
95
- function replaceVariables(text, variables) {
96
- return text.replace(/\{\{(\w+)\}\}/g, (_match, varName) => {
97
- return variables[varName] || "";
98
- });
99
- }
100
- // Execute script and get output
101
- async function executeScript(scriptPath, variables, options) {
102
- try {
103
- // Check if file exists
104
- await promises_1.default.access(scriptPath);
105
- // Use require to dynamically load script module
106
- const scriptModule = require(scriptPath);
107
- // If export is a function, call it with variables
108
- if (typeof scriptModule === 'function') {
109
- const result = scriptModule(variables, options);
110
- // If returns a Promise, wait for it to complete
111
- if (result instanceof Promise) {
112
- return await result;
113
- }
114
- return result;
115
- }
116
- // If export is a default function, call it
117
- if (scriptModule.default && typeof scriptModule.default === 'function') {
118
- const result = scriptModule.default(variables);
119
- // If returns a Promise, wait for it to complete
120
- if (result instanceof Promise) {
121
- return await result;
122
- }
123
- return result;
124
- }
125
- // If export is a string, return directly
126
- if (typeof scriptModule === 'string') {
127
- return scriptModule;
128
- }
129
- // If export is a default string, return it
130
- if (scriptModule.default && typeof scriptModule.default === 'string') {
131
- return scriptModule.default;
132
- }
133
- // Default to empty string
134
- return "";
135
- }
136
- catch (error) {
137
- console.error(`Error executing script ${scriptPath}:`, error);
138
- return "";
139
- }
140
- }
141
- // Default theme configuration - using Nerd Fonts icons and beautiful color scheme
142
- const DEFAULT_THEME = {
143
- modules: [
144
- {
145
- type: "workDir",
146
- icon: "󰉋", // nf-md-folder_outline
147
- text: "{{workDirName}}",
148
- color: "bright_blue"
149
- },
150
- {
151
- type: "gitBranch",
152
- icon: "", // nf-dev-git_branch
153
- text: "{{gitBranch}}",
154
- color: "bright_magenta"
155
- },
156
- {
157
- type: "model",
158
- icon: "󰚩", // nf-md-robot_outline
159
- text: "{{model}}",
160
- color: "bright_cyan"
161
- },
162
- {
163
- type: "contextCircle",
164
- icon: "○",
165
- text: "{{contextPercent}}%",
166
- color: "#22c55e"
167
- },
168
- {
169
- type: "usage",
170
- icon: "↑", // Up arrow
171
- text: "{{inputTokens}}",
172
- color: "bright_green"
173
- },
174
- {
175
- type: "usage",
176
- icon: "↓", // Down arrow
177
- text: "{{outputTokens}}",
178
- color: "bright_yellow"
179
- },
180
- {
181
- type: "totalTokens",
182
- icon: "📋",
183
- text: "{{totalTokens}}",
184
- color: "bright_white"
185
- }
186
- ]
187
- };
188
- // Powerline style theme configuration
189
- const POWERLINE_THEME = {
190
- modules: [
191
- {
192
- type: "workDir",
193
- icon: "󰉋", // nf-md-folder_outline
194
- text: "{{workDirName}}",
195
- color: "white",
196
- background: "bg_bright_blue"
197
- },
198
- {
199
- type: "gitBranch",
200
- icon: "", // nf-dev-git_branch
201
- text: "{{gitBranch}}",
202
- color: "white",
203
- background: "bg_bright_magenta"
204
- },
205
- {
206
- type: "model",
207
- icon: "󰚩", // nf-md-robot_outline
208
- text: "{{model}}",
209
- color: "white",
210
- background: "bg_bright_cyan"
211
- },
212
- {
213
- type: "contextCircle",
214
- icon: "○",
215
- text: "{{contextPercent}}%",
216
- color: "#22c55e",
217
- background: "bg_bright_black"
218
- },
219
- {
220
- type: "usage",
221
- icon: "↑", // Up arrow
222
- text: "{{inputTokens}}",
223
- color: "white",
224
- background: "bg_bright_green"
225
- },
226
- {
227
- type: "usage",
228
- icon: "↓", // Down arrow
229
- text: "{{outputTokens}}",
230
- color: "white",
231
- background: "bg_bright_yellow"
232
- },
233
- {
234
- type: "totalTokens",
235
- icon: "📋",
236
- text: "{{totalTokens}}",
237
- color: "white",
238
- background: "bg_bright_white"
239
- }
240
- ]
241
- };
242
- // Simple text theme configuration - fallback for when icons cannot be displayed
243
- const SIMPLE_THEME = {
244
- modules: [
245
- {
246
- type: "workDir",
247
- icon: "",
248
- text: "{{workDirName}}",
249
- color: "bright_blue"
250
- },
251
- {
252
- type: "gitBranch",
253
- icon: "",
254
- text: "{{gitBranch}}",
255
- color: "bright_magenta"
256
- },
257
- {
258
- type: "model",
259
- icon: "",
260
- text: "{{model}}",
261
- color: "bright_cyan"
262
- },
263
- {
264
- type: "contextCircle",
265
- icon: "○",
266
- text: "{{contextPercent}}%",
267
- color: "#22c55e"
268
- },
269
- {
270
- type: "usage",
271
- icon: "↑",
272
- text: "{{inputTokens}}",
273
- color: "bright_green"
274
- },
275
- {
276
- type: "usage",
277
- icon: "↓",
278
- text: "{{outputTokens}}",
279
- color: "bright_yellow"
280
- }
281
- ]
282
- };
283
- // Full theme configuration - showcasing all available modules
284
- const FULL_THEME = {
285
- modules: [
286
- {
287
- type: "workDir",
288
- icon: "󰉋",
289
- text: "{{workDirName}}",
290
- color: "bright_blue"
291
- },
292
- {
293
- type: "gitBranch",
294
- icon: "",
295
- text: "{{gitBranch}}",
296
- color: "bright_magenta"
297
- },
298
- {
299
- type: "model",
300
- icon: "󰚩",
301
- text: "{{model}}",
302
- color: "bright_cyan"
303
- },
304
- {
305
- type: "contextCircle",
306
- icon: "○",
307
- text: "{{contextPercent}}%",
308
- color: "#22c55e"
309
- },
310
- {
311
- type: "context",
312
- icon: "🪟",
313
- text: "{{contextPercent}}% / {{contextWindowSize}}",
314
- color: "bright_green"
315
- },
316
- {
317
- type: "speed",
318
- icon: "⚡",
319
- text: "{{tokenSpeed}} t/s {{isStreaming}}",
320
- color: "bright_yellow"
321
- },
322
- {
323
- type: "cost",
324
- icon: "💰",
325
- text: "{{cost}}",
326
- color: "bright_magenta"
327
- },
328
- {
329
- type: "duration",
330
- icon: "⏱️",
331
- text: "{{duration}}",
332
- color: "bright_white"
333
- },
334
- {
335
- type: "lines",
336
- icon: "📝",
337
- text: "+{{linesAdded}}/-{{linesRemoved}}",
338
- color: "bright_cyan"
339
- }
340
- ]
341
- };
342
- // Format token count with auto unit: k -> m -> b
343
- function formatTokenCount(count) {
344
- if (count < 1000) {
345
- return `${count}`;
346
- }
347
- if (count < 1_000_000) {
348
- const val = count / 1000;
349
- return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}k`;
350
- }
351
- if (count < 1_000_000_000) {
352
- const val = count / 1_000_000;
353
- return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}m`;
354
- }
355
- const val = count / 1_000_000_000;
356
- return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}b`;
357
- }
358
- // Format usage information with auto unit
359
- function formatUsage(input_tokens, output_tokens) {
360
- return `${formatTokenCount(input_tokens)} ${formatTokenCount(output_tokens)}`;
361
- }
362
- // Calculate context window usage percentage
363
- function calculateContextPercent(context_window) {
364
- if (!context_window || !context_window.current_usage) {
365
- return 0;
366
- }
367
- const { current_usage, context_window_size } = context_window;
368
- const currentTokens = current_usage.input_tokens +
369
- current_usage.cache_creation_input_tokens +
370
- current_usage.cache_read_input_tokens;
371
- return Math.round((currentTokens / context_window_size) * 100);
372
- }
373
- function getContextUsageColor(contextPercent) {
374
- const percent = parseInt(contextPercent || "0", 10);
375
- if (percent > 75) {
376
- return "#ef4444";
377
- }
378
- if (percent > 50) {
379
- return "#eab308";
380
- }
381
- return "#22c55e";
382
- }
383
- // Format cost display
384
- function formatCost(cost_usd) {
385
- if (cost_usd < 0.01) {
386
- return `${(cost_usd * 100).toFixed(2)}¢`;
387
- }
388
- return `$${cost_usd.toFixed(2)}`;
389
- }
390
- // Format duration
391
- function formatDuration(ms) {
392
- if (Number.isNaN(ms)) {
393
- return '';
394
- }
395
- if (ms < 1000) {
396
- return `${ms}ms`;
397
- }
398
- else if (ms < 60000) {
399
- return `${(ms / 1000).toFixed(1)}s`;
400
- }
401
- else {
402
- const minutes = Math.floor(ms / 60000);
403
- const seconds = ((ms % 60000) / 1000).toFixed(0);
404
- if (Number.isNaN(minutes) || Number.isNaN(seconds)) {
405
- return '';
406
- }
407
- return `${minutes}m${seconds}s`;
408
- }
409
- }
410
- // Read token-speed statistics from temp file
411
- async function getTokenSpeedStats(sessionId) {
412
- try {
413
- // Use system temp directory
414
- const tempDir = node_path_1.default.join((0, node_os_1.tmpdir)(), 'claude-code-router');
415
- // Check if temp directory exists
416
- try {
417
- await promises_1.default.access(tempDir);
418
- }
419
- catch {
420
- return null;
421
- }
422
- const statsFilePath = node_path_1.default.join(tempDir, `session-${sessionId}.json`);
423
- try {
424
- await promises_1.default.access(statsFilePath);
425
- }
426
- catch {
427
- return null;
428
- }
429
- // Read stats file
430
- const content = await promises_1.default.readFile(statsFilePath, 'utf-8');
431
- const data = JSON.parse(content);
432
- // Check if data has tokensPerSecond
433
- if (data.tokensPerSecond !== undefined && data.tokensPerSecond > 0) {
434
- // Check if timestamp is within last 3 seconds
435
- const now = Date.now();
436
- const timestamp = data.timestamp || 0;
437
- const ageInSeconds = (now - timestamp) / 1000;
438
- // If data is older than 3 seconds, return 0 speed
439
- if (ageInSeconds > 3) {
440
- return {
441
- tokensPerSecond: 0,
442
- timeToFirstToken: data.timeToFirstToken
443
- };
444
- }
445
- const result = {
446
- tokensPerSecond: parseInt(data.tokensPerSecond),
447
- timeToFirstToken: data.timeToFirstToken
448
- };
449
- return result;
450
- }
451
- return null;
452
- }
453
- catch (error) {
454
- // Silently fail on error
455
- return null;
456
- }
457
- }
458
- // Read theme configuration from user home directory
459
- async function getProjectThemeConfig() {
460
- try {
461
- // Only use fixed configuration file in home directory
462
- const configPath = claude_code_router_shared_1.CONFIG_FILE;
463
- // Check if configuration file exists
464
- try {
465
- await promises_1.default.access(configPath);
466
- }
467
- catch {
468
- return { theme: null, style: 'default' };
469
- }
470
- const configContent = await promises_1.default.readFile(configPath, "utf-8");
471
- const config = json5_1.default.parse(configContent);
472
- // Check if there's StatusLine configuration
473
- if (config.StatusLine) {
474
- // Get current style, default to 'default'
475
- const currentStyle = config.StatusLine.currentStyle || 'default';
476
- // Check if there's configuration for the corresponding style
477
- if (config.StatusLine[currentStyle] && config.StatusLine[currentStyle].modules) {
478
- return { theme: config.StatusLine[currentStyle], style: currentStyle };
479
- }
480
- }
481
- }
482
- catch (error) {
483
- // Return null if reading fails
484
- // console.error("Failed to read theme config:", error);
485
- }
486
- return { theme: null, style: 'default' };
487
- }
488
- // Read theme configuration from preset
489
- async function getPresetThemeConfig(presetName) {
490
- try {
491
- // Read preset manifest
492
- const manifest = await (0, claude_code_router_shared_1.readPresetFile)(presetName);
493
- if (!manifest) {
494
- return { theme: null, style: 'default' };
495
- }
496
- // Load preset configuration (applies userValues if present)
497
- const presetDir = (0, claude_code_router_shared_1.getPresetDir)(presetName);
498
- const config = (0, claude_code_router_shared_1.loadConfigFromManifest)(manifest, presetDir);
499
- // Check if there's StatusLine configuration in preset
500
- if (config.StatusLine) {
501
- // Get current style, default to 'default'
502
- const currentStyle = config.StatusLine.currentStyle || 'default';
503
- // Check if there's configuration for the corresponding style
504
- if (config.StatusLine[currentStyle] && config.StatusLine[currentStyle].modules) {
505
- return { theme: config.StatusLine[currentStyle], style: currentStyle };
506
- }
507
- }
508
- }
509
- catch (error) {
510
- // Return null if reading fails
511
- // console.error("Failed to read preset theme config:", error);
512
- }
513
- return { theme: null, style: 'default' };
514
- }
515
- // Check if simple theme should be used (fallback scheme)
516
- // When environment variable USE_SIMPLE_ICONS is set, or when a terminal that might not support Nerd Fonts is detected
517
- function shouldUseSimpleTheme() {
518
- // Check environment variable
519
- if (process.env.USE_SIMPLE_ICONS === 'true') {
520
- return true;
521
- }
522
- // Check terminal type (some common terminals that don't support complex icons)
523
- const term = process.env.TERM || '';
524
- const unsupportedTerms = ['dumb', 'unknown'];
525
- if (unsupportedTerms.includes(term)) {
526
- return true;
527
- }
528
- // By default, assume terminal supports Nerd Fonts
529
- return false;
530
- }
531
- // Check if Nerd Fonts icons can be displayed correctly
532
- // By checking terminal font information or using heuristic methods
533
- function canDisplayNerdFonts() {
534
- // If environment variable explicitly specifies simple icons, Nerd Fonts cannot be displayed
535
- if (process.env.USE_SIMPLE_ICONS === 'true') {
536
- return false;
537
- }
538
- // Check some common terminal environment variables that support Nerd Fonts
539
- const fontEnvVars = ['NERD_FONT', 'NERDFONT', 'FONT'];
540
- for (const envVar of fontEnvVars) {
541
- const value = process.env[envVar];
542
- if (value && (value.includes('Nerd') || value.includes('nerd'))) {
543
- return true;
544
- }
545
- }
546
- // Check terminal type
547
- const termProgram = process.env.TERM_PROGRAM || '';
548
- const supportedTerminals = ['iTerm.app', 'vscode', 'Hyper', 'kitty', 'alacritty'];
549
- if (supportedTerminals.includes(termProgram)) {
550
- return true;
551
- }
552
- // Check COLORTERM environment variable
553
- const colorTerm = process.env.COLORTERM || '';
554
- if (colorTerm.includes('truecolor') || colorTerm.includes('24bit')) {
555
- return true;
556
- }
557
- // By default, assume Nerd Fonts can be displayed (but allow users to override via environment variables)
558
- return process.env.USE_SIMPLE_ICONS !== 'true';
559
- }
560
- async function parseStatusLineData(input, presetName) {
561
- try {
562
- // Check if simple theme should be used
563
- const useSimpleTheme = shouldUseSimpleTheme();
564
- // Check if Nerd Fonts icons can be displayed
565
- const canDisplayNerd = canDisplayNerdFonts();
566
- // Determine which theme to use: use simple theme if user forces it or Nerd Fonts cannot be displayed
567
- const effectiveTheme = useSimpleTheme || !canDisplayNerd ? SIMPLE_THEME : DEFAULT_THEME;
568
- // Get theme configuration: preset config > home directory config > default theme
569
- let projectTheme = null;
570
- let currentStyle = 'default';
571
- if (presetName) {
572
- // Try to get theme configuration from preset first
573
- const presetConfig = await getPresetThemeConfig(presetName);
574
- projectTheme = presetConfig.theme;
575
- currentStyle = presetConfig.style;
576
- }
577
- // If preset theme not found or no preset specified, try home directory config
578
- if (!projectTheme) {
579
- const homeConfig = await getProjectThemeConfig();
580
- projectTheme = homeConfig.theme;
581
- currentStyle = homeConfig.style;
582
- }
583
- const theme = projectTheme || effectiveTheme;
584
- // Get current working directory and Git branch
585
- const workDir = input.workspace.current_dir;
586
- let gitBranch = "";
587
- try {
588
- // Try to get Git branch name
589
- gitBranch = (0, child_process_1.execSync)("git branch --show-current", {
590
- cwd: workDir,
591
- stdio: ["pipe", "pipe", "ignore"],
592
- })
593
- .toString()
594
- .trim();
595
- }
596
- catch (error) {
597
- // If not a Git repository or retrieval fails, ignore error
598
- }
599
- // Read last assistant message from transcript_path file
600
- const transcriptContent = await promises_1.default.readFile(input.transcript_path, "utf-8");
601
- const lines = transcriptContent.trim().split("\n");
602
- // Traverse in reverse to find last assistant message
603
- let model = "";
604
- let inputTokens = 0;
605
- let outputTokens = 0;
606
- // Also accumulate total tokens from all assistant messages
607
- let sessionTotalInputTokens = 0;
608
- let sessionTotalOutputTokens = 0;
609
- let sessionTotalCacheCreationTokens = 0;
610
- let sessionTotalCacheReadTokens = 0;
611
- for (let i = lines.length - 1; i >= 0; i--) {
612
- try {
613
- const message = JSON.parse(lines[i]);
614
- if (message.type === "assistant" && message.message.model) {
615
- // Accumulate tokens for session total
616
- if (message.message.usage) {
617
- sessionTotalInputTokens += message.message.usage.input_tokens;
618
- sessionTotalOutputTokens += message.message.usage.output_tokens;
619
- sessionTotalCacheCreationTokens += message.message.usage.cache_creation_input_tokens || 0;
620
- sessionTotalCacheReadTokens += message.message.usage.cache_read_input_tokens || 0;
621
- }
622
- // Get last message's model and tokens
623
- if (!model) {
624
- model = message.message.model;
625
- if (message.message.usage) {
626
- inputTokens = message.message.usage.input_tokens;
627
- outputTokens = message.message.usage.output_tokens;
628
- }
629
- }
630
- }
631
- }
632
- catch (parseError) {
633
- // Ignore parse errors, continue searching
634
- continue;
635
- }
636
- }
637
- // If model name not retrieved from transcript, try to get from configuration file
638
- if (!model) {
639
- try {
640
- // Get project configuration file path
641
- const projectConfigPath = node_path_1.default.join(workDir, ".claude-code-router", "config.json");
642
- let configPath = projectConfigPath;
643
- // Check if project configuration file exists, if not use user home directory configuration file
644
- try {
645
- await promises_1.default.access(projectConfigPath);
646
- }
647
- catch {
648
- configPath = claude_code_router_shared_1.CONFIG_FILE;
649
- }
650
- // Read configuration file
651
- const configContent = await promises_1.default.readFile(configPath, "utf-8");
652
- const config = json5_1.default.parse(configContent);
653
- // Get model name from Router field's default content
654
- if (config.Router && config.Router.default) {
655
- const [, defaultModel] = config.Router.default.split(",");
656
- if (defaultModel) {
657
- model = defaultModel.trim();
658
- }
659
- }
660
- }
661
- catch (configError) {
662
- // If configuration file reading fails, ignore error
663
- }
664
- }
665
- // If still unable to get model name, use display_name from input JSON data's model field
666
- if (!model) {
667
- model = input.model.display_name;
668
- }
669
- // Get working directory name
670
- const workDirName = workDir.split("/").pop() || "";
671
- // Format usage information
672
- const usage = formatUsage(inputTokens, outputTokens);
673
- const [formattedInputTokens, formattedOutputTokens] = usage.split(" ");
674
- // Get token-speed statistics
675
- const tokenSpeedData = await getTokenSpeedStats(input.session_id);
676
- const formattedTokenSpeed = tokenSpeedData && tokenSpeedData.tokensPerSecond > 0
677
- ? tokenSpeedData.tokensPerSecond.toString()
678
- : '';
679
- // Check if streaming (has active token speed)
680
- const isStreaming = tokenSpeedData !== null && tokenSpeedData.tokensPerSecond > 0;
681
- const streamingIndicator = isStreaming ? '[Streaming]' : '';
682
- // Format time to first token
683
- let formattedTimeToFirstToken = '';
684
- if (tokenSpeedData?.timeToFirstToken !== undefined) {
685
- formattedTimeToFirstToken = formatDuration(tokenSpeedData.timeToFirstToken);
686
- }
687
- // Process context window data
688
- const contextPercent = input.context_window ? calculateContextPercent(input.context_window) : 0;
689
- // Always use transcript-accumulated values for stable monotonically-increasing totals
690
- // Fallback to context_window only when transcript has no data
691
- const totalInputTokens = sessionTotalInputTokens || input.context_window?.total_input_tokens || 0;
692
- const totalOutputTokens = sessionTotalOutputTokens || input.context_window?.total_output_tokens || 0;
693
- const totalCacheTokens = sessionTotalCacheCreationTokens + sessionTotalCacheReadTokens;
694
- const contextWindowSize = input.context_window?.context_window_size || 0;
695
- // Process cost data
696
- const totalCost = input.cost?.total_cost_usd || 0;
697
- const formattedCost = totalCost > 0 ? formatCost(totalCost) : '';
698
- const totalDuration = input.cost?.total_duration_ms || 0;
699
- const formattedDuration = totalDuration > 0 ? formatDuration(totalDuration) : '';
700
- const linesAdded = input.cost?.total_lines_added || 0;
701
- const linesRemoved = input.cost?.total_lines_removed || 0;
702
- // Define variable replacement mapping
703
- const variables = {
704
- workDirName,
705
- gitBranch,
706
- model,
707
- inputTokens: formattedInputTokens,
708
- outputTokens: formattedOutputTokens,
709
- tokenSpeed: formattedTokenSpeed || '0',
710
- isStreaming: isStreaming ? 'streaming' : '',
711
- timeToFirstToken: formattedTimeToFirstToken,
712
- contextPercent: contextPercent.toString(),
713
- streamingIndicator,
714
- contextWindowSize: formatTokenCount(contextWindowSize),
715
- totalInputTokens: formatTokenCount(totalInputTokens),
716
- totalOutputTokens: formatTokenCount(totalOutputTokens),
717
- totalTokens: formatTokenCount(totalInputTokens + totalOutputTokens + totalCacheTokens),
718
- cost: formattedCost || '',
719
- duration: formattedDuration || '',
720
- linesAdded: linesAdded.toString(),
721
- linesRemoved: linesRemoved.toString(),
722
- netLines: (linesAdded - linesRemoved).toString(),
723
- version: input.version || '',
724
- sessionId: input.session_id.substring(0, 8)
725
- };
726
- // Determine the style to use
727
- const isPowerline = currentStyle === 'powerline';
728
- // Render status line based on style
729
- if (isPowerline) {
730
- return await renderPowerlineStyle(theme, variables);
731
- }
732
- else {
733
- return await renderDefaultStyle(theme, variables);
734
- }
735
- }
736
- catch (error) {
737
- // Return empty string on error
738
- return "";
739
- }
740
- }
741
- // Render default style status line
742
- async function renderDefaultStyle(theme, variables) {
743
- const modules = theme.modules || DEFAULT_THEME.modules;
744
- const parts = [];
745
- // Iterate through module array, rendering each module (maximum 10)
746
- for (let i = 0; i < modules.length; i++) {
747
- const module = modules[i];
748
- const dynamicColor = module.type === "contextCircle"
749
- ? getContextUsageColor(variables.contextPercent)
750
- : module.color || "";
751
- const color = dynamicColor ? getColorCode(dynamicColor) : "";
752
- const background = module.background ? getColorCode(module.background) : "";
753
- const icon = module.icon || "";
754
- // If script type, execute script to get text
755
- let text = "";
756
- if (module.type === "script" && module.scriptPath) {
757
- text = await executeScript(module.scriptPath, variables, module.options);
758
- }
759
- else {
760
- text = replaceVariables(module.text, variables);
761
- }
762
- // Build display text
763
- let displayText = "";
764
- if (icon) {
765
- displayText += `${icon} `;
766
- }
767
- displayText += text;
768
- // Skip module if displayText is empty or only has icon without actual text
769
- if (!displayText || !text) {
770
- continue;
771
- }
772
- // Build module string (plain text, Claude Code statusline does not support ANSI)
773
- parts.push(displayText);
774
- }
775
- // Join all parts with spaces
776
- return parts.join(" ");
777
- }
778
- // Powerline symbols
779
- const SEP_RIGHT = "\uE0B0"; // 
780
- // Color numbers (256-color table)
781
- const COLOR_MAP = {
782
- // Basic colors mapped to 256 colors
783
- black: 0,
784
- red: 1,
785
- green: 2,
786
- yellow: 3,
787
- blue: 4,
788
- magenta: 5,
789
- cyan: 6,
790
- white: 7,
791
- bright_black: 8,
792
- bright_red: 9,
793
- bright_green: 10,
794
- bright_yellow: 11,
795
- bright_blue: 12,
796
- bright_magenta: 13,
797
- bright_cyan: 14,
798
- bright_white: 15,
799
- // Bright background color mapping
800
- bg_black: 0,
801
- bg_red: 1,
802
- bg_green: 2,
803
- bg_yellow: 3,
804
- bg_blue: 4,
805
- bg_magenta: 5,
806
- bg_cyan: 6,
807
- bg_white: 7,
808
- bg_bright_black: 8,
809
- bg_bright_red: 9,
810
- bg_bright_green: 10,
811
- bg_bright_yellow: 11,
812
- bg_bright_blue: 12,
813
- bg_bright_magenta: 13,
814
- bg_bright_cyan: 14,
815
- bg_bright_white: 15,
816
- // Custom color mapping
817
- bg_bright_orange: 202,
818
- bg_bright_purple: 129,
819
- };
820
- // Get TrueColor RGB value
821
- function getTrueColorRgb(colorName) {
822
- // If predefined color, return corresponding RGB
823
- if (COLOR_MAP[colorName] !== undefined) {
824
- const color256 = COLOR_MAP[colorName];
825
- return color256ToRgb(color256);
826
- }
827
- // Handle hexadecimal color
828
- if (colorName.startsWith('#') || /^[0-9a-fA-F]{6}$/.test(colorName) || /^[0-9a-fA-F]{3}$/.test(colorName)) {
829
- return hexToRgb(colorName);
830
- }
831
- // Handle background color hexadecimal
832
- if (colorName.startsWith('bg_#')) {
833
- return hexToRgb(colorName.substring(3));
834
- }
835
- return null;
836
- }
837
- // Convert 256-color table index to RGB value
838
- function color256ToRgb(index) {
839
- if (index < 0 || index > 255)
840
- return null;
841
- // ANSI 256-color table conversion
842
- if (index < 16) {
843
- // Basic colors
844
- const basicColors = [
845
- [0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0],
846
- [0, 0, 128], [128, 0, 128], [0, 128, 128], [192, 192, 192],
847
- [128, 128, 128], [255, 0, 0], [0, 255, 0], [255, 255, 0],
848
- [0, 0, 255], [255, 0, 255], [0, 255, 255], [255, 255, 255]
849
- ];
850
- return { r: basicColors[index][0], g: basicColors[index][1], b: basicColors[index][2] };
851
- }
852
- else if (index < 232) {
853
- // 216 colors: 6×6×6 color cube
854
- const i = index - 16;
855
- const r = Math.floor(i / 36);
856
- const g = Math.floor((i % 36) / 6);
857
- const b = i % 6;
858
- const rgb = [0, 95, 135, 175, 215, 255];
859
- return { r: rgb[r], g: rgb[g], b: rgb[b] };
860
- }
861
- else {
862
- // Grayscale colors
863
- const gray = 8 + (index - 232) * 10;
864
- return { r: gray, g: gray, b: gray };
865
- }
866
- }
867
- // Generate a seamless segment: text displayed on bgN, separator transitions from bgN to nextBgN
868
- function segment(text, textFg, bgColor, nextBgColor) {
869
- // Plain text output (Claude Code statusline does not support ANSI)
870
- const body = ` ${text} `;
871
- if (nextBgColor != null) {
872
- return body + ` ${SEP_RIGHT} `;
873
- }
874
- return body;
875
- }
876
- // Render Powerline style status line
877
- async function renderPowerlineStyle(theme, variables) {
878
- const modules = theme.modules || POWERLINE_THEME.modules;
879
- const segments = [];
880
- // Iterate through module array, rendering each module (maximum 10)
881
- for (let i = 0; i < Math.min(modules.length, 10); i++) {
882
- const module = modules[i];
883
- const color = module.type === "contextCircle"
884
- ? getContextUsageColor(variables.contextPercent)
885
- : module.color || "white";
886
- const backgroundName = module.background || "";
887
- const icon = module.icon || "";
888
- // If script type, execute script to get text
889
- let text = "";
890
- if (module.type === "script" && module.scriptPath) {
891
- text = await executeScript(module.scriptPath, variables);
892
- }
893
- else if (module.type === "speed") {
894
- // speed module: use tokenSpeed variable
895
- text = replaceVariables(module.text, variables);
896
- }
897
- else {
898
- text = replaceVariables(module.text, variables);
899
- }
900
- // Build display text
901
- let displayText = "";
902
- if (icon) {
903
- displayText += `${icon} `;
904
- }
905
- displayText += text;
906
- // Skip module if displayText is empty or only has icon without actual text
907
- if (!displayText || !text) {
908
- continue;
909
- }
910
- // Get next module's background color (for separator)
911
- let nextBackground = null;
912
- if (i < modules.length - 1) {
913
- const nextModule = modules[i + 1];
914
- nextBackground = nextModule.background || null;
915
- }
916
- // Use module-defined background color, or provide default background color for Powerline style
917
- const actualBackground = backgroundName || "bg_bright_blue";
918
- // Generate segment, supports hexadecimal colors
919
- const segmentStr = segment(displayText, color, actualBackground, nextBackground);
920
- segments.push(segmentStr);
921
- }
922
- return segments.join("");
923
- }
924
- //# sourceMappingURL=statusline.js.map