@praeviso/code-env-switch 0.1.3 → 0.1.5
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.
- package/README.md +17 -0
- package/README_zh.md +17 -0
- package/bin/commands/list.js +44 -2
- package/bin/statusline/debug.js +42 -0
- package/bin/statusline/format.js +57 -0
- package/bin/statusline/git.js +96 -0
- package/bin/statusline/index.js +107 -558
- package/bin/statusline/input.js +167 -0
- package/bin/statusline/style.js +22 -0
- package/bin/statusline/types.js +2 -0
- package/bin/statusline/usage/claude.js +181 -0
- package/bin/statusline/usage/codex.js +168 -0
- package/bin/statusline/usage.js +67 -0
- package/bin/statusline/utils.js +35 -0
- package/bin/usage/index.js +396 -41
- package/bin/usage/pricing.js +303 -0
- package/code-env.example.json +55 -0
- package/package.json +1 -1
- package/src/commands/list.ts +74 -4
- package/src/statusline/debug.ts +40 -0
- package/src/statusline/format.ts +67 -0
- package/src/statusline/git.ts +82 -0
- package/src/statusline/index.ts +143 -764
- package/src/statusline/input.ts +159 -0
- package/src/statusline/style.ts +19 -0
- package/src/statusline/types.ts +111 -0
- package/src/statusline/usage/claude.ts +299 -0
- package/src/statusline/usage/codex.ts +263 -0
- package/src/statusline/usage.ts +80 -0
- package/src/statusline/utils.ts +27 -0
- package/src/types.ts +23 -0
- package/src/usage/index.ts +519 -35
- package/src/usage/pricing.ts +323 -0
- package/PLAN.md +0 -33
package/bin/statusline/index.js
CHANGED
|
@@ -1,542 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildStatuslineResult = buildStatuslineResult;
|
|
4
|
-
/**
|
|
5
|
-
* Statusline builder
|
|
6
|
-
*/
|
|
7
|
-
const fs = require("fs");
|
|
8
|
-
const path = require("path");
|
|
9
|
-
const child_process_1 = require("child_process");
|
|
10
|
-
const constants_1 = require("../constants");
|
|
11
4
|
const type_1 = require("../profile/type");
|
|
12
5
|
const usage_1 = require("../usage");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const ICON_REVIEW = "📝";
|
|
21
|
-
const ICON_CWD = "📁";
|
|
22
|
-
function colorize(text, colorCode) {
|
|
23
|
-
if (!COLOR_ENABLED)
|
|
24
|
-
return text;
|
|
25
|
-
return `\x1b[${colorCode}m${text}${ANSI_RESET}`;
|
|
26
|
-
}
|
|
27
|
-
function dim(text) {
|
|
28
|
-
return colorize(text, "2");
|
|
29
|
-
}
|
|
30
|
-
function getCwdSegment(cwd) {
|
|
31
|
-
if (!cwd)
|
|
32
|
-
return null;
|
|
33
|
-
const base = path.basename(cwd) || cwd;
|
|
34
|
-
const segment = `${ICON_CWD} ${base}`;
|
|
35
|
-
return dim(segment);
|
|
36
|
-
}
|
|
37
|
-
function isRecord(value) {
|
|
38
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39
|
-
}
|
|
40
|
-
function readStdinJson() {
|
|
41
|
-
if (process.stdin.isTTY)
|
|
42
|
-
return null;
|
|
43
|
-
try {
|
|
44
|
-
const raw = fs.readFileSync(0, "utf8");
|
|
45
|
-
const trimmed = raw.trim();
|
|
46
|
-
if (!trimmed)
|
|
47
|
-
return null;
|
|
48
|
-
const parsed = JSON.parse(trimmed);
|
|
49
|
-
if (!isRecord(parsed))
|
|
50
|
-
return null;
|
|
51
|
-
return parsed;
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
function firstNonEmpty(...values) {
|
|
58
|
-
for (const value of values) {
|
|
59
|
-
if (value === null || value === undefined)
|
|
60
|
-
continue;
|
|
61
|
-
const text = String(value).trim();
|
|
62
|
-
if (text)
|
|
63
|
-
return text;
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
function coerceNumber(value) {
|
|
68
|
-
if (value === null || value === undefined || value === "")
|
|
69
|
-
return null;
|
|
70
|
-
const num = Number(value);
|
|
71
|
-
if (!Number.isFinite(num))
|
|
72
|
-
return null;
|
|
73
|
-
return num;
|
|
74
|
-
}
|
|
75
|
-
function firstNumber(...values) {
|
|
76
|
-
for (const value of values) {
|
|
77
|
-
const num = coerceNumber(value);
|
|
78
|
-
if (num !== null)
|
|
79
|
-
return num;
|
|
80
|
-
}
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
function normalizeTypeValue(value) {
|
|
84
|
-
if (!value)
|
|
85
|
-
return null;
|
|
86
|
-
const normalized = (0, type_1.normalizeType)(value);
|
|
87
|
-
if (normalized)
|
|
88
|
-
return normalized;
|
|
89
|
-
const trimmed = String(value).trim();
|
|
90
|
-
return trimmed ? trimmed : null;
|
|
91
|
-
}
|
|
92
|
-
function detectTypeFromEnv() {
|
|
93
|
-
const matches = constants_1.DEFAULT_PROFILE_TYPES.filter((type) => {
|
|
94
|
-
const suffix = type.toUpperCase();
|
|
95
|
-
return (process.env[`CODE_ENV_PROFILE_KEY_${suffix}`] ||
|
|
96
|
-
process.env[`CODE_ENV_PROFILE_NAME_${suffix}`]);
|
|
97
|
-
});
|
|
98
|
-
if (matches.length === 1)
|
|
99
|
-
return matches[0];
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
function resolveEnvProfile(type) {
|
|
103
|
-
const genericKey = process.env.CODE_ENV_PROFILE_KEY || null;
|
|
104
|
-
const genericName = process.env.CODE_ENV_PROFILE_NAME || null;
|
|
105
|
-
if (!type) {
|
|
106
|
-
return { key: genericKey, name: genericName };
|
|
107
|
-
}
|
|
108
|
-
const suffix = type.toUpperCase();
|
|
109
|
-
const key = process.env[`CODE_ENV_PROFILE_KEY_${suffix}`] || genericKey;
|
|
110
|
-
const name = process.env[`CODE_ENV_PROFILE_NAME_${suffix}`] || genericName;
|
|
111
|
-
return { key: key || null, name: name || null };
|
|
112
|
-
}
|
|
113
|
-
function getModelFromInput(input) {
|
|
114
|
-
if (!input)
|
|
115
|
-
return null;
|
|
116
|
-
const raw = input.model;
|
|
117
|
-
if (!raw)
|
|
118
|
-
return null;
|
|
119
|
-
if (typeof raw === "string")
|
|
120
|
-
return raw;
|
|
121
|
-
if (isRecord(raw)) {
|
|
122
|
-
const displayName = raw.displayName || raw.display_name;
|
|
123
|
-
if (displayName)
|
|
124
|
-
return String(displayName);
|
|
125
|
-
if (raw.id)
|
|
126
|
-
return String(raw.id);
|
|
127
|
-
}
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
function getModelProviderFromInput(input) {
|
|
131
|
-
if (!input || !input.model_provider)
|
|
132
|
-
return null;
|
|
133
|
-
const provider = String(input.model_provider).trim();
|
|
134
|
-
return provider ? provider : null;
|
|
135
|
-
}
|
|
136
|
-
function getInputProfile(input) {
|
|
137
|
-
if (!input || !isRecord(input.profile))
|
|
138
|
-
return null;
|
|
139
|
-
return input.profile;
|
|
140
|
-
}
|
|
141
|
-
function getInputUsage(input) {
|
|
142
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
143
|
-
if (!input)
|
|
144
|
-
return null;
|
|
145
|
-
if (isRecord(input.usage)) {
|
|
146
|
-
return input.usage;
|
|
147
|
-
}
|
|
148
|
-
const tokenUsage = input.token_usage;
|
|
149
|
-
if (tokenUsage !== null && tokenUsage !== undefined) {
|
|
150
|
-
if (typeof tokenUsage === "number") {
|
|
151
|
-
return {
|
|
152
|
-
todayTokens: null,
|
|
153
|
-
totalTokens: coerceNumber(tokenUsage),
|
|
154
|
-
inputTokens: null,
|
|
155
|
-
outputTokens: null,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
if (isRecord(tokenUsage)) {
|
|
159
|
-
const record = tokenUsage;
|
|
160
|
-
const todayTokens = (_a = firstNumber(record.todayTokens, record.today, record.today_tokens, record.daily, record.daily_tokens)) !== null && _a !== void 0 ? _a : null;
|
|
161
|
-
const totalTokens = (_b = firstNumber(record.totalTokens, record.total, record.total_tokens)) !== null && _b !== void 0 ? _b : null;
|
|
162
|
-
const inputTokens = (_c = firstNumber(record.inputTokens, record.input, record.input_tokens)) !== null && _c !== void 0 ? _c : null;
|
|
163
|
-
const outputTokens = (_d = firstNumber(record.outputTokens, record.output, record.output_tokens)) !== null && _d !== void 0 ? _d : null;
|
|
164
|
-
const cacheRead = (_e = firstNumber(record.cache_read_input_tokens, record.cacheReadInputTokens, record.cache_read, record.cacheRead)) !== null && _e !== void 0 ? _e : null;
|
|
165
|
-
const cacheWrite = (_f = firstNumber(record.cache_creation_input_tokens, record.cacheCreationInputTokens, record.cache_write_input_tokens, record.cacheWriteInputTokens, record.cache_write, record.cacheWrite)) !== null && _f !== void 0 ? _f : null;
|
|
166
|
-
if (todayTokens === null &&
|
|
167
|
-
totalTokens === null &&
|
|
168
|
-
inputTokens === null &&
|
|
169
|
-
outputTokens === null &&
|
|
170
|
-
cacheRead === null &&
|
|
171
|
-
cacheWrite === null) {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
const hasCacheTokens = cacheRead !== null || cacheWrite !== null;
|
|
175
|
-
const computedTotal = hasCacheTokens
|
|
176
|
-
? (inputTokens || 0) +
|
|
177
|
-
(outputTokens || 0) +
|
|
178
|
-
(cacheRead || 0) +
|
|
179
|
-
(cacheWrite || 0)
|
|
180
|
-
: null;
|
|
181
|
-
const resolvedTodayTokens = hasCacheTokens
|
|
182
|
-
? (_g = todayTokens !== null && todayTokens !== void 0 ? todayTokens : totalTokens) !== null && _g !== void 0 ? _g : computedTotal
|
|
183
|
-
: todayTokens;
|
|
184
|
-
return {
|
|
185
|
-
todayTokens: resolvedTodayTokens,
|
|
186
|
-
totalTokens: totalTokens !== null && totalTokens !== void 0 ? totalTokens : null,
|
|
187
|
-
inputTokens,
|
|
188
|
-
outputTokens,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
const contextWindow = isRecord(input.context_window)
|
|
193
|
-
? input.context_window
|
|
194
|
-
: isRecord(input.contextWindow)
|
|
195
|
-
? input.contextWindow
|
|
196
|
-
: null;
|
|
197
|
-
if (!contextWindow)
|
|
198
|
-
return null;
|
|
199
|
-
const totalInputTokens = (_h = firstNumber(contextWindow.total_input_tokens, contextWindow.totalInputTokens)) !== null && _h !== void 0 ? _h : null;
|
|
200
|
-
const totalOutputTokens = (_j = firstNumber(contextWindow.total_output_tokens, contextWindow.totalOutputTokens)) !== null && _j !== void 0 ? _j : null;
|
|
201
|
-
if (totalInputTokens !== null || totalOutputTokens !== null) {
|
|
202
|
-
return {
|
|
203
|
-
todayTokens: null,
|
|
204
|
-
totalTokens: null,
|
|
205
|
-
inputTokens: totalInputTokens,
|
|
206
|
-
outputTokens: totalOutputTokens,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const currentUsage = isRecord(contextWindow.current_usage)
|
|
210
|
-
? contextWindow.current_usage
|
|
211
|
-
: isRecord(contextWindow.currentUsage)
|
|
212
|
-
? contextWindow.currentUsage
|
|
213
|
-
: null;
|
|
214
|
-
if (!currentUsage)
|
|
215
|
-
return null;
|
|
216
|
-
const inputTokens = (_k = firstNumber(currentUsage.input_tokens, currentUsage.inputTokens)) !== null && _k !== void 0 ? _k : null;
|
|
217
|
-
const outputTokens = (_l = firstNumber(currentUsage.output_tokens, currentUsage.outputTokens)) !== null && _l !== void 0 ? _l : null;
|
|
218
|
-
const cacheRead = (_m = firstNumber(currentUsage.cache_read_input_tokens, currentUsage.cacheReadInputTokens)) !== null && _m !== void 0 ? _m : null;
|
|
219
|
-
const cacheWrite = (_o = firstNumber(currentUsage.cache_creation_input_tokens, currentUsage.cacheCreationInputTokens)) !== null && _o !== void 0 ? _o : null;
|
|
220
|
-
if (inputTokens === null &&
|
|
221
|
-
outputTokens === null &&
|
|
222
|
-
cacheRead === null &&
|
|
223
|
-
cacheWrite === null) {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
const totalTokens = (inputTokens || 0) +
|
|
227
|
-
(outputTokens || 0) +
|
|
228
|
-
(cacheRead || 0) +
|
|
229
|
-
(cacheWrite || 0);
|
|
230
|
-
return {
|
|
231
|
-
todayTokens: totalTokens,
|
|
232
|
-
totalTokens: null,
|
|
233
|
-
inputTokens,
|
|
234
|
-
outputTokens,
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
function getSessionId(input) {
|
|
238
|
-
if (!input)
|
|
239
|
-
return null;
|
|
240
|
-
return firstNonEmpty(input.session_id, input.sessionId);
|
|
241
|
-
}
|
|
242
|
-
function parseUsageTotalsRecord(record) {
|
|
243
|
-
var _a, _b, _c, _d, _e;
|
|
244
|
-
const inputTokens = (_a = firstNumber(record.inputTokens, record.input, record.input_tokens)) !== null && _a !== void 0 ? _a : null;
|
|
245
|
-
const outputTokens = (_b = firstNumber(record.outputTokens, record.output, record.output_tokens)) !== null && _b !== void 0 ? _b : null;
|
|
246
|
-
const totalTokens = (_c = firstNumber(record.totalTokens, record.total, record.total_tokens)) !== null && _c !== void 0 ? _c : null;
|
|
247
|
-
const cacheRead = (_d = firstNumber(record.cache_read_input_tokens, record.cacheReadInputTokens, record.cache_read, record.cacheRead)) !== null && _d !== void 0 ? _d : null;
|
|
248
|
-
const cacheWrite = (_e = firstNumber(record.cache_creation_input_tokens, record.cacheCreationInputTokens, record.cache_write_input_tokens, record.cacheWriteInputTokens, record.cache_write, record.cacheWrite)) !== null && _e !== void 0 ? _e : null;
|
|
249
|
-
let computedTotal = null;
|
|
250
|
-
if (inputTokens !== null ||
|
|
251
|
-
outputTokens !== null ||
|
|
252
|
-
cacheRead !== null ||
|
|
253
|
-
cacheWrite !== null) {
|
|
254
|
-
computedTotal =
|
|
255
|
-
(inputTokens || 0) +
|
|
256
|
-
(outputTokens || 0) +
|
|
257
|
-
(cacheRead || 0) +
|
|
258
|
-
(cacheWrite || 0);
|
|
259
|
-
}
|
|
260
|
-
const resolvedTotal = totalTokens !== null && totalTokens !== void 0 ? totalTokens : computedTotal;
|
|
261
|
-
if (inputTokens === null &&
|
|
262
|
-
outputTokens === null &&
|
|
263
|
-
resolvedTotal === null) {
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
inputTokens,
|
|
268
|
-
outputTokens,
|
|
269
|
-
totalTokens: resolvedTotal,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
function getUsageTotalsFromInput(input) {
|
|
273
|
-
var _a, _b;
|
|
274
|
-
if (!input)
|
|
275
|
-
return null;
|
|
276
|
-
const contextWindow = isRecord(input.context_window)
|
|
277
|
-
? input.context_window
|
|
278
|
-
: isRecord(input.contextWindow)
|
|
279
|
-
? input.contextWindow
|
|
280
|
-
: null;
|
|
281
|
-
if (contextWindow) {
|
|
282
|
-
const totalInputTokens = (_a = firstNumber(contextWindow.total_input_tokens, contextWindow.totalInputTokens)) !== null && _a !== void 0 ? _a : null;
|
|
283
|
-
const totalOutputTokens = (_b = firstNumber(contextWindow.total_output_tokens, contextWindow.totalOutputTokens)) !== null && _b !== void 0 ? _b : null;
|
|
284
|
-
if (totalInputTokens !== null || totalOutputTokens !== null) {
|
|
285
|
-
return {
|
|
286
|
-
inputTokens: totalInputTokens,
|
|
287
|
-
outputTokens: totalOutputTokens,
|
|
288
|
-
totalTokens: (totalInputTokens || 0) + (totalOutputTokens || 0),
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
if (typeof input.token_usage === "number") {
|
|
293
|
-
return {
|
|
294
|
-
inputTokens: null,
|
|
295
|
-
outputTokens: null,
|
|
296
|
-
totalTokens: coerceNumber(input.token_usage),
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
if (isRecord(input.token_usage)) {
|
|
300
|
-
return parseUsageTotalsRecord(input.token_usage);
|
|
301
|
-
}
|
|
302
|
-
if (isRecord(input.usage)) {
|
|
303
|
-
return parseUsageTotalsRecord(input.usage);
|
|
304
|
-
}
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
function getContextUsedTokens(input) {
|
|
308
|
-
if (!input)
|
|
309
|
-
return null;
|
|
310
|
-
return coerceNumber(input.context_window_used_tokens);
|
|
311
|
-
}
|
|
312
|
-
function normalizeInputUsage(inputUsage) {
|
|
313
|
-
if (!inputUsage)
|
|
314
|
-
return null;
|
|
315
|
-
const usage = {
|
|
316
|
-
todayTokens: coerceNumber(inputUsage.todayTokens),
|
|
317
|
-
totalTokens: coerceNumber(inputUsage.totalTokens),
|
|
318
|
-
inputTokens: coerceNumber(inputUsage.inputTokens),
|
|
319
|
-
outputTokens: coerceNumber(inputUsage.outputTokens),
|
|
320
|
-
};
|
|
321
|
-
const hasUsage = usage.todayTokens !== null ||
|
|
322
|
-
usage.totalTokens !== null ||
|
|
323
|
-
usage.inputTokens !== null ||
|
|
324
|
-
usage.outputTokens !== null;
|
|
325
|
-
return hasUsage ? usage : null;
|
|
326
|
-
}
|
|
327
|
-
function getContextLeftPercent(input, type) {
|
|
328
|
-
if (!input)
|
|
329
|
-
return null;
|
|
330
|
-
const raw = coerceNumber(input.context_window_percent);
|
|
331
|
-
if (raw === null || raw < 0)
|
|
332
|
-
return null;
|
|
333
|
-
const percent = raw <= 1 ? raw * 100 : raw;
|
|
334
|
-
if (percent > 100)
|
|
335
|
-
return null;
|
|
336
|
-
const usedTokens = getContextUsedTokens(input);
|
|
337
|
-
const normalizedType = normalizeTypeValue(type);
|
|
338
|
-
// Prefer treating the percent as "remaining" for codex/claude and when usage is absent.
|
|
339
|
-
const preferRemaining = normalizedType === "codex" ||
|
|
340
|
-
normalizedType === "claude" ||
|
|
341
|
-
usedTokens === null ||
|
|
342
|
-
(usedTokens <= 0 && percent >= 99);
|
|
343
|
-
const left = preferRemaining ? percent : 100 - percent;
|
|
344
|
-
return Math.max(0, Math.min(100, left));
|
|
345
|
-
}
|
|
346
|
-
function getWorkspaceDir(input) {
|
|
347
|
-
if (!input || !isRecord(input.workspace))
|
|
348
|
-
return null;
|
|
349
|
-
const currentDir = input.workspace.current_dir;
|
|
350
|
-
if (currentDir) {
|
|
351
|
-
const trimmed = String(currentDir).trim();
|
|
352
|
-
if (trimmed)
|
|
353
|
-
return trimmed;
|
|
354
|
-
}
|
|
355
|
-
const projectDir = input.workspace.project_dir;
|
|
356
|
-
if (!projectDir)
|
|
357
|
-
return null;
|
|
358
|
-
const trimmed = String(projectDir).trim();
|
|
359
|
-
return trimmed ? trimmed : null;
|
|
360
|
-
}
|
|
361
|
-
function getGitStatusFromInput(input) {
|
|
362
|
-
if (!input || !input.git_branch)
|
|
363
|
-
return null;
|
|
364
|
-
const branch = String(input.git_branch).trim();
|
|
365
|
-
if (!branch)
|
|
366
|
-
return null;
|
|
367
|
-
return {
|
|
368
|
-
branch,
|
|
369
|
-
ahead: 0,
|
|
370
|
-
behind: 0,
|
|
371
|
-
staged: 0,
|
|
372
|
-
unstaged: 0,
|
|
373
|
-
untracked: 0,
|
|
374
|
-
conflicted: 0,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
function getGitStatus(cwd) {
|
|
378
|
-
if (!cwd)
|
|
379
|
-
return null;
|
|
380
|
-
const result = (0, child_process_1.spawnSync)("git", ["-C", cwd, "status", "--porcelain=v2", "-b"], {
|
|
381
|
-
encoding: "utf8",
|
|
382
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
383
|
-
});
|
|
384
|
-
if (result.status !== 0 || !result.stdout)
|
|
385
|
-
return null;
|
|
386
|
-
const status = {
|
|
387
|
-
branch: null,
|
|
388
|
-
ahead: 0,
|
|
389
|
-
behind: 0,
|
|
390
|
-
staged: 0,
|
|
391
|
-
unstaged: 0,
|
|
392
|
-
untracked: 0,
|
|
393
|
-
conflicted: 0,
|
|
394
|
-
};
|
|
395
|
-
const lines = result.stdout.split(/\r?\n/);
|
|
396
|
-
for (const line of lines) {
|
|
397
|
-
if (!line)
|
|
398
|
-
continue;
|
|
399
|
-
if (line.startsWith("# branch.head ")) {
|
|
400
|
-
status.branch = line.slice("# branch.head ".length).trim();
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
if (line.startsWith("# branch.ab ")) {
|
|
404
|
-
const parts = line
|
|
405
|
-
.slice("# branch.ab ".length)
|
|
406
|
-
.trim()
|
|
407
|
-
.split(/\s+/);
|
|
408
|
-
for (const part of parts) {
|
|
409
|
-
if (part.startsWith("+"))
|
|
410
|
-
status.ahead = Number(part.slice(1)) || 0;
|
|
411
|
-
if (part.startsWith("-"))
|
|
412
|
-
status.behind = Number(part.slice(1)) || 0;
|
|
413
|
-
}
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
if (line.startsWith("? ")) {
|
|
417
|
-
status.untracked += 1;
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
if (line.startsWith("u ")) {
|
|
421
|
-
status.conflicted += 1;
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
if (line.startsWith("1 ") || line.startsWith("2 ")) {
|
|
425
|
-
const parts = line.split(/\s+/);
|
|
426
|
-
const xy = parts[1] || "";
|
|
427
|
-
const staged = xy[0];
|
|
428
|
-
const unstaged = xy[1];
|
|
429
|
-
if (staged && staged !== ".")
|
|
430
|
-
status.staged += 1;
|
|
431
|
-
if (unstaged && unstaged !== ".")
|
|
432
|
-
status.unstaged += 1;
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
if (!status.branch) {
|
|
437
|
-
status.branch = "HEAD";
|
|
438
|
-
}
|
|
439
|
-
return status;
|
|
440
|
-
}
|
|
441
|
-
function formatGitSegment(status) {
|
|
442
|
-
if (!status || !status.branch)
|
|
443
|
-
return null;
|
|
444
|
-
const meta = [];
|
|
445
|
-
const dirtyCount = status.staged + status.unstaged + status.untracked;
|
|
446
|
-
if (status.ahead > 0)
|
|
447
|
-
meta.push(`↑${status.ahead}`);
|
|
448
|
-
if (status.behind > 0)
|
|
449
|
-
meta.push(`↓${status.behind}`);
|
|
450
|
-
if (status.conflicted > 0)
|
|
451
|
-
meta.push(`✖${status.conflicted}`);
|
|
452
|
-
if (dirtyCount > 0)
|
|
453
|
-
meta.push(`+${dirtyCount}`);
|
|
454
|
-
const suffix = meta.length > 0 ? ` [${meta.join("")}]` : "";
|
|
455
|
-
const text = `${ICON_GIT} ${status.branch}${suffix}`;
|
|
456
|
-
const hasConflicts = status.conflicted > 0;
|
|
457
|
-
const isDirty = dirtyCount > 0;
|
|
458
|
-
if (hasConflicts)
|
|
459
|
-
return colorize(text, "31");
|
|
460
|
-
if (isDirty)
|
|
461
|
-
return colorize(text, "33");
|
|
462
|
-
if (status.ahead > 0 || status.behind > 0)
|
|
463
|
-
return colorize(text, "36");
|
|
464
|
-
return colorize(text, "32");
|
|
465
|
-
}
|
|
466
|
-
function resolveUsageFromRecords(config, configPath, type, profileKey, profileName, syncUsage) {
|
|
467
|
-
try {
|
|
468
|
-
const normalized = (0, type_1.normalizeType)(type || "");
|
|
469
|
-
if (!normalized || (!profileKey && !profileName))
|
|
470
|
-
return null;
|
|
471
|
-
const totals = (0, usage_1.readUsageTotalsIndex)(config, configPath, syncUsage);
|
|
472
|
-
if (!totals)
|
|
473
|
-
return null;
|
|
474
|
-
const usage = (0, usage_1.resolveUsageTotalsForProfile)(totals, normalized, profileKey, profileName);
|
|
475
|
-
if (!usage)
|
|
476
|
-
return null;
|
|
477
|
-
return {
|
|
478
|
-
todayTokens: usage.today,
|
|
479
|
-
totalTokens: usage.total,
|
|
480
|
-
inputTokens: null,
|
|
481
|
-
outputTokens: null,
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
catch {
|
|
485
|
-
return null;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
function formatUsageSegment(usage) {
|
|
489
|
-
var _a;
|
|
490
|
-
if (!usage)
|
|
491
|
-
return null;
|
|
492
|
-
const today = (_a = usage.todayTokens) !== null && _a !== void 0 ? _a : (usage.inputTokens !== null || usage.outputTokens !== null
|
|
493
|
-
? (usage.inputTokens || 0) + (usage.outputTokens || 0)
|
|
494
|
-
: usage.totalTokens);
|
|
495
|
-
if (today === null)
|
|
496
|
-
return null;
|
|
497
|
-
const text = `Today ${(0, usage_1.formatTokenCount)(today)}`;
|
|
498
|
-
return colorize(`${ICON_USAGE} ${text}`, "33");
|
|
499
|
-
}
|
|
500
|
-
function formatModelSegment(model, provider) {
|
|
501
|
-
if (!model)
|
|
502
|
-
return null;
|
|
503
|
-
const providerLabel = provider ? `${provider}:${model}` : model;
|
|
504
|
-
return colorize(`${ICON_MODEL} ${providerLabel}`, "35");
|
|
505
|
-
}
|
|
506
|
-
function formatProfileSegment(type, profileKey, profileName) {
|
|
507
|
-
const name = profileName || profileKey;
|
|
508
|
-
if (!name)
|
|
509
|
-
return null;
|
|
510
|
-
const label = type ? `${type}:${name}` : name;
|
|
511
|
-
return colorize(`${ICON_PROFILE} ${label}`, "37");
|
|
512
|
-
}
|
|
513
|
-
function formatContextSegment(contextLeft) {
|
|
514
|
-
if (contextLeft === null)
|
|
515
|
-
return null;
|
|
516
|
-
const left = Math.max(0, Math.min(100, Math.round(contextLeft)));
|
|
517
|
-
return colorize(`${ICON_CONTEXT} ${left}% left`, "36");
|
|
518
|
-
}
|
|
519
|
-
function formatContextUsedSegment(usedTokens) {
|
|
520
|
-
if (usedTokens === null)
|
|
521
|
-
return null;
|
|
522
|
-
return colorize(`${ICON_CONTEXT} ${(0, usage_1.formatTokenCount)(usedTokens)} used`, "36");
|
|
523
|
-
}
|
|
524
|
-
function formatModeSegment(reviewMode) {
|
|
525
|
-
if (!reviewMode)
|
|
526
|
-
return null;
|
|
527
|
-
return colorize(`${ICON_REVIEW} review`, "34");
|
|
528
|
-
}
|
|
6
|
+
const pricing_1 = require("../usage/pricing");
|
|
7
|
+
const debug_1 = require("./debug");
|
|
8
|
+
const format_1 = require("./format");
|
|
9
|
+
const git_1 = require("./git");
|
|
10
|
+
const input_1 = require("./input");
|
|
11
|
+
const usage_2 = require("./usage");
|
|
12
|
+
const utils_1 = require("./utils");
|
|
529
13
|
function buildStatuslineResult(args, config, configPath) {
|
|
530
|
-
const stdinInput = readStdinJson();
|
|
531
|
-
const inputProfile = getInputProfile(stdinInput);
|
|
532
|
-
let typeCandidate = firstNonEmpty(args.type, process.env.CODE_ENV_TYPE, inputProfile ? inputProfile.type : null, stdinInput ? stdinInput.type : null);
|
|
14
|
+
const stdinInput = (0, input_1.readStdinJson)();
|
|
15
|
+
const inputProfile = (0, input_1.getInputProfile)(stdinInput);
|
|
16
|
+
let typeCandidate = (0, utils_1.firstNonEmpty)(args.type, process.env.CODE_ENV_TYPE, inputProfile ? inputProfile.type : null, stdinInput ? stdinInput.type : null);
|
|
533
17
|
if (!typeCandidate) {
|
|
534
|
-
typeCandidate = detectTypeFromEnv();
|
|
18
|
+
typeCandidate = (0, input_1.detectTypeFromEnv)();
|
|
535
19
|
}
|
|
536
|
-
let type = normalizeTypeValue(typeCandidate);
|
|
537
|
-
const envProfile = resolveEnvProfile(type);
|
|
538
|
-
const profileKey = firstNonEmpty(args.profileKey, envProfile.key, inputProfile ? inputProfile.key : null);
|
|
539
|
-
let profileName = firstNonEmpty(args.profileName, envProfile.name, inputProfile ? inputProfile.name : null);
|
|
20
|
+
let type = (0, input_1.normalizeTypeValue)(typeCandidate);
|
|
21
|
+
const envProfile = (0, input_1.resolveEnvProfile)(type);
|
|
22
|
+
const profileKey = (0, utils_1.firstNonEmpty)(args.profileKey, envProfile.key, inputProfile ? inputProfile.key : null);
|
|
23
|
+
let profileName = (0, utils_1.firstNonEmpty)(args.profileName, envProfile.name, inputProfile ? inputProfile.name : null);
|
|
540
24
|
if (profileKey && !profileName && config.profiles && config.profiles[profileKey]) {
|
|
541
25
|
const profile = config.profiles[profileKey];
|
|
542
26
|
profileName = (0, type_1.getProfileDisplayName)(profileKey, profile, type || undefined);
|
|
@@ -552,53 +36,118 @@ function buildStatuslineResult(args, config, configPath) {
|
|
|
552
36
|
if (inferred)
|
|
553
37
|
type = inferred;
|
|
554
38
|
}
|
|
555
|
-
const cwd = firstNonEmpty(args.cwd, process.env.CODE_ENV_CWD, getWorkspaceDir(stdinInput), stdinInput ? stdinInput.cwd : null, process.cwd());
|
|
556
|
-
const sessionId = getSessionId(stdinInput);
|
|
557
|
-
const
|
|
39
|
+
const cwd = (0, utils_1.firstNonEmpty)(args.cwd, process.env.CODE_ENV_CWD, (0, input_1.getWorkspaceDir)(stdinInput), stdinInput ? stdinInput.cwd : null, process.cwd());
|
|
40
|
+
const sessionId = (0, input_1.getSessionId)(stdinInput);
|
|
41
|
+
const usageType = (0, type_1.normalizeType)(type || "");
|
|
42
|
+
const stdinUsageTotals = (0, usage_2.getUsageTotalsFromInput)(stdinInput, usageType);
|
|
43
|
+
const model = (0, utils_1.firstNonEmpty)(args.model, process.env.CODE_ENV_MODEL, (0, input_1.getModelFromInput)(stdinInput));
|
|
44
|
+
const modelProvider = (0, utils_1.firstNonEmpty)(process.env.CODE_ENV_MODEL_PROVIDER, (0, input_1.getModelProviderFromInput)(stdinInput));
|
|
45
|
+
(0, debug_1.appendStatuslineDebug)(configPath, {
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
typeCandidate,
|
|
48
|
+
resolvedType: type,
|
|
49
|
+
usageType,
|
|
50
|
+
profile: { key: profileKey, name: profileName },
|
|
51
|
+
sessionId,
|
|
52
|
+
stdinUsageTotals,
|
|
53
|
+
cwd,
|
|
54
|
+
args: {
|
|
55
|
+
type: args.type,
|
|
56
|
+
profileKey: args.profileKey,
|
|
57
|
+
profileName: args.profileName,
|
|
58
|
+
usageToday: args.usageToday,
|
|
59
|
+
usageTotal: args.usageTotal,
|
|
60
|
+
usageInput: args.usageInput,
|
|
61
|
+
usageOutput: args.usageOutput,
|
|
62
|
+
syncUsage: args.syncUsage,
|
|
63
|
+
},
|
|
64
|
+
env: {
|
|
65
|
+
CODE_ENV_TYPE: process.env.CODE_ENV_TYPE,
|
|
66
|
+
CODE_ENV_PROFILE_KEY: process.env.CODE_ENV_PROFILE_KEY,
|
|
67
|
+
CODE_ENV_PROFILE_NAME: process.env.CODE_ENV_PROFILE_NAME,
|
|
68
|
+
CODE_ENV_CWD: process.env.CODE_ENV_CWD,
|
|
69
|
+
CODE_ENV_STATUSLINE: process.env.CODE_ENV_STATUSLINE,
|
|
70
|
+
},
|
|
71
|
+
input: stdinInput,
|
|
72
|
+
});
|
|
558
73
|
if (args.syncUsage && sessionId && stdinUsageTotals) {
|
|
559
|
-
|
|
560
|
-
(0, usage_1.syncUsageFromStatuslineInput)(config, configPath, usageType, profileKey, profileName, sessionId, stdinUsageTotals, cwd);
|
|
74
|
+
(0, usage_1.syncUsageFromStatuslineInput)(config, configPath, usageType, profileKey, profileName, sessionId, stdinUsageTotals, cwd, model);
|
|
561
75
|
}
|
|
562
|
-
const model = firstNonEmpty(args.model, process.env.CODE_ENV_MODEL, getModelFromInput(stdinInput));
|
|
563
|
-
const modelProvider = firstNonEmpty(process.env.CODE_ENV_MODEL_PROVIDER, getModelProviderFromInput(stdinInput));
|
|
564
76
|
const usage = {
|
|
565
|
-
todayTokens: firstNumber(args.usageToday, process.env.CODE_ENV_USAGE_TODAY),
|
|
566
|
-
totalTokens: firstNumber(args.usageTotal, process.env.CODE_ENV_USAGE_TOTAL),
|
|
567
|
-
inputTokens: firstNumber(args.usageInput, process.env.CODE_ENV_USAGE_INPUT),
|
|
568
|
-
outputTokens: firstNumber(args.usageOutput, process.env.CODE_ENV_USAGE_OUTPUT),
|
|
77
|
+
todayTokens: (0, utils_1.firstNumber)(args.usageToday, process.env.CODE_ENV_USAGE_TODAY),
|
|
78
|
+
totalTokens: (0, utils_1.firstNumber)(args.usageTotal, process.env.CODE_ENV_USAGE_TOTAL),
|
|
79
|
+
inputTokens: (0, utils_1.firstNumber)(args.usageInput, process.env.CODE_ENV_USAGE_INPUT),
|
|
80
|
+
outputTokens: (0, utils_1.firstNumber)(args.usageOutput, process.env.CODE_ENV_USAGE_OUTPUT),
|
|
81
|
+
cacheReadTokens: null,
|
|
82
|
+
cacheWriteTokens: null,
|
|
569
83
|
};
|
|
570
84
|
const hasExplicitUsage = usage.todayTokens !== null ||
|
|
571
85
|
usage.totalTokens !== null ||
|
|
572
86
|
usage.inputTokens !== null ||
|
|
573
87
|
usage.outputTokens !== null;
|
|
574
|
-
const stdinUsage = normalizeInputUsage(getInputUsage(stdinInput));
|
|
88
|
+
const stdinUsage = (0, usage_2.normalizeInputUsage)((0, input_1.getInputUsage)(stdinInput, usageType));
|
|
89
|
+
const recordsUsage = (0, usage_2.resolveUsageFromRecords)(config, configPath, type, profileKey, profileName, args.syncUsage);
|
|
575
90
|
let finalUsage = hasExplicitUsage ? usage : null;
|
|
91
|
+
if (!finalUsage && args.syncUsage && recordsUsage) {
|
|
92
|
+
finalUsage = recordsUsage;
|
|
93
|
+
}
|
|
576
94
|
if (!finalUsage) {
|
|
577
95
|
finalUsage = stdinUsage;
|
|
578
96
|
}
|
|
579
|
-
if (!finalUsage) {
|
|
580
|
-
finalUsage =
|
|
97
|
+
if (!finalUsage && recordsUsage) {
|
|
98
|
+
finalUsage = recordsUsage;
|
|
581
99
|
}
|
|
582
|
-
let gitStatus = getGitStatus(cwd);
|
|
100
|
+
let gitStatus = (0, git_1.getGitStatus)(cwd);
|
|
583
101
|
if (!gitStatus) {
|
|
584
|
-
gitStatus = getGitStatusFromInput(stdinInput);
|
|
102
|
+
gitStatus = (0, input_1.getGitStatusFromInput)(stdinInput);
|
|
585
103
|
}
|
|
586
104
|
else {
|
|
587
|
-
const inputGit = getGitStatusFromInput(stdinInput);
|
|
105
|
+
const inputGit = (0, input_1.getGitStatusFromInput)(stdinInput);
|
|
588
106
|
if (inputGit && (!gitStatus.branch || gitStatus.branch === "HEAD")) {
|
|
589
107
|
gitStatus.branch = inputGit.branch;
|
|
590
108
|
}
|
|
591
109
|
}
|
|
592
|
-
const gitSegment = formatGitSegment(gitStatus);
|
|
593
|
-
const profileSegment = formatProfileSegment(type, profileKey, profileName);
|
|
594
|
-
const modelSegment = formatModelSegment(model, modelProvider);
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
110
|
+
const gitSegment = (0, git_1.formatGitSegment)(gitStatus);
|
|
111
|
+
const profileSegment = (0, format_1.formatProfileSegment)(type, profileKey, profileName);
|
|
112
|
+
const modelSegment = (0, format_1.formatModelSegment)(model, modelProvider);
|
|
113
|
+
let profile = profileKey && config.profiles ? config.profiles[profileKey] : null;
|
|
114
|
+
if (!profile && profileName && config.profiles) {
|
|
115
|
+
const matches = Object.entries(config.profiles).find(([key, entry]) => {
|
|
116
|
+
const displayName = (0, type_1.getProfileDisplayName)(key, entry);
|
|
117
|
+
return (displayName === profileName ||
|
|
118
|
+
entry.name === profileName ||
|
|
119
|
+
key === profileName);
|
|
120
|
+
});
|
|
121
|
+
if (matches)
|
|
122
|
+
profile = matches[1];
|
|
123
|
+
}
|
|
124
|
+
const sessionUsage = hasExplicitUsage ? usage : stdinUsage;
|
|
125
|
+
const pricing = (0, pricing_1.resolvePricingForProfile)(config, profile || null, model);
|
|
126
|
+
let sessionCost = null;
|
|
127
|
+
if (hasExplicitUsage) {
|
|
128
|
+
sessionCost = sessionUsage
|
|
129
|
+
? (0, pricing_1.calculateUsageCost)(sessionUsage, pricing)
|
|
130
|
+
: null;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const sessionCostFromRecords = sessionId
|
|
134
|
+
? (0, usage_1.readUsageSessionCost)(config, configPath, type, sessionId, args.syncUsage)
|
|
135
|
+
: null;
|
|
136
|
+
sessionCost =
|
|
137
|
+
sessionCostFromRecords !== null && sessionCostFromRecords !== void 0 ? sessionCostFromRecords : (sessionUsage ? (0, pricing_1.calculateUsageCost)(sessionUsage, pricing) : null);
|
|
138
|
+
}
|
|
139
|
+
const costIndex = (0, usage_1.readUsageCostIndex)(config, configPath, args.syncUsage);
|
|
140
|
+
const costTotals = costIndex
|
|
141
|
+
? (0, usage_1.resolveUsageCostForProfile)(costIndex, type, profileKey, profileName)
|
|
142
|
+
: null;
|
|
143
|
+
const todayCost = costTotals ? costTotals.today : null;
|
|
144
|
+
const usageSegment = (0, format_1.formatUsageSegment)(todayCost, sessionCost);
|
|
145
|
+
const contextLeft = (0, input_1.getContextLeftPercent)(stdinInput, type);
|
|
146
|
+
const contextSegment = (0, format_1.formatContextSegment)(contextLeft);
|
|
147
|
+
const contextUsedTokens = (0, input_1.getContextUsedTokens)(stdinInput);
|
|
148
|
+
const contextUsedSegment = contextSegment === null ? (0, format_1.formatContextUsedSegment)(contextUsedTokens) : null;
|
|
149
|
+
const modeSegment = (0, format_1.formatModeSegment)((stdinInput === null || stdinInput === void 0 ? void 0 : stdinInput.review_mode) === true);
|
|
150
|
+
const cwdSegment = (0, format_1.getCwdSegment)(cwd);
|
|
602
151
|
const segments = [];
|
|
603
152
|
if (gitSegment)
|
|
604
153
|
segments.push(gitSegment);
|