ccstatusline-usage 2.3.17 → 2.4.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.
- package/README.md +43 -0
- package/dist/ccstatusline.js +190 -105
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -36,6 +36,42 @@ This fork adds API-based usage widgets beyond the upstream:
|
|
|
36
36
|
- **Context Window Display** - Visual bar showing context usage
|
|
37
37
|
- **Off Peak** - Shows peak/off-peak status with countdown timer (peak hours drain sessions faster)
|
|
38
38
|
- **Two-line Layout** - Session info on line 1, context on line 2
|
|
39
|
+
- **Multi-provider routing** - Usage widgets dispatch per model: Anthropic models hit the usage API; opencode/local models (GLM, Kimi, MiniMax, Qwen, Ollama) skip the fetch and gracefully hide usage bars while keeping the real-time context bar.
|
|
40
|
+
|
|
41
|
+
### Multi-Provider Routing (Opencode / Local Models)
|
|
42
|
+
|
|
43
|
+
Claude Code can route individual prompts to non-Anthropic backends via opencode or a local Ollama runtime. The status line reads the `model.id` that Claude Code sends on stdin each render and dispatches through a per-provider resolver (`src/utils/usage/resolver.ts`):
|
|
44
|
+
|
|
45
|
+
- **Anthropic** (`opus`, `sonnet`, `haiku` in the id) — fetches Session/Weekly/Reset from the usage API.
|
|
46
|
+
- **Opencode** (`glm`, `kimi`, `minimax`, `mm-`, `qwen`, `owen`, `mimo`) — no usage API call; Session/Weekly/Reset widgets hide themselves. Context Bar still renders when `context_window` is in the payload.
|
|
47
|
+
- **Unknown model id** — same behavior as opencode (hides usage widgets).
|
|
48
|
+
|
|
49
|
+
This means opencode-routed turns don't trigger pointless Anthropic API calls or rate-limiting during heavy local usage.
|
|
50
|
+
|
|
51
|
+
Example — configure Claude Code to route a model through opencode (edit `~/.claude/settings.json`):
|
|
52
|
+
|
|
53
|
+
```jsonc
|
|
54
|
+
{
|
|
55
|
+
"statusLine": {
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "npx -y ccstatusline-usage@latest",
|
|
58
|
+
"padding": 0
|
|
59
|
+
},
|
|
60
|
+
"model": "glm-5.1" // or "kimi-k2.6", "minimax-m2.7", "qwen-3.6-plus", "qwen3.6:35b-a3b-q4_K_M" for local Ollama
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
What the status line renders per model:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
# Anthropic (opus / sonnet / haiku)
|
|
68
|
+
Session: [████░░░░░░░░░░░] 27.0% | Weekly: [████░░░░░░░░░░░] 34.0% | 2:03 hr | Model: Opus 4.7
|
|
69
|
+
Context: [██████░░░░░░░░░] 389k/1M (39%) | Pace: [░░░░░░█|░░░░░░░] D4/7 -8% | Off-peak (4:03 hr)
|
|
70
|
+
|
|
71
|
+
# Opencode / local (glm-5.1, kimi, qwen, …)
|
|
72
|
+
Model: glm-5.1 | Off-peak (4:03 hr)
|
|
73
|
+
Context: [██░░░░░░░░░░░░░] 50k/200k (25%)
|
|
74
|
+
```
|
|
39
75
|
|
|
40
76
|
### Enhanced Status Line Preview
|
|
41
77
|
|
|
@@ -67,6 +103,13 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
|
|
|
67
103
|
|
|
68
104
|
## 🆕 Recent Updates
|
|
69
105
|
|
|
106
|
+
### [v2.4.0](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.4.0) - Multi-provider router for usage widgets
|
|
107
|
+
|
|
108
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Provider pattern end-to-end** — Usage widgets now dispatch through `resolveProvider(modelId)` in `src/utils/usage/resolver.ts`. Anthropic models (`opus`/`sonnet`/`haiku`) fetch from the usage API; opencode/local models (`glm`, `kimi`, `minimax`, `mm-`, `qwen`, `owen`, `mimo`) skip the fetch entirely so heavy local-model sessions no longer trigger needless Anthropic API calls or rate-limiting.
|
|
109
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): The prefetch layer (`usage-prefetch.ts`) now reads `data.model.id` from the payload and dispatches via `provider.fetchUsage()` instead of the hardcoded Anthropic path. `opencodeProvider` / `nullProvider` return empty `UsageData`, so Session/Weekly/Reset widgets hide themselves naturally.
|
|
110
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Context Bar stays backend-agnostic** — renders whenever `context_window` is present in the payload, regardless of which provider handled the turn.
|
|
111
|
+
- See the new [Multi-Provider Routing](#multi-provider-routing-opencode--local-models) section under Fork Enhancements for example config.
|
|
112
|
+
|
|
70
113
|
### [v2.3.17](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.17) - Local model fallback for API usage widgets
|
|
71
114
|
|
|
72
115
|
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Local model fallback** — When the active model is not Opus/Sonnet/Haiku (e.g. a local Ollama model like `qwen3-coder:30b`), the Session, Weekly, and Context widgets now render empty-bar placeholders (`[░░░░░░░░░░░░░░░] -.0%`) and the Reset Timer shows `-:00 hr` instead of misleading Claude API values.
|
package/dist/ccstatusline.js
CHANGED
|
@@ -52556,7 +52556,7 @@ class OutputStyleWidget {
|
|
|
52556
52556
|
}
|
|
52557
52557
|
|
|
52558
52558
|
// src/utils/git.ts
|
|
52559
|
-
import {
|
|
52559
|
+
import { execFileSync } from "child_process";
|
|
52560
52560
|
function resolveGitCwd(context) {
|
|
52561
52561
|
const candidates = [
|
|
52562
52562
|
context.data?.cwd,
|
|
@@ -52571,13 +52571,18 @@ function resolveGitCwd(context) {
|
|
|
52571
52571
|
return;
|
|
52572
52572
|
}
|
|
52573
52573
|
function runGit(command, context) {
|
|
52574
|
+
const args = command.trim().split(/\s+/).filter(Boolean);
|
|
52575
|
+
return runGitArgs(args, context, command);
|
|
52576
|
+
}
|
|
52577
|
+
function runGitArgs(args, context, cacheCommand) {
|
|
52574
52578
|
const cwd2 = resolveGitCwd(context);
|
|
52575
|
-
const
|
|
52579
|
+
const cacheToken = cacheCommand ?? args.join("\x00");
|
|
52580
|
+
const cacheKey = `${cacheToken}|${cwd2 ?? ""}`;
|
|
52576
52581
|
if (gitCommandCache.has(cacheKey)) {
|
|
52577
52582
|
return gitCommandCache.get(cacheKey) ?? null;
|
|
52578
52583
|
}
|
|
52579
52584
|
try {
|
|
52580
|
-
const output =
|
|
52585
|
+
const output = execFileSync("git", args, {
|
|
52581
52586
|
encoding: "utf8",
|
|
52582
52587
|
stdio: ["pipe", "pipe", "ignore"],
|
|
52583
52588
|
...cwd2 ? { cwd: cwd2 } : {}
|
|
@@ -53136,7 +53141,7 @@ var init_GitRootDir = __esm(() => {
|
|
|
53136
53141
|
});
|
|
53137
53142
|
|
|
53138
53143
|
// src/utils/gh-pr-cache.ts
|
|
53139
|
-
import { execFileSync } from "child_process";
|
|
53144
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
53140
53145
|
import {
|
|
53141
53146
|
existsSync as existsSync2,
|
|
53142
53147
|
mkdirSync,
|
|
@@ -53278,7 +53283,7 @@ function truncateTitle(title, maxWidth) {
|
|
|
53278
53283
|
var PR_CACHE_TTL = 30000, GH_TIMEOUT = 5000, DEFAULT_TITLE_MAX_WIDTH = 30, DEFAULT_PR_CACHE_DEPS;
|
|
53279
53284
|
var init_gh_pr_cache = __esm(() => {
|
|
53280
53285
|
DEFAULT_PR_CACHE_DEPS = {
|
|
53281
|
-
execFileSync,
|
|
53286
|
+
execFileSync: execFileSync2,
|
|
53282
53287
|
existsSync: existsSync2,
|
|
53283
53288
|
mkdirSync,
|
|
53284
53289
|
readFileSync: readFileSync2,
|
|
@@ -53956,7 +53961,7 @@ function parseRemoteUrl(url2) {
|
|
|
53956
53961
|
}
|
|
53957
53962
|
}
|
|
53958
53963
|
function getRemoteInfo(remoteName, context) {
|
|
53959
|
-
const url2 =
|
|
53964
|
+
const url2 = runGitArgs(["remote", "get-url", "--", remoteName], context, `remote get-url -- ${remoteName}`);
|
|
53960
53965
|
if (!url2) {
|
|
53961
53966
|
return null;
|
|
53962
53967
|
}
|
|
@@ -55301,6 +55306,15 @@ function getContextConfig(modelIdentifier, contextWindowSize) {
|
|
|
55301
55306
|
if (!modelIdentifier) {
|
|
55302
55307
|
return defaultConfig;
|
|
55303
55308
|
}
|
|
55309
|
+
const normalizedModel = modelIdentifier.toLowerCase().trim();
|
|
55310
|
+
for (const [modelName, contextSize] of Object.entries(OPENCODE_MODEL_CONTEXT_MAP)) {
|
|
55311
|
+
if (normalizedModel.includes(modelName)) {
|
|
55312
|
+
return {
|
|
55313
|
+
maxTokens: contextSize,
|
|
55314
|
+
usableTokens: Math.floor(contextSize * USABLE_CONTEXT_RATIO)
|
|
55315
|
+
};
|
|
55316
|
+
}
|
|
55317
|
+
}
|
|
55304
55318
|
const inferredWindowSize = parseContextWindowSize(modelIdentifier);
|
|
55305
55319
|
if (inferredWindowSize !== null) {
|
|
55306
55320
|
return {
|
|
@@ -55310,7 +55324,24 @@ function getContextConfig(modelIdentifier, contextWindowSize) {
|
|
|
55310
55324
|
}
|
|
55311
55325
|
return defaultConfig;
|
|
55312
55326
|
}
|
|
55313
|
-
var DEFAULT_CONTEXT_WINDOW_SIZE = 200000, USABLE_CONTEXT_RATIO = 0.8;
|
|
55327
|
+
var DEFAULT_CONTEXT_WINDOW_SIZE = 200000, USABLE_CONTEXT_RATIO = 0.8, OPENCODE_MODEL_CONTEXT_MAP;
|
|
55328
|
+
var init_model_context = __esm(() => {
|
|
55329
|
+
OPENCODE_MODEL_CONTEXT_MAP = {
|
|
55330
|
+
"glm-5.1": 1e6,
|
|
55331
|
+
"glm-4.5": 1e6,
|
|
55332
|
+
"glm-4.0": 1e6,
|
|
55333
|
+
"mm-2.7": 1e6,
|
|
55334
|
+
"mm-2.5": 1e6,
|
|
55335
|
+
"kimi-k2.6": 1e6,
|
|
55336
|
+
"kimi-k2.5": 1e6,
|
|
55337
|
+
"owen-3.6": 1e6,
|
|
55338
|
+
"owen-3.5": 1e6,
|
|
55339
|
+
"qwen-2.5": 1e6,
|
|
55340
|
+
"qwen-2.0": 1e6,
|
|
55341
|
+
"qwen-1.5": 1e6,
|
|
55342
|
+
"qwen-1.0": 1e6
|
|
55343
|
+
};
|
|
55344
|
+
});
|
|
55314
55345
|
|
|
55315
55346
|
// src/utils/context-percentage.ts
|
|
55316
55347
|
function calculateContextPercentage(context) {
|
|
@@ -55325,10 +55356,12 @@ function calculateContextPercentage(context) {
|
|
|
55325
55356
|
const contextConfig = getContextConfig(modelIdentifier, contextWindowMetrics.windowSize);
|
|
55326
55357
|
return Math.min(100, context.tokenMetrics.contextLength / contextConfig.maxTokens * 100);
|
|
55327
55358
|
}
|
|
55328
|
-
var init_context_percentage = () => {
|
|
55359
|
+
var init_context_percentage = __esm(() => {
|
|
55360
|
+
init_model_context();
|
|
55361
|
+
});
|
|
55329
55362
|
|
|
55330
55363
|
// src/utils/terminal.ts
|
|
55331
|
-
import { execSync
|
|
55364
|
+
import { execSync } from "child_process";
|
|
55332
55365
|
import * as fs2 from "fs";
|
|
55333
55366
|
import * as path2 from "path";
|
|
55334
55367
|
function getPackageVersion() {
|
|
@@ -55352,7 +55385,7 @@ function getPackageVersion() {
|
|
|
55352
55385
|
function probeTerminalWidth() {
|
|
55353
55386
|
if (process.env.TMUX) {
|
|
55354
55387
|
try {
|
|
55355
|
-
const output =
|
|
55388
|
+
const output = execSync("tmux display-message -p '#{pane_width}'", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 2000 }).trim();
|
|
55356
55389
|
const parsed = parseInt(output, 10);
|
|
55357
55390
|
if (!isNaN(parsed) && parsed > 0)
|
|
55358
55391
|
return parsed;
|
|
@@ -55378,7 +55411,7 @@ function probeTerminalWidth() {
|
|
|
55378
55411
|
}
|
|
55379
55412
|
}
|
|
55380
55413
|
try {
|
|
55381
|
-
const width =
|
|
55414
|
+
const width = execSync("tput cols 2>/dev/null", {
|
|
55382
55415
|
encoding: "utf8",
|
|
55383
55416
|
stdio: ["pipe", "pipe", "ignore"]
|
|
55384
55417
|
}).trim();
|
|
@@ -55395,7 +55428,7 @@ function parsePositiveInteger(value) {
|
|
|
55395
55428
|
}
|
|
55396
55429
|
function getParentProcessId(pid) {
|
|
55397
55430
|
try {
|
|
55398
|
-
const parentPidOutput =
|
|
55431
|
+
const parentPidOutput = execSync(`ps -o ppid= -p ${pid}`, {
|
|
55399
55432
|
encoding: "utf8",
|
|
55400
55433
|
stdio: ["pipe", "pipe", "ignore"],
|
|
55401
55434
|
shell: "/bin/sh"
|
|
@@ -55407,7 +55440,7 @@ function getParentProcessId(pid) {
|
|
|
55407
55440
|
}
|
|
55408
55441
|
function getTTYForProcess(pid) {
|
|
55409
55442
|
try {
|
|
55410
|
-
const tty2 =
|
|
55443
|
+
const tty2 = execSync(`ps -o tty= -p ${pid}`, {
|
|
55411
55444
|
encoding: "utf8",
|
|
55412
55445
|
stdio: ["pipe", "pipe", "ignore"],
|
|
55413
55446
|
shell: "/bin/sh"
|
|
@@ -55422,7 +55455,7 @@ function getTTYForProcess(pid) {
|
|
|
55422
55455
|
}
|
|
55423
55456
|
function getWidthForTTY(tty2) {
|
|
55424
55457
|
try {
|
|
55425
|
-
const width =
|
|
55458
|
+
const width = execSync(`stty size < /dev/${tty2} | awk '{print $2}'`, {
|
|
55426
55459
|
encoding: "utf8",
|
|
55427
55460
|
stdio: ["pipe", "pipe", "ignore"],
|
|
55428
55461
|
shell: "/bin/sh"
|
|
@@ -55438,7 +55471,7 @@ function getTerminalWidth() {
|
|
|
55438
55471
|
function canDetectTerminalWidth() {
|
|
55439
55472
|
return probeTerminalWidth() !== null;
|
|
55440
55473
|
}
|
|
55441
|
-
var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.
|
|
55474
|
+
var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.4.0";
|
|
55442
55475
|
var init_terminal = () => {};
|
|
55443
55476
|
|
|
55444
55477
|
// src/utils/renderer.ts
|
|
@@ -56311,6 +56344,7 @@ class ContextPercentageWidget {
|
|
|
56311
56344
|
}
|
|
56312
56345
|
}
|
|
56313
56346
|
var init_ContextPercentage = __esm(() => {
|
|
56347
|
+
init_model_context();
|
|
56314
56348
|
init_context_inverse();
|
|
56315
56349
|
});
|
|
56316
56350
|
|
|
@@ -56371,6 +56405,7 @@ class ContextPercentageUsableWidget {
|
|
|
56371
56405
|
}
|
|
56372
56406
|
}
|
|
56373
56407
|
var init_ContextPercentageUsable = __esm(() => {
|
|
56408
|
+
init_model_context();
|
|
56374
56409
|
init_context_inverse();
|
|
56375
56410
|
});
|
|
56376
56411
|
|
|
@@ -57026,7 +57061,7 @@ var init_CustomSymbol = __esm(async () => {
|
|
|
57026
57061
|
});
|
|
57027
57062
|
|
|
57028
57063
|
// src/widgets/CustomCommand.tsx
|
|
57029
|
-
import { execSync as
|
|
57064
|
+
import { execSync as execSync2 } from "child_process";
|
|
57030
57065
|
|
|
57031
57066
|
class CustomCommandWidget {
|
|
57032
57067
|
getDefaultColor() {
|
|
@@ -57073,7 +57108,7 @@ class CustomCommandWidget {
|
|
|
57073
57108
|
try {
|
|
57074
57109
|
const timeout = item.timeout ?? 1000;
|
|
57075
57110
|
const jsonInput = JSON.stringify(context.data);
|
|
57076
|
-
let output =
|
|
57111
|
+
let output = execSync2(item.commandPath, {
|
|
57077
57112
|
encoding: "utf8",
|
|
57078
57113
|
input: jsonInput,
|
|
57079
57114
|
timeout,
|
|
@@ -58237,7 +58272,7 @@ var init_usage_types = __esm(() => {
|
|
|
58237
58272
|
});
|
|
58238
58273
|
|
|
58239
58274
|
// src/utils/usage-fetch.ts
|
|
58240
|
-
import { execFileSync as
|
|
58275
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
58241
58276
|
import * as fs3 from "fs";
|
|
58242
58277
|
import * as https from "https";
|
|
58243
58278
|
import * as os4 from "os";
|
|
@@ -58383,7 +58418,7 @@ function parseMacKeychainCredentialCandidates(rawDump, servicePrefix = MACOS_USA
|
|
|
58383
58418
|
}
|
|
58384
58419
|
function readMacKeychainSecret(service) {
|
|
58385
58420
|
try {
|
|
58386
|
-
return
|
|
58421
|
+
return execFileSync3("security", ["find-generic-password", "-s", service, "-w"], { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }).trim();
|
|
58387
58422
|
} catch {
|
|
58388
58423
|
return null;
|
|
58389
58424
|
}
|
|
@@ -58394,7 +58429,7 @@ function readUsageTokenFromMacKeychainService(service) {
|
|
|
58394
58429
|
}
|
|
58395
58430
|
function listMacKeychainCredentialCandidates() {
|
|
58396
58431
|
try {
|
|
58397
|
-
const rawDump =
|
|
58432
|
+
const rawDump = execFileSync3("security", ["dump-keychain"], {
|
|
58398
58433
|
encoding: "utf8",
|
|
58399
58434
|
maxBuffer: MACOS_SECURITY_DUMP_MAX_BUFFER,
|
|
58400
58435
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -58640,7 +58675,8 @@ var init_usage_fetch = __esm(() => {
|
|
|
58640
58675
|
extraUsageLimit: exports_external.number().nullable().optional(),
|
|
58641
58676
|
extraUsageUsed: exports_external.number().nullable().optional(),
|
|
58642
58677
|
extraUsageUtilization: exports_external.number().nullable().optional(),
|
|
58643
|
-
error: exports_external.string().nullable().optional()
|
|
58678
|
+
error: exports_external.string().nullable().optional(),
|
|
58679
|
+
provider: exports_external.enum(["anthropic", "opencode"]).nullable().optional()
|
|
58644
58680
|
});
|
|
58645
58681
|
UsageApiResponseSchema = exports_external.object({
|
|
58646
58682
|
five_hour: exports_external.object({
|
|
@@ -62796,7 +62832,7 @@ var init_TotalSpeed = __esm(async () => {
|
|
|
62796
62832
|
});
|
|
62797
62833
|
|
|
62798
62834
|
// src/widgets/FreeMemory.ts
|
|
62799
|
-
import { execSync as
|
|
62835
|
+
import { execSync as execSync3 } from "child_process";
|
|
62800
62836
|
import os7 from "os";
|
|
62801
62837
|
function formatBytes(bytes) {
|
|
62802
62838
|
const GB = 1024 ** 3;
|
|
@@ -62812,7 +62848,7 @@ function formatBytes(bytes) {
|
|
|
62812
62848
|
}
|
|
62813
62849
|
function getUsedMemoryMacOS() {
|
|
62814
62850
|
try {
|
|
62815
|
-
const output =
|
|
62851
|
+
const output = execSync3("vm_stat", { encoding: "utf8" });
|
|
62816
62852
|
const lines = output.split(`
|
|
62817
62853
|
`);
|
|
62818
62854
|
const firstLine = lines[0];
|
|
@@ -62934,6 +62970,61 @@ class SessionNameWidget {
|
|
|
62934
62970
|
}
|
|
62935
62971
|
var init_SessionName = () => {};
|
|
62936
62972
|
|
|
62973
|
+
// src/utils/usage/providers/anthropic.ts
|
|
62974
|
+
var anthropicProvider;
|
|
62975
|
+
var init_anthropic = __esm(() => {
|
|
62976
|
+
init_usage_fetch();
|
|
62977
|
+
anthropicProvider = {
|
|
62978
|
+
name: "anthropic",
|
|
62979
|
+
async fetchUsage() {
|
|
62980
|
+
const data = await fetchUsageData();
|
|
62981
|
+
return { ...data, provider: "anthropic" };
|
|
62982
|
+
}
|
|
62983
|
+
};
|
|
62984
|
+
});
|
|
62985
|
+
|
|
62986
|
+
// src/utils/usage/providers/null.ts
|
|
62987
|
+
var nullProvider;
|
|
62988
|
+
var init_null = __esm(() => {
|
|
62989
|
+
nullProvider = {
|
|
62990
|
+
name: "null",
|
|
62991
|
+
fetchUsage() {
|
|
62992
|
+
return Promise.resolve({ provider: null });
|
|
62993
|
+
}
|
|
62994
|
+
};
|
|
62995
|
+
});
|
|
62996
|
+
|
|
62997
|
+
// src/utils/usage/providers/opencode.ts
|
|
62998
|
+
var opencodeProvider;
|
|
62999
|
+
var init_opencode = __esm(() => {
|
|
63000
|
+
opencodeProvider = {
|
|
63001
|
+
name: "opencode",
|
|
63002
|
+
fetchUsage() {
|
|
63003
|
+
return Promise.resolve({ provider: "opencode" });
|
|
63004
|
+
}
|
|
63005
|
+
};
|
|
63006
|
+
});
|
|
63007
|
+
|
|
63008
|
+
// src/utils/usage/resolver.ts
|
|
63009
|
+
function resolveProvider(modelId) {
|
|
63010
|
+
if (!modelId)
|
|
63011
|
+
return nullProvider;
|
|
63012
|
+
const id = modelId.toLowerCase();
|
|
63013
|
+
if (ANTHROPIC_KEYWORDS.some((k) => id.includes(k)))
|
|
63014
|
+
return anthropicProvider;
|
|
63015
|
+
if (OPENCODE_PATTERN.test(id))
|
|
63016
|
+
return opencodeProvider;
|
|
63017
|
+
return nullProvider;
|
|
63018
|
+
}
|
|
63019
|
+
var OPENCODE_PATTERN, ANTHROPIC_KEYWORDS;
|
|
63020
|
+
var init_resolver = __esm(() => {
|
|
63021
|
+
init_anthropic();
|
|
63022
|
+
init_null();
|
|
63023
|
+
init_opencode();
|
|
63024
|
+
OPENCODE_PATTERN = /(?:^|[^a-z])(glm|kimi|minimax|mm-|qwen|owen|mimo)/i;
|
|
63025
|
+
ANTHROPIC_KEYWORDS = ["opus", "sonnet", "haiku"];
|
|
63026
|
+
});
|
|
63027
|
+
|
|
62937
63028
|
// src/widgets/ApiUsage.tsx
|
|
62938
63029
|
function getDisplaySize(context) {
|
|
62939
63030
|
const w = context.terminalWidth ?? 0;
|
|
@@ -62991,17 +63082,6 @@ function getModelId(context) {
|
|
|
62991
63082
|
const model = context.data?.model;
|
|
62992
63083
|
return (typeof model === "string" ? model : model?.id) ?? "";
|
|
62993
63084
|
}
|
|
62994
|
-
function isLocalModel(context) {
|
|
62995
|
-
const modelId = getModelId(context);
|
|
62996
|
-
if (modelId === "")
|
|
62997
|
-
return false;
|
|
62998
|
-
return !(modelId.includes("opus") || modelId.includes("sonnet") || modelId.includes("haiku"));
|
|
62999
|
-
}
|
|
63000
|
-
function renderLocalUsageFallback(label, shortLabel, size2) {
|
|
63001
|
-
if (size2 === "mobile")
|
|
63002
|
-
return `${shortLabel}: [░░░░] -.0%`;
|
|
63003
|
-
return `${label}: [░░░░░░░░░░░░░░░] -.0%`;
|
|
63004
|
-
}
|
|
63005
63085
|
|
|
63006
63086
|
class SessionUsageWidget {
|
|
63007
63087
|
getDefaultColor() {
|
|
@@ -63028,8 +63108,8 @@ class SessionUsageWidget {
|
|
|
63028
63108
|
if (data.sessionUsage === undefined)
|
|
63029
63109
|
return null;
|
|
63030
63110
|
const size2 = getDisplaySize(context);
|
|
63031
|
-
if (
|
|
63032
|
-
return
|
|
63111
|
+
if (resolveProvider(getModelId(context)).name === "null")
|
|
63112
|
+
return null;
|
|
63033
63113
|
const extraUsed = data.extraUsageUsed;
|
|
63034
63114
|
const extraLimit = data.extraUsageLimit;
|
|
63035
63115
|
if (size2 !== "mobile" && data.extraUsageEnabled === true && extraUsed !== undefined && extraLimit !== undefined && data.sessionUsage >= 100 && (data.weeklyUsage === undefined || data.weeklyUsage < 100)) {
|
|
@@ -63071,8 +63151,8 @@ class WeeklyUsageWidget {
|
|
|
63071
63151
|
if (data.weeklyUsage === undefined)
|
|
63072
63152
|
return null;
|
|
63073
63153
|
const size2 = getDisplaySize(context);
|
|
63074
|
-
if (
|
|
63075
|
-
return
|
|
63154
|
+
if (resolveProvider(getModelId(context)).name === "null")
|
|
63155
|
+
return null;
|
|
63076
63156
|
const extraUsed = data.extraUsageUsed;
|
|
63077
63157
|
const extraLimit = data.extraUsageLimit;
|
|
63078
63158
|
if (data.extraUsageEnabled === true && extraUsed !== undefined && extraLimit !== undefined && data.weeklyUsage >= 100) {
|
|
@@ -63111,8 +63191,8 @@ class ResetTimerWidget {
|
|
|
63111
63191
|
const data = context.usageData ?? {};
|
|
63112
63192
|
if (data.error)
|
|
63113
63193
|
return getUsageErrorMessage(data.error);
|
|
63114
|
-
if (
|
|
63115
|
-
return
|
|
63194
|
+
if (resolveProvider(getModelId(context)).name === "null")
|
|
63195
|
+
return null;
|
|
63116
63196
|
const modelId = getModelId(context);
|
|
63117
63197
|
const is1mModel = modelId.includes("[1m]");
|
|
63118
63198
|
const isOpus = modelId.includes("opus");
|
|
@@ -63171,8 +63251,6 @@ class ContextBarWidget {
|
|
|
63171
63251
|
const cw = context.data?.context_window;
|
|
63172
63252
|
if (!cw)
|
|
63173
63253
|
return null;
|
|
63174
|
-
if (isLocalModel(context) && !getModelId(context).includes("qwen"))
|
|
63175
|
-
return renderLocalUsageFallback("Context", "C", getDisplaySize(context));
|
|
63176
63254
|
const total = Number(cw.context_window_size) || 200000;
|
|
63177
63255
|
let used = 0;
|
|
63178
63256
|
if (typeof cw.current_usage === "number") {
|
|
@@ -63202,6 +63280,7 @@ class ContextBarWidget {
|
|
|
63202
63280
|
var DARK_RED_OPEN2 = "\x1B[38;2;204;0;0m", DARK_RED_CLOSE2 = "\x1B[39m", MOBILE_THRESHOLD = 134, MEDIUM_THRESHOLD2 = 178, MOBILE_BAR_WIDTH = 4, MEDIUM_BAR_WIDTH = 8, DEFAULT_BAR_WIDTH = 15;
|
|
63203
63281
|
var init_ApiUsage = __esm(() => {
|
|
63204
63282
|
init_usage();
|
|
63283
|
+
init_resolver();
|
|
63205
63284
|
});
|
|
63206
63285
|
|
|
63207
63286
|
// src/widgets/WeeklyResetTimer.ts
|
|
@@ -63775,11 +63854,11 @@ var init_ThinkingEffort = __esm(() => {
|
|
|
63775
63854
|
});
|
|
63776
63855
|
|
|
63777
63856
|
// src/widgets/Battery.ts
|
|
63778
|
-
import { execSync as
|
|
63857
|
+
import { execSync as execSync4 } from "child_process";
|
|
63779
63858
|
import { readFileSync as readFileSync11 } from "fs";
|
|
63780
63859
|
function getMacBatteryInfo() {
|
|
63781
63860
|
try {
|
|
63782
|
-
const output =
|
|
63861
|
+
const output = execSync4("pmset -g batt", { encoding: "utf-8", timeout: 2000 });
|
|
63783
63862
|
const match = /(\d+)%;\s*(charging|discharging|charged|finishing charge|AC attached)/i.exec(output);
|
|
63784
63863
|
const percentStr = match?.[1];
|
|
63785
63864
|
const stateStr = match?.[2];
|
|
@@ -64904,7 +64983,7 @@ var init_config = __esm(() => {
|
|
|
64904
64983
|
});
|
|
64905
64984
|
|
|
64906
64985
|
// src/utils/claude-settings.ts
|
|
64907
|
-
import { execSync as
|
|
64986
|
+
import { execSync as execSync5 } from "child_process";
|
|
64908
64987
|
import * as fs11 from "fs";
|
|
64909
64988
|
import * as os9 from "os";
|
|
64910
64989
|
import * as path9 from "path";
|
|
@@ -65013,7 +65092,7 @@ async function isInstalled() {
|
|
|
65013
65092
|
function isBunxAvailable() {
|
|
65014
65093
|
try {
|
|
65015
65094
|
const command = process.platform === "win32" ? "where bunx" : "which bunx";
|
|
65016
|
-
|
|
65095
|
+
execSync5(command, { stdio: "ignore" });
|
|
65017
65096
|
return true;
|
|
65018
65097
|
} catch {
|
|
65019
65098
|
return false;
|
|
@@ -65021,7 +65100,7 @@ function isBunxAvailable() {
|
|
|
65021
65100
|
}
|
|
65022
65101
|
function getClaudeCodeVersion() {
|
|
65023
65102
|
try {
|
|
65024
|
-
const output =
|
|
65103
|
+
const output = execSync5("claude --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"], timeout: 5000 }).trim();
|
|
65025
65104
|
const match = /^(\d+\.\d+\.\d+)/.exec(output);
|
|
65026
65105
|
return match?.[1] ?? null;
|
|
65027
65106
|
} catch {
|
|
@@ -65832,7 +65911,7 @@ function openExternalUrl(url2) {
|
|
|
65832
65911
|
}
|
|
65833
65912
|
|
|
65834
65913
|
// src/utils/powerline.ts
|
|
65835
|
-
import { execSync as
|
|
65914
|
+
import { execSync as execSync6 } from "child_process";
|
|
65836
65915
|
import * as fs12 from "fs";
|
|
65837
65916
|
import * as os11 from "os";
|
|
65838
65917
|
import * as path10 from "path";
|
|
@@ -65966,7 +66045,7 @@ async function installPowerlineFonts() {
|
|
|
65966
66045
|
if (fs12.existsSync(tempDir)) {
|
|
65967
66046
|
fs12.rmSync(tempDir, { recursive: true, force: true });
|
|
65968
66047
|
}
|
|
65969
|
-
|
|
66048
|
+
execSync6(`git clone --depth=1 https://github.com/powerline/fonts.git "${tempDir}"`, {
|
|
65970
66049
|
stdio: "pipe",
|
|
65971
66050
|
encoding: "utf8"
|
|
65972
66051
|
});
|
|
@@ -65974,14 +66053,14 @@ async function installPowerlineFonts() {
|
|
|
65974
66053
|
const installScript = path10.join(tempDir, "install.sh");
|
|
65975
66054
|
if (fs12.existsSync(installScript)) {
|
|
65976
66055
|
fs12.chmodSync(installScript, 493);
|
|
65977
|
-
|
|
66056
|
+
execSync6(`cd "${tempDir}" && ./install.sh`, {
|
|
65978
66057
|
stdio: "pipe",
|
|
65979
66058
|
encoding: "utf8",
|
|
65980
66059
|
shell: "/bin/bash"
|
|
65981
66060
|
});
|
|
65982
66061
|
if (platform4 === "linux") {
|
|
65983
66062
|
try {
|
|
65984
|
-
|
|
66063
|
+
execSync6("fc-cache -f -v", {
|
|
65985
66064
|
stdio: "pipe",
|
|
65986
66065
|
encoding: "utf8"
|
|
65987
66066
|
});
|
|
@@ -71250,56 +71329,7 @@ var StatusJSONSchema = exports_external.looseObject({
|
|
|
71250
71329
|
// src/ccstatusline.ts
|
|
71251
71330
|
init_ansi();
|
|
71252
71331
|
init_colors();
|
|
71253
|
-
init_config();
|
|
71254
|
-
init_jsonl();
|
|
71255
|
-
await init_renderer2();
|
|
71256
71332
|
|
|
71257
|
-
// src/utils/skills.ts
|
|
71258
|
-
import * as fs13 from "fs";
|
|
71259
|
-
import * as os13 from "os";
|
|
71260
|
-
import * as path11 from "path";
|
|
71261
|
-
var EMPTY = { totalInvocations: 0, uniqueSkills: [], lastSkill: null };
|
|
71262
|
-
function getSkillsDir() {
|
|
71263
|
-
return path11.join(os13.homedir(), ".cache", "ccstatusline", "skills");
|
|
71264
|
-
}
|
|
71265
|
-
function getSkillsFilePath(sessionId) {
|
|
71266
|
-
return path11.join(getSkillsDir(), `skills-${sessionId}.jsonl`);
|
|
71267
|
-
}
|
|
71268
|
-
function getSkillsMetrics(sessionId) {
|
|
71269
|
-
const filePath = getSkillsFilePath(sessionId);
|
|
71270
|
-
if (!fs13.existsSync(filePath)) {
|
|
71271
|
-
return EMPTY;
|
|
71272
|
-
}
|
|
71273
|
-
try {
|
|
71274
|
-
const invocations = fs13.readFileSync(filePath, "utf-8").trim().split(`
|
|
71275
|
-
`).filter((line) => line.trim()).map((line) => {
|
|
71276
|
-
try {
|
|
71277
|
-
return JSON.parse(line);
|
|
71278
|
-
} catch {
|
|
71279
|
-
return null;
|
|
71280
|
-
}
|
|
71281
|
-
}).filter((e) => e !== null && typeof e.skill === "string" && typeof e.session_id === "string");
|
|
71282
|
-
if (invocations.length === 0) {
|
|
71283
|
-
return EMPTY;
|
|
71284
|
-
}
|
|
71285
|
-
const uniqueSkills = [];
|
|
71286
|
-
const seenSkills = new Set;
|
|
71287
|
-
for (let i = invocations.length - 1;i >= 0; i--) {
|
|
71288
|
-
const skill = invocations[i]?.skill;
|
|
71289
|
-
if (skill && !seenSkills.has(skill)) {
|
|
71290
|
-
seenSkills.add(skill);
|
|
71291
|
-
uniqueSkills.push(skill);
|
|
71292
|
-
}
|
|
71293
|
-
}
|
|
71294
|
-
return {
|
|
71295
|
-
totalInvocations: invocations.length,
|
|
71296
|
-
uniqueSkills,
|
|
71297
|
-
lastSkill: invocations[invocations.length - 1]?.skill ?? null
|
|
71298
|
-
};
|
|
71299
|
-
} catch {
|
|
71300
|
-
return EMPTY;
|
|
71301
|
-
}
|
|
71302
|
-
}
|
|
71303
71333
|
// src/utils/compact-renderer.ts
|
|
71304
71334
|
init_source();
|
|
71305
71335
|
init_ColorLevel();
|
|
@@ -71361,11 +71391,63 @@ function renderCompactOutput(preRenderedLines, settings, maxWidth) {
|
|
|
71361
71391
|
}
|
|
71362
71392
|
}
|
|
71363
71393
|
|
|
71394
|
+
// src/ccstatusline.ts
|
|
71395
|
+
init_config();
|
|
71396
|
+
init_jsonl();
|
|
71397
|
+
await init_renderer2();
|
|
71398
|
+
|
|
71399
|
+
// src/utils/skills.ts
|
|
71400
|
+
import * as fs13 from "fs";
|
|
71401
|
+
import * as os13 from "os";
|
|
71402
|
+
import * as path11 from "path";
|
|
71403
|
+
var EMPTY = { totalInvocations: 0, uniqueSkills: [], lastSkill: null };
|
|
71404
|
+
function getSkillsDir() {
|
|
71405
|
+
return path11.join(os13.homedir(), ".cache", "ccstatusline", "skills");
|
|
71406
|
+
}
|
|
71407
|
+
function getSkillsFilePath(sessionId) {
|
|
71408
|
+
return path11.join(getSkillsDir(), `skills-${sessionId}.jsonl`);
|
|
71409
|
+
}
|
|
71410
|
+
function getSkillsMetrics(sessionId) {
|
|
71411
|
+
const filePath = getSkillsFilePath(sessionId);
|
|
71412
|
+
if (!fs13.existsSync(filePath)) {
|
|
71413
|
+
return EMPTY;
|
|
71414
|
+
}
|
|
71415
|
+
try {
|
|
71416
|
+
const invocations = fs13.readFileSync(filePath, "utf-8").trim().split(`
|
|
71417
|
+
`).filter((line) => line.trim()).map((line) => {
|
|
71418
|
+
try {
|
|
71419
|
+
return JSON.parse(line);
|
|
71420
|
+
} catch {
|
|
71421
|
+
return null;
|
|
71422
|
+
}
|
|
71423
|
+
}).filter((e) => e !== null && typeof e.skill === "string" && typeof e.session_id === "string");
|
|
71424
|
+
if (invocations.length === 0) {
|
|
71425
|
+
return EMPTY;
|
|
71426
|
+
}
|
|
71427
|
+
const uniqueSkills = [];
|
|
71428
|
+
const seenSkills = new Set;
|
|
71429
|
+
for (let i = invocations.length - 1;i >= 0; i--) {
|
|
71430
|
+
const skill = invocations[i]?.skill;
|
|
71431
|
+
if (skill && !seenSkills.has(skill)) {
|
|
71432
|
+
seenSkills.add(skill);
|
|
71433
|
+
uniqueSkills.push(skill);
|
|
71434
|
+
}
|
|
71435
|
+
}
|
|
71436
|
+
return {
|
|
71437
|
+
totalInvocations: invocations.length,
|
|
71438
|
+
uniqueSkills,
|
|
71439
|
+
lastSkill: invocations[invocations.length - 1]?.skill ?? null
|
|
71440
|
+
};
|
|
71441
|
+
} catch {
|
|
71442
|
+
return EMPTY;
|
|
71443
|
+
}
|
|
71444
|
+
}
|
|
71445
|
+
|
|
71364
71446
|
// src/ccstatusline.ts
|
|
71365
71447
|
init_terminal();
|
|
71366
71448
|
|
|
71367
71449
|
// src/utils/usage-prefetch.ts
|
|
71368
|
-
|
|
71450
|
+
init_resolver();
|
|
71369
71451
|
var USAGE_WIDGET_TYPES = new Set([
|
|
71370
71452
|
"session-usage",
|
|
71371
71453
|
"weekly-usage",
|
|
@@ -71406,17 +71488,20 @@ async function prefetchUsageDataIfNeeded(lines, data) {
|
|
|
71406
71488
|
if (!hasUsageDependentWidgets(lines)) {
|
|
71407
71489
|
return null;
|
|
71408
71490
|
}
|
|
71491
|
+
const model = data?.model;
|
|
71492
|
+
const modelId = (typeof model === "string" ? model : model?.id) ?? "";
|
|
71493
|
+
const provider = resolveProvider(modelId);
|
|
71409
71494
|
const rateLimitsData = extractUsageDataFromRateLimits(data?.rate_limits);
|
|
71410
71495
|
if (hasCompleteRateLimitsUsageData(rateLimitsData)) {
|
|
71411
71496
|
if (hasExtraUsageDependentWidgets(lines)) {
|
|
71412
|
-
const apiData = await
|
|
71497
|
+
const apiData = await provider.fetchUsage();
|
|
71413
71498
|
if (apiData.error === undefined) {
|
|
71414
71499
|
return { ...rateLimitsData, extraUsageEnabled: apiData.extraUsageEnabled, extraUsageLimit: apiData.extraUsageLimit, extraUsageUsed: apiData.extraUsageUsed, extraUsageUtilization: apiData.extraUsageUtilization };
|
|
71415
71500
|
}
|
|
71416
71501
|
}
|
|
71417
71502
|
return rateLimitsData;
|
|
71418
71503
|
}
|
|
71419
|
-
return
|
|
71504
|
+
return provider.fetchUsage();
|
|
71420
71505
|
}
|
|
71421
71506
|
|
|
71422
71507
|
// src/ccstatusline.ts
|
|
@@ -71460,8 +71545,8 @@ async function ensureWindowsUtf8CodePage() {
|
|
|
71460
71545
|
return;
|
|
71461
71546
|
}
|
|
71462
71547
|
try {
|
|
71463
|
-
const { execFileSync:
|
|
71464
|
-
|
|
71548
|
+
const { execFileSync: execFileSync4 } = await import("child_process");
|
|
71549
|
+
execFileSync4("chcp.com", ["65001"], { stdio: "ignore" });
|
|
71465
71550
|
} catch {}
|
|
71466
71551
|
}
|
|
71467
71552
|
async function renderMultipleLines(data) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccstatusline-usage",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "A customizable status line formatter for Claude Code CLI",
|
|
5
5
|
"module": "src/ccstatusline.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"chalk": "^5.5.0",
|
|
31
31
|
"eslint": "^10.0.0",
|
|
32
32
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
33
|
-
"eslint-plugin-import": "^2.32.0",
|
|
34
33
|
"eslint-plugin-import-newlines": "^2.0.0",
|
|
34
|
+
"eslint-plugin-import-x": "^4.16.2",
|
|
35
35
|
"eslint-plugin-react": "^7.37.5",
|
|
36
36
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
37
37
|
"globals": "^17.3.0",
|