ccstatusline-usage 2.0.28 → 2.0.30
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/defaults/settings-enhanced.json +73 -0
- package/dist/ccstatusline.js +139 -77
- package/package.json +6 -2
- package/scripts/context.sh +58 -0
- package/scripts/setup-enhanced.sh +98 -0
- package/scripts/usage.sh +147 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"lines": [
|
|
4
|
+
[
|
|
5
|
+
{
|
|
6
|
+
"id": "session-usage",
|
|
7
|
+
"type": "custom-command",
|
|
8
|
+
"color": "brightBlue",
|
|
9
|
+
"commandPath": "scripts/usage.sh session"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "sep-session-weekly",
|
|
13
|
+
"type": "separator"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "weekly-usage",
|
|
17
|
+
"type": "custom-command",
|
|
18
|
+
"color": "brightBlue",
|
|
19
|
+
"commandPath": "scripts/usage.sh weekly"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "sep-weekly-reset",
|
|
23
|
+
"type": "separator"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "reset-timer",
|
|
27
|
+
"type": "custom-command",
|
|
28
|
+
"color": "brightBlue",
|
|
29
|
+
"commandPath": "scripts/usage.sh reset"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "sep-reset-model",
|
|
33
|
+
"type": "separator"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "model",
|
|
37
|
+
"type": "model",
|
|
38
|
+
"color": "magenta"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "sep-model-chatid",
|
|
42
|
+
"type": "separator"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "chat-id",
|
|
46
|
+
"type": "claude-session-id",
|
|
47
|
+
"color": "cyan"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
{
|
|
52
|
+
"id": "context-usage",
|
|
53
|
+
"type": "custom-command",
|
|
54
|
+
"color": "blue",
|
|
55
|
+
"commandPath": "scripts/context.sh"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
[]
|
|
59
|
+
],
|
|
60
|
+
"flexMode": "full-minus-40",
|
|
61
|
+
"compactThreshold": 60,
|
|
62
|
+
"colorLevel": 2,
|
|
63
|
+
"inheritSeparatorColors": false,
|
|
64
|
+
"globalBold": false,
|
|
65
|
+
"powerline": {
|
|
66
|
+
"enabled": false,
|
|
67
|
+
"separators": [""],
|
|
68
|
+
"separatorInvertBackground": [false],
|
|
69
|
+
"startCaps": [],
|
|
70
|
+
"endCaps": [],
|
|
71
|
+
"autoAlign": false
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/ccstatusline.js
CHANGED
|
@@ -32383,8 +32383,8 @@ var require_utils = __commonJS((exports) => {
|
|
|
32383
32383
|
}
|
|
32384
32384
|
return output;
|
|
32385
32385
|
};
|
|
32386
|
-
exports.basename = (
|
|
32387
|
-
const segs =
|
|
32386
|
+
exports.basename = (path6, { windows } = {}) => {
|
|
32387
|
+
const segs = path6.split(windows ? /[\\/]/ : "/");
|
|
32388
32388
|
const last = segs[segs.length - 1];
|
|
32389
32389
|
if (last === "") {
|
|
32390
32390
|
return segs[segs.length - 2];
|
|
@@ -50982,18 +50982,18 @@ var SettingsSchema = exports_external.object({
|
|
|
50982
50982
|
version: exports_external.number().default(CURRENT_VERSION),
|
|
50983
50983
|
lines: exports_external.array(exports_external.array(WidgetItemSchema)).min(1).default([
|
|
50984
50984
|
[
|
|
50985
|
-
{ id: "
|
|
50985
|
+
{ id: "session-usage", type: "custom-command", color: "brightBlue", commandPath: "$PKG/scripts/usage.sh session", timeout: 5000, label: "Session Usage" },
|
|
50986
50986
|
{ id: "sep1", type: "separator" },
|
|
50987
|
-
{ id: "
|
|
50987
|
+
{ id: "weekly-usage", type: "custom-command", color: "brightBlue", commandPath: "$PKG/scripts/usage.sh weekly", timeout: 5000, label: "Weekly Usage" },
|
|
50988
50988
|
{ id: "sep2", type: "separator" },
|
|
50989
|
-
{ id: "
|
|
50989
|
+
{ id: "reset-timer", type: "custom-command", color: "brightBlue", commandPath: "$PKG/scripts/usage.sh reset", timeout: 5000, label: "Reset Timer" },
|
|
50990
50990
|
{ id: "sep3", type: "separator" },
|
|
50991
|
+
{ id: "model", type: "model", color: "magenta" },
|
|
50992
|
+
{ id: "sep4", type: "separator" },
|
|
50991
50993
|
{ id: "session-id", type: "claude-session-id", color: "cyan" }
|
|
50992
50994
|
],
|
|
50993
50995
|
[
|
|
50994
|
-
{ id: "
|
|
50995
|
-
{ id: "sep4", type: "separator" },
|
|
50996
|
-
{ id: "git-changes", type: "git-changes", color: "yellow" }
|
|
50996
|
+
{ id: "context-usage", type: "custom-command", color: "blue", commandPath: "$PKG/scripts/context.sh", timeout: 5000, label: "Context Bar" }
|
|
50997
50997
|
],
|
|
50998
50998
|
[]
|
|
50999
50999
|
]),
|
|
@@ -51450,7 +51450,7 @@ import { execSync as execSync3 } from "child_process";
|
|
|
51450
51450
|
import * as fs5 from "fs";
|
|
51451
51451
|
import * as path4 from "path";
|
|
51452
51452
|
var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils";
|
|
51453
|
-
var PACKAGE_VERSION = "2.0.
|
|
51453
|
+
var PACKAGE_VERSION = "2.0.30";
|
|
51454
51454
|
function getPackageVersion() {
|
|
51455
51455
|
if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
|
|
51456
51456
|
return PACKAGE_VERSION;
|
|
@@ -53812,8 +53812,56 @@ var CustomTextEditor = ({ widget, onComplete, onCancel }) => {
|
|
|
53812
53812
|
};
|
|
53813
53813
|
// src/widgets/CustomCommand.tsx
|
|
53814
53814
|
import { execSync as execSync7 } from "child_process";
|
|
53815
|
+
import * as fs6 from "fs";
|
|
53815
53816
|
var import_react30 = __toESM(require_react(), 1);
|
|
53816
53817
|
var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
|
|
53818
|
+
import * as path5 from "path";
|
|
53819
|
+
function getPackageDir() {
|
|
53820
|
+
const scriptPath = process.argv[1];
|
|
53821
|
+
if (scriptPath) {
|
|
53822
|
+
try {
|
|
53823
|
+
const realPath = fs6.realpathSync(scriptPath);
|
|
53824
|
+
let dir = path5.dirname(realPath);
|
|
53825
|
+
for (let i = 0;i < 10; i++) {
|
|
53826
|
+
const pkgJson = path5.join(dir, "package.json");
|
|
53827
|
+
if (fs6.existsSync(pkgJson)) {
|
|
53828
|
+
try {
|
|
53829
|
+
const pkg = JSON.parse(fs6.readFileSync(pkgJson, "utf8"));
|
|
53830
|
+
if (pkg.name === "ccstatusline-usage") {
|
|
53831
|
+
return dir;
|
|
53832
|
+
}
|
|
53833
|
+
} catch {}
|
|
53834
|
+
}
|
|
53835
|
+
const scriptsDir = path5.join(dir, "scripts");
|
|
53836
|
+
if (fs6.existsSync(path5.join(scriptsDir, "usage.sh"))) {
|
|
53837
|
+
return dir;
|
|
53838
|
+
}
|
|
53839
|
+
dir = path5.dirname(dir);
|
|
53840
|
+
}
|
|
53841
|
+
} catch {}
|
|
53842
|
+
}
|
|
53843
|
+
const home = process.env.HOME ?? "";
|
|
53844
|
+
const npxPaths = [
|
|
53845
|
+
path5.join(home, ".npm", "_npx"),
|
|
53846
|
+
path5.join(home, ".cache", "npm", "_npx"),
|
|
53847
|
+
path5.join(home, ".local", "share", "npm", "_npx")
|
|
53848
|
+
];
|
|
53849
|
+
for (const npxBase of npxPaths) {
|
|
53850
|
+
if (fs6.existsSync(npxBase)) {
|
|
53851
|
+
try {
|
|
53852
|
+
const entries = fs6.readdirSync(npxBase);
|
|
53853
|
+
for (const entry of entries) {
|
|
53854
|
+
const pkgDir = path5.join(npxBase, entry, "node_modules", "ccstatusline-usage");
|
|
53855
|
+
if (fs6.existsSync(path5.join(pkgDir, "scripts", "usage.sh"))) {
|
|
53856
|
+
return pkgDir;
|
|
53857
|
+
}
|
|
53858
|
+
}
|
|
53859
|
+
} catch {}
|
|
53860
|
+
}
|
|
53861
|
+
}
|
|
53862
|
+
return "";
|
|
53863
|
+
}
|
|
53864
|
+
var PKG_DIR = getPackageDir();
|
|
53817
53865
|
|
|
53818
53866
|
class CustomCommandWidget {
|
|
53819
53867
|
getDefaultColor() {
|
|
@@ -53826,9 +53874,11 @@ class CustomCommandWidget {
|
|
|
53826
53874
|
return "Custom Command";
|
|
53827
53875
|
}
|
|
53828
53876
|
getEditorDisplay(item) {
|
|
53829
|
-
const
|
|
53830
|
-
|
|
53831
|
-
|
|
53877
|
+
const displayText = item.label ? item.label : (() => {
|
|
53878
|
+
const cmd = item.commandPath ?? "No command";
|
|
53879
|
+
const truncatedCmd = cmd.length > 20 ? `${cmd.substring(0, 17)}...` : cmd;
|
|
53880
|
+
return `${this.getDisplayName()} (${truncatedCmd})`;
|
|
53881
|
+
})();
|
|
53832
53882
|
const modifiers = [];
|
|
53833
53883
|
if (item.maxWidth) {
|
|
53834
53884
|
modifiers.push(`max:${item.maxWidth}`);
|
|
@@ -53850,6 +53900,17 @@ class CustomCommandWidget {
|
|
|
53850
53900
|
}
|
|
53851
53901
|
return null;
|
|
53852
53902
|
}
|
|
53903
|
+
resolveCommandPath(commandPath) {
|
|
53904
|
+
if (commandPath.startsWith("$PKG/") || commandPath.startsWith("$PACKAGE_DIR/")) {
|
|
53905
|
+
const relativePath = commandPath.replace(/^\$(PKG|PACKAGE_DIR)\//, "");
|
|
53906
|
+
const resolved = path5.join(PKG_DIR, relativePath);
|
|
53907
|
+
return resolved;
|
|
53908
|
+
}
|
|
53909
|
+
if (commandPath.startsWith("$HOME/")) {
|
|
53910
|
+
return commandPath.replace("$HOME", process.env.HOME ?? "~");
|
|
53911
|
+
}
|
|
53912
|
+
return commandPath;
|
|
53913
|
+
}
|
|
53853
53914
|
render(item, context, settings) {
|
|
53854
53915
|
if (context.isPreview) {
|
|
53855
53916
|
return item.commandPath ? `[cmd: ${item.commandPath.substring(0, 20)}${item.commandPath.length > 20 ? "..." : ""}]` : "[No command]";
|
|
@@ -53857,7 +53918,8 @@ class CustomCommandWidget {
|
|
|
53857
53918
|
try {
|
|
53858
53919
|
const timeout = item.timeout ?? 1000;
|
|
53859
53920
|
const jsonInput = JSON.stringify(context.data);
|
|
53860
|
-
|
|
53921
|
+
const resolvedPath = this.resolveCommandPath(item.commandPath);
|
|
53922
|
+
let output = execSync7(resolvedPath, {
|
|
53861
53923
|
encoding: "utf8",
|
|
53862
53924
|
input: jsonInput,
|
|
53863
53925
|
timeout,
|
|
@@ -54278,13 +54340,13 @@ class CurrentWorkingDirWidget {
|
|
|
54278
54340
|
supportsColors(item) {
|
|
54279
54341
|
return true;
|
|
54280
54342
|
}
|
|
54281
|
-
abbreviatePath(
|
|
54343
|
+
abbreviatePath(path6) {
|
|
54282
54344
|
const homeDir = os5.homedir();
|
|
54283
|
-
const useBackslash =
|
|
54345
|
+
const useBackslash = path6.includes("\\") && !path6.includes("/");
|
|
54284
54346
|
const sep = useBackslash ? "\\" : "/";
|
|
54285
|
-
let normalizedPath =
|
|
54286
|
-
if (
|
|
54287
|
-
normalizedPath = "~" +
|
|
54347
|
+
let normalizedPath = path6;
|
|
54348
|
+
if (path6.startsWith(homeDir)) {
|
|
54349
|
+
normalizedPath = "~" + path6.slice(homeDir.length);
|
|
54288
54350
|
}
|
|
54289
54351
|
const parts = normalizedPath.split(/[\\/]+/).filter((part) => part !== "");
|
|
54290
54352
|
const abbreviated = parts.map((part, index) => {
|
|
@@ -58238,42 +58300,42 @@ var StatusJSONSchema = exports_external.looseObject({
|
|
|
58238
58300
|
});
|
|
58239
58301
|
|
|
58240
58302
|
// src/utils/jsonl.ts
|
|
58241
|
-
import * as
|
|
58242
|
-
import
|
|
58303
|
+
import * as fs7 from "fs";
|
|
58304
|
+
import path7 from "node:path";
|
|
58243
58305
|
|
|
58244
58306
|
// node_modules/tinyglobby/dist/index.mjs
|
|
58245
|
-
import
|
|
58307
|
+
import path6, { posix } from "path";
|
|
58246
58308
|
|
|
58247
58309
|
// node_modules/fdir/dist/index.mjs
|
|
58248
58310
|
import { createRequire as createRequire2 } from "module";
|
|
58249
|
-
import { basename as basename2, dirname as
|
|
58311
|
+
import { basename as basename2, dirname as dirname3, normalize, relative, resolve as resolve2, sep } from "path";
|
|
58250
58312
|
import * as nativeFs from "fs";
|
|
58251
58313
|
var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
|
|
58252
|
-
function cleanPath(
|
|
58253
|
-
let normalized = normalize(
|
|
58314
|
+
function cleanPath(path6) {
|
|
58315
|
+
let normalized = normalize(path6);
|
|
58254
58316
|
if (normalized.length > 1 && normalized[normalized.length - 1] === sep)
|
|
58255
58317
|
normalized = normalized.substring(0, normalized.length - 1);
|
|
58256
58318
|
return normalized;
|
|
58257
58319
|
}
|
|
58258
58320
|
var SLASHES_REGEX = /[\\/]/g;
|
|
58259
|
-
function convertSlashes(
|
|
58260
|
-
return
|
|
58321
|
+
function convertSlashes(path6, separator) {
|
|
58322
|
+
return path6.replace(SLASHES_REGEX, separator);
|
|
58261
58323
|
}
|
|
58262
58324
|
var WINDOWS_ROOT_DIR_REGEX = /^[a-z]:[\\/]$/i;
|
|
58263
|
-
function isRootDirectory(
|
|
58264
|
-
return
|
|
58325
|
+
function isRootDirectory(path6) {
|
|
58326
|
+
return path6 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path6);
|
|
58265
58327
|
}
|
|
58266
|
-
function normalizePath(
|
|
58328
|
+
function normalizePath(path6, options) {
|
|
58267
58329
|
const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
|
|
58268
|
-
const pathNeedsCleaning = process.platform === "win32" &&
|
|
58330
|
+
const pathNeedsCleaning = process.platform === "win32" && path6.includes("/") || path6.startsWith(".");
|
|
58269
58331
|
if (resolvePaths)
|
|
58270
|
-
|
|
58332
|
+
path6 = resolve2(path6);
|
|
58271
58333
|
if (normalizePath$1 || pathNeedsCleaning)
|
|
58272
|
-
|
|
58273
|
-
if (
|
|
58334
|
+
path6 = cleanPath(path6);
|
|
58335
|
+
if (path6 === ".")
|
|
58274
58336
|
return "";
|
|
58275
|
-
const needsSeperator =
|
|
58276
|
-
return convertSlashes(needsSeperator ?
|
|
58337
|
+
const needsSeperator = path6[path6.length - 1] !== pathSeparator;
|
|
58338
|
+
return convertSlashes(needsSeperator ? path6 + pathSeparator : path6, pathSeparator);
|
|
58277
58339
|
}
|
|
58278
58340
|
function joinPathWithBasePath(filename, directoryPath) {
|
|
58279
58341
|
return directoryPath + filename;
|
|
@@ -58313,9 +58375,9 @@ var pushDirectory = (directoryPath, paths) => {
|
|
|
58313
58375
|
paths.push(directoryPath || ".");
|
|
58314
58376
|
};
|
|
58315
58377
|
var pushDirectoryFilter = (directoryPath, paths, filters) => {
|
|
58316
|
-
const
|
|
58317
|
-
if (filters.every((filter) => filter(
|
|
58318
|
-
paths.push(
|
|
58378
|
+
const path6 = directoryPath || ".";
|
|
58379
|
+
if (filters.every((filter) => filter(path6, true)))
|
|
58380
|
+
paths.push(path6);
|
|
58319
58381
|
};
|
|
58320
58382
|
var empty$2 = () => {};
|
|
58321
58383
|
function build$6(root, options) {
|
|
@@ -58372,29 +58434,29 @@ var empty = () => {};
|
|
|
58372
58434
|
function build$3(options) {
|
|
58373
58435
|
return options.group ? groupFiles : empty;
|
|
58374
58436
|
}
|
|
58375
|
-
var resolveSymlinksAsync = function(
|
|
58376
|
-
const { queue, fs:
|
|
58437
|
+
var resolveSymlinksAsync = function(path6, state, callback$1) {
|
|
58438
|
+
const { queue, fs: fs7, options: { suppressErrors } } = state;
|
|
58377
58439
|
queue.enqueue();
|
|
58378
|
-
|
|
58440
|
+
fs7.realpath(path6, (error43, resolvedPath) => {
|
|
58379
58441
|
if (error43)
|
|
58380
58442
|
return queue.dequeue(suppressErrors ? null : error43, state);
|
|
58381
|
-
|
|
58443
|
+
fs7.stat(resolvedPath, (error$1, stat) => {
|
|
58382
58444
|
if (error$1)
|
|
58383
58445
|
return queue.dequeue(suppressErrors ? null : error$1, state);
|
|
58384
|
-
if (stat.isDirectory() && isRecursive(
|
|
58446
|
+
if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
|
|
58385
58447
|
return queue.dequeue(null, state);
|
|
58386
58448
|
callback$1(stat, resolvedPath);
|
|
58387
58449
|
queue.dequeue(null, state);
|
|
58388
58450
|
});
|
|
58389
58451
|
});
|
|
58390
58452
|
};
|
|
58391
|
-
var resolveSymlinks = function(
|
|
58392
|
-
const { queue, fs:
|
|
58453
|
+
var resolveSymlinks = function(path6, state, callback$1) {
|
|
58454
|
+
const { queue, fs: fs7, options: { suppressErrors } } = state;
|
|
58393
58455
|
queue.enqueue();
|
|
58394
58456
|
try {
|
|
58395
|
-
const resolvedPath =
|
|
58396
|
-
const stat =
|
|
58397
|
-
if (stat.isDirectory() && isRecursive(
|
|
58457
|
+
const resolvedPath = fs7.realpathSync(path6);
|
|
58458
|
+
const stat = fs7.statSync(resolvedPath);
|
|
58459
|
+
if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
|
|
58398
58460
|
return;
|
|
58399
58461
|
callback$1(stat, resolvedPath);
|
|
58400
58462
|
} catch (e) {
|
|
@@ -58407,10 +58469,10 @@ function build$2(options, isSynchronous) {
|
|
|
58407
58469
|
return null;
|
|
58408
58470
|
return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
|
|
58409
58471
|
}
|
|
58410
|
-
function isRecursive(
|
|
58472
|
+
function isRecursive(path6, resolved, state) {
|
|
58411
58473
|
if (state.options.useRealPaths)
|
|
58412
58474
|
return isRecursiveUsingRealPaths(resolved, state);
|
|
58413
|
-
let parent =
|
|
58475
|
+
let parent = dirname3(path6);
|
|
58414
58476
|
let depth = 1;
|
|
58415
58477
|
while (parent !== state.root && depth < 2) {
|
|
58416
58478
|
const resolvedPath = state.symlinks.get(parent);
|
|
@@ -58418,9 +58480,9 @@ function isRecursive(path5, resolved, state) {
|
|
|
58418
58480
|
if (isSameRoot)
|
|
58419
58481
|
depth++;
|
|
58420
58482
|
else
|
|
58421
|
-
parent =
|
|
58483
|
+
parent = dirname3(parent);
|
|
58422
58484
|
}
|
|
58423
|
-
state.symlinks.set(
|
|
58485
|
+
state.symlinks.set(path6, resolved);
|
|
58424
58486
|
return depth > 1;
|
|
58425
58487
|
}
|
|
58426
58488
|
function isRecursiveUsingRealPaths(resolved, state) {
|
|
@@ -58476,23 +58538,23 @@ var walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
|
|
|
58476
58538
|
state.queue.enqueue();
|
|
58477
58539
|
if (currentDepth < 0)
|
|
58478
58540
|
return state.queue.dequeue(null, state);
|
|
58479
|
-
const { fs:
|
|
58541
|
+
const { fs: fs7 } = state;
|
|
58480
58542
|
state.visited.push(crawlPath);
|
|
58481
58543
|
state.counts.directories++;
|
|
58482
|
-
|
|
58544
|
+
fs7.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
|
|
58483
58545
|
callback$1(entries, directoryPath, currentDepth);
|
|
58484
58546
|
state.queue.dequeue(state.options.suppressErrors ? null : error43, state);
|
|
58485
58547
|
});
|
|
58486
58548
|
};
|
|
58487
58549
|
var walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
|
|
58488
|
-
const { fs:
|
|
58550
|
+
const { fs: fs7 } = state;
|
|
58489
58551
|
if (currentDepth < 0)
|
|
58490
58552
|
return;
|
|
58491
58553
|
state.visited.push(crawlPath);
|
|
58492
58554
|
state.counts.directories++;
|
|
58493
58555
|
let entries = [];
|
|
58494
58556
|
try {
|
|
58495
|
-
entries =
|
|
58557
|
+
entries = fs7.readdirSync(crawlPath || ".", readdirOpts);
|
|
58496
58558
|
} catch (e) {
|
|
58497
58559
|
if (!state.options.suppressErrors)
|
|
58498
58560
|
throw e;
|
|
@@ -58598,23 +58660,23 @@ var Walker = class {
|
|
|
58598
58660
|
const filename = this.joinPath(entry.name, directoryPath);
|
|
58599
58661
|
this.pushFile(filename, files, this.state.counts, filters);
|
|
58600
58662
|
} else if (entry.isDirectory()) {
|
|
58601
|
-
let
|
|
58602
|
-
if (exclude && exclude(entry.name,
|
|
58663
|
+
let path6 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
|
|
58664
|
+
if (exclude && exclude(entry.name, path6))
|
|
58603
58665
|
continue;
|
|
58604
|
-
this.pushDirectory(
|
|
58605
|
-
this.walkDirectory(this.state,
|
|
58666
|
+
this.pushDirectory(path6, paths, filters);
|
|
58667
|
+
this.walkDirectory(this.state, path6, path6, depth - 1, this.walk);
|
|
58606
58668
|
} else if (this.resolveSymlink && entry.isSymbolicLink()) {
|
|
58607
|
-
let
|
|
58608
|
-
this.resolveSymlink(
|
|
58669
|
+
let path6 = joinPathWithBasePath(entry.name, directoryPath);
|
|
58670
|
+
this.resolveSymlink(path6, this.state, (stat, resolvedPath) => {
|
|
58609
58671
|
if (stat.isDirectory()) {
|
|
58610
58672
|
resolvedPath = normalizePath(resolvedPath, this.state.options);
|
|
58611
|
-
if (exclude && exclude(entry.name, useRealPaths ? resolvedPath :
|
|
58673
|
+
if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path6 + pathSeparator))
|
|
58612
58674
|
return;
|
|
58613
|
-
this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath :
|
|
58675
|
+
this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path6 + pathSeparator, depth - 1, this.walk);
|
|
58614
58676
|
} else {
|
|
58615
|
-
resolvedPath = useRealPaths ? resolvedPath :
|
|
58677
|
+
resolvedPath = useRealPaths ? resolvedPath : path6;
|
|
58616
58678
|
const filename = basename2(resolvedPath);
|
|
58617
|
-
const directoryPath$1 = normalizePath(
|
|
58679
|
+
const directoryPath$1 = normalizePath(dirname3(resolvedPath), this.state.options);
|
|
58618
58680
|
resolvedPath = this.joinPath(filename, directoryPath$1);
|
|
58619
58681
|
this.pushFile(resolvedPath, files, this.state.counts, filters);
|
|
58620
58682
|
}
|
|
@@ -58772,7 +58834,7 @@ var Builder = class {
|
|
|
58772
58834
|
isMatch = globFn(patterns, ...options);
|
|
58773
58835
|
this.globCache[patterns.join("\x00")] = isMatch;
|
|
58774
58836
|
}
|
|
58775
|
-
this.options.filters.push((
|
|
58837
|
+
this.options.filters.push((path6) => isMatch(path6));
|
|
58776
58838
|
return this;
|
|
58777
58839
|
}
|
|
58778
58840
|
};
|
|
@@ -58851,7 +58913,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
|
|
|
58851
58913
|
if (!result.endsWith("*") && expandDirectories)
|
|
58852
58914
|
result += "/**";
|
|
58853
58915
|
const escapedCwd = escapePath(cwd2);
|
|
58854
|
-
if (
|
|
58916
|
+
if (path6.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
|
|
58855
58917
|
result = posix.relative(escapedCwd, result);
|
|
58856
58918
|
else
|
|
58857
58919
|
result = posix.normalize(result);
|
|
@@ -58888,7 +58950,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
|
|
|
58888
58950
|
}
|
|
58889
58951
|
props.depthOffset = newCommonPath.length;
|
|
58890
58952
|
props.commonPath = newCommonPath;
|
|
58891
|
-
props.root = newCommonPath.length > 0 ?
|
|
58953
|
+
props.root = newCommonPath.length > 0 ? path6.posix.join(cwd2, ...newCommonPath) : cwd2;
|
|
58892
58954
|
}
|
|
58893
58955
|
return result;
|
|
58894
58956
|
}
|
|
@@ -59021,18 +59083,18 @@ function globSync(patternsOrOptions, options) {
|
|
|
59021
59083
|
...options,
|
|
59022
59084
|
patterns: patternsOrOptions
|
|
59023
59085
|
} : patternsOrOptions;
|
|
59024
|
-
const cwd2 = opts.cwd ?
|
|
59086
|
+
const cwd2 = opts.cwd ? path6.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
|
|
59025
59087
|
return crawl(opts, cwd2, true);
|
|
59026
59088
|
}
|
|
59027
59089
|
|
|
59028
59090
|
// src/utils/jsonl.ts
|
|
59029
59091
|
import { promisify } from "util";
|
|
59030
|
-
var readFile4 = promisify(
|
|
59031
|
-
var
|
|
59032
|
-
var statSync4 =
|
|
59092
|
+
var readFile4 = promisify(fs7.readFile);
|
|
59093
|
+
var readFileSync5 = fs7.readFileSync;
|
|
59094
|
+
var statSync4 = fs7.statSync;
|
|
59033
59095
|
async function getSessionDuration(transcriptPath) {
|
|
59034
59096
|
try {
|
|
59035
|
-
if (!
|
|
59097
|
+
if (!fs7.existsSync(transcriptPath)) {
|
|
59036
59098
|
return null;
|
|
59037
59099
|
}
|
|
59038
59100
|
const content = await readFile4(transcriptPath, "utf-8");
|
|
@@ -59084,7 +59146,7 @@ async function getSessionDuration(transcriptPath) {
|
|
|
59084
59146
|
}
|
|
59085
59147
|
async function getTokenMetrics(transcriptPath) {
|
|
59086
59148
|
try {
|
|
59087
|
-
if (!
|
|
59149
|
+
if (!fs7.existsSync(transcriptPath)) {
|
|
59088
59150
|
return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
|
|
59089
59151
|
}
|
|
59090
59152
|
const content = await readFile4(transcriptPath, "utf-8");
|
|
@@ -59137,7 +59199,7 @@ function getBlockMetrics() {
|
|
|
59137
59199
|
function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
|
|
59138
59200
|
const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
|
|
59139
59201
|
const now = new Date;
|
|
59140
|
-
const pattern =
|
|
59202
|
+
const pattern = path7.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
|
|
59141
59203
|
const files = globSync([pattern], {
|
|
59142
59204
|
absolute: true,
|
|
59143
59205
|
cwd: rootDir
|
|
@@ -59231,7 +59293,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
|
|
|
59231
59293
|
function getAllTimestampsFromFile(filePath) {
|
|
59232
59294
|
const timestamps = [];
|
|
59233
59295
|
try {
|
|
59234
|
-
const content =
|
|
59296
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
59235
59297
|
const lines = content.trim().split(`
|
|
59236
59298
|
`).filter((line) => line.length > 0);
|
|
59237
59299
|
for (const line of lines) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccstatusline-usage",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
4
4
|
"description": "A customizable status line formatter for Claude Code CLI",
|
|
5
5
|
"module": "src/ccstatusline.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
"ccstatusline": "dist/ccstatusline.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist/"
|
|
11
|
+
"dist/",
|
|
12
|
+
"scripts/usage.sh",
|
|
13
|
+
"scripts/context.sh",
|
|
14
|
+
"scripts/setup-enhanced.sh",
|
|
15
|
+
"defaults/"
|
|
12
16
|
],
|
|
13
17
|
"scripts": {
|
|
14
18
|
"start": "bun run src/ccstatusline.ts",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
CACHE_FILE="$HOME/.claude/.statusline/context.json"
|
|
4
|
+
|
|
5
|
+
make_bar() {
|
|
6
|
+
local pct="$1"
|
|
7
|
+
local width=15
|
|
8
|
+
local filled=$((pct * width / 100))
|
|
9
|
+
local empty=$((width - filled))
|
|
10
|
+
printf "["
|
|
11
|
+
printf "█%.0s" $(seq 1 "$filled")
|
|
12
|
+
printf "░%.0s" $(seq 1 "$empty")
|
|
13
|
+
printf "]"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
format_tokens() {
|
|
17
|
+
local tokens="$1"
|
|
18
|
+
if [[ $tokens -ge 1000 ]]; then
|
|
19
|
+
echo "$((tokens / 1000))k"
|
|
20
|
+
else
|
|
21
|
+
echo "$tokens"
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if [[ ! -f "$CACHE_FILE" ]]; then
|
|
26
|
+
BAR=$(make_bar 0)
|
|
27
|
+
echo "Context: $BAR 0k/200k (0%)"
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
INPUT=$(cat "$CACHE_FILE")
|
|
32
|
+
|
|
33
|
+
MAX_TOKENS=$(echo "$INPUT" | jq -r '.context_window_size // empty')
|
|
34
|
+
CURRENT_USAGE=$(echo "$INPUT" | jq -r '.current_usage // empty')
|
|
35
|
+
|
|
36
|
+
if [[ -z "$MAX_TOKENS" || "$MAX_TOKENS" == "null" || -z "$CURRENT_USAGE" || "$CURRENT_USAGE" == "null" ]]; then
|
|
37
|
+
BAR=$(make_bar 0)
|
|
38
|
+
echo "Context: $BAR 0k/200k (0%)"
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
INPUT_TOKENS=$(echo "$INPUT" | jq -r '.current_usage.input_tokens // 0')
|
|
43
|
+
CACHE_CREATE=$(echo "$INPUT" | jq -r '.current_usage.cache_creation_input_tokens // 0')
|
|
44
|
+
CACHE_READ=$(echo "$INPUT" | jq -r '.current_usage.cache_read_input_tokens // 0')
|
|
45
|
+
|
|
46
|
+
CURRENT_TOKENS=$((INPUT_TOKENS + CACHE_CREATE + CACHE_READ))
|
|
47
|
+
|
|
48
|
+
if [[ $MAX_TOKENS -gt 0 ]]; then
|
|
49
|
+
PERCENT=$((CURRENT_TOKENS * 100 / MAX_TOKENS))
|
|
50
|
+
else
|
|
51
|
+
PERCENT=0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
CURRENT_FMT=$(format_tokens "$CURRENT_TOKENS")
|
|
55
|
+
MAX_FMT=$(format_tokens "$MAX_TOKENS")
|
|
56
|
+
BAR=$(make_bar "$PERCENT")
|
|
57
|
+
|
|
58
|
+
echo "Context: $BAR ${CURRENT_FMT}/${MAX_FMT} (${PERCENT}%)"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Setup script for ccstatusline-usage enhanced configuration
|
|
3
|
+
# Installs usage scripts and applies enhanced settings
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$HOME/.local/share/ccstatusline"
|
|
8
|
+
CONFIG_DIR="$HOME/.config/ccstatusline"
|
|
9
|
+
SETTINGS_FILE="$CONFIG_DIR/settings.json"
|
|
10
|
+
|
|
11
|
+
# Find the package directory (where this script is located)
|
|
12
|
+
PKG_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
13
|
+
|
|
14
|
+
echo "Setting up ccstatusline-usage enhanced configuration..."
|
|
15
|
+
|
|
16
|
+
# Create directories
|
|
17
|
+
mkdir -p "$SCRIPT_DIR"
|
|
18
|
+
mkdir -p "$CONFIG_DIR"
|
|
19
|
+
|
|
20
|
+
# Copy scripts
|
|
21
|
+
if [[ -f "$PKG_DIR/scripts/usage.sh" ]]; then
|
|
22
|
+
cp "$PKG_DIR/scripts/usage.sh" "$SCRIPT_DIR/"
|
|
23
|
+
chmod +x "$SCRIPT_DIR/usage.sh"
|
|
24
|
+
echo "✓ Installed usage.sh to $SCRIPT_DIR/"
|
|
25
|
+
else
|
|
26
|
+
echo "✗ usage.sh not found in package"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [[ -f "$PKG_DIR/scripts/context.sh" ]]; then
|
|
31
|
+
cp "$PKG_DIR/scripts/context.sh" "$SCRIPT_DIR/"
|
|
32
|
+
chmod +x "$SCRIPT_DIR/context.sh"
|
|
33
|
+
echo "✓ Installed context.sh to $SCRIPT_DIR/"
|
|
34
|
+
else
|
|
35
|
+
echo "✗ context.sh not found in package"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Create enhanced settings with correct paths
|
|
40
|
+
cat > "$SETTINGS_FILE" << 'EOF'
|
|
41
|
+
{
|
|
42
|
+
"version": 3,
|
|
43
|
+
"lines": [
|
|
44
|
+
[
|
|
45
|
+
{
|
|
46
|
+
"id": "session-usage",
|
|
47
|
+
"type": "custom-command",
|
|
48
|
+
"color": "brightBlue",
|
|
49
|
+
"commandPath": "$HOME/.local/share/ccstatusline/usage.sh session",
|
|
50
|
+
"timeout": 5000
|
|
51
|
+
},
|
|
52
|
+
{"id": "sep1", "type": "separator"},
|
|
53
|
+
{
|
|
54
|
+
"id": "weekly-usage",
|
|
55
|
+
"type": "custom-command",
|
|
56
|
+
"color": "brightBlue",
|
|
57
|
+
"commandPath": "$HOME/.local/share/ccstatusline/usage.sh weekly",
|
|
58
|
+
"timeout": 5000
|
|
59
|
+
},
|
|
60
|
+
{"id": "sep2", "type": "separator"},
|
|
61
|
+
{
|
|
62
|
+
"id": "reset-timer",
|
|
63
|
+
"type": "custom-command",
|
|
64
|
+
"color": "brightBlue",
|
|
65
|
+
"commandPath": "$HOME/.local/share/ccstatusline/usage.sh reset",
|
|
66
|
+
"timeout": 5000
|
|
67
|
+
},
|
|
68
|
+
{"id": "sep3", "type": "separator"},
|
|
69
|
+
{"id": "model", "type": "model", "color": "magenta"},
|
|
70
|
+
{"id": "sep4", "type": "separator"},
|
|
71
|
+
{"id": "session-id", "type": "claude-session-id", "color": "cyan"}
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
{
|
|
75
|
+
"id": "context-usage",
|
|
76
|
+
"type": "custom-command",
|
|
77
|
+
"color": "blue",
|
|
78
|
+
"commandPath": "$HOME/.local/share/ccstatusline/context.sh",
|
|
79
|
+
"timeout": 5000
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
[]
|
|
83
|
+
],
|
|
84
|
+
"flexMode": "full-minus-40",
|
|
85
|
+
"compactThreshold": 60,
|
|
86
|
+
"colorLevel": 2
|
|
87
|
+
}
|
|
88
|
+
EOF
|
|
89
|
+
|
|
90
|
+
# Expand $HOME in the settings file
|
|
91
|
+
sed -i.bak "s|\$HOME|$HOME|g" "$SETTINGS_FILE" && rm -f "$SETTINGS_FILE.bak"
|
|
92
|
+
|
|
93
|
+
echo "✓ Created enhanced settings at $SETTINGS_FILE"
|
|
94
|
+
echo ""
|
|
95
|
+
echo "Setup complete! Run 'npx ccstatusline-usage' to configure."
|
|
96
|
+
echo ""
|
|
97
|
+
echo "Note: The usage widgets require Anthropic API access."
|
|
98
|
+
echo "Make sure you have valid credentials in ~/.claude/credentials.json"
|
package/scripts/usage.sh
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Cross-platform usage script for ccstatusline-usage
|
|
4
|
+
# Works on both macOS and Linux
|
|
5
|
+
|
|
6
|
+
CACHE_FILE="$HOME/.cache/ccstatusline-api.json"
|
|
7
|
+
LOCK_FILE="$HOME/.cache/ccstatusline-api.lock"
|
|
8
|
+
|
|
9
|
+
# Detect OS for platform-specific commands
|
|
10
|
+
is_macos() {
|
|
11
|
+
[[ "$(uname)" == "Darwin" ]]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# Get file modification time (seconds since epoch)
|
|
15
|
+
get_mtime() {
|
|
16
|
+
if is_macos; then
|
|
17
|
+
stat -f '%m' "$1" 2>/dev/null
|
|
18
|
+
else
|
|
19
|
+
stat -c '%Y' "$1" 2>/dev/null
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# Get OAuth token from credentials
|
|
24
|
+
get_token() {
|
|
25
|
+
if is_macos; then
|
|
26
|
+
# macOS: read from keychain
|
|
27
|
+
security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty'
|
|
28
|
+
else
|
|
29
|
+
# Linux: read from credentials file
|
|
30
|
+
jq -r '.claudeAiOauth.accessToken // empty' ~/.claude/.credentials.json 2>/dev/null
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Parse ISO date to epoch
|
|
35
|
+
parse_iso_date() {
|
|
36
|
+
local iso_date="$1"
|
|
37
|
+
# Remove fractional seconds and Z suffix
|
|
38
|
+
local clean_date="${iso_date%%.*}"
|
|
39
|
+
clean_date="${clean_date%%Z}"
|
|
40
|
+
|
|
41
|
+
if is_macos; then
|
|
42
|
+
TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$clean_date" "+%s" 2>/dev/null
|
|
43
|
+
else
|
|
44
|
+
# Linux: use date -d with ISO format
|
|
45
|
+
date -d "$clean_date" "+%s" 2>/dev/null
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fetch_api() {
|
|
50
|
+
local NOW=$(date +%s)
|
|
51
|
+
|
|
52
|
+
# Use cache if < 180 seconds old
|
|
53
|
+
if [[ -f "$CACHE_FILE" ]]; then
|
|
54
|
+
local MTIME=$(get_mtime "$CACHE_FILE")
|
|
55
|
+
if [[ -n "$MTIME" ]]; then
|
|
56
|
+
local CACHE_AGE=$((NOW - MTIME))
|
|
57
|
+
[[ $CACHE_AGE -lt 180 ]] && return 0
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Rate limit: only try API once per 30 seconds
|
|
62
|
+
if [[ -f "$LOCK_FILE" ]]; then
|
|
63
|
+
local LOCK_MTIME=$(get_mtime "$LOCK_FILE")
|
|
64
|
+
if [[ -n "$LOCK_MTIME" ]]; then
|
|
65
|
+
local LOCK_AGE=$((NOW - LOCK_MTIME))
|
|
66
|
+
if [[ $LOCK_AGE -lt 30 ]]; then
|
|
67
|
+
[[ -f "$CACHE_FILE" ]] && return 0
|
|
68
|
+
return 1
|
|
69
|
+
fi
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
touch "$LOCK_FILE"
|
|
74
|
+
|
|
75
|
+
TOKEN=$(get_token)
|
|
76
|
+
[[ -z "$TOKEN" ]] && return 1
|
|
77
|
+
|
|
78
|
+
RESPONSE=$(curl -s --max-time 5 "https://api.anthropic.com/api/oauth/usage" \
|
|
79
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
80
|
+
-H "anthropic-beta: oauth-2025-04-20" 2>/dev/null)
|
|
81
|
+
[[ -z "$RESPONSE" ]] && return 1
|
|
82
|
+
|
|
83
|
+
# Ensure cache directory exists
|
|
84
|
+
mkdir -p "$(dirname "$CACHE_FILE")"
|
|
85
|
+
echo "$RESPONSE" > "$CACHE_FILE"
|
|
86
|
+
return 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
make_bar() {
|
|
90
|
+
local pct="$1"
|
|
91
|
+
local width=15
|
|
92
|
+
local filled=$((pct * width / 100))
|
|
93
|
+
local empty=$((width - filled))
|
|
94
|
+
printf "["
|
|
95
|
+
printf "█%.0s" $(seq 1 "$filled")
|
|
96
|
+
printf "░%.0s" $(seq 1 "$empty")
|
|
97
|
+
printf "]"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
MODE="${1:-all}"
|
|
101
|
+
|
|
102
|
+
fetch_api || { echo "[API Error]"; exit 1; }
|
|
103
|
+
|
|
104
|
+
case "$MODE" in
|
|
105
|
+
session)
|
|
106
|
+
SESSION=$(jq -r '.five_hour.utilization // empty' "$CACHE_FILE" 2>/dev/null)
|
|
107
|
+
[[ -z "$SESSION" ]] && { echo "[Parse Error]"; exit 1; }
|
|
108
|
+
SESSION_INT=${SESSION%.*}
|
|
109
|
+
SESSION_BAR=$(make_bar "$SESSION_INT")
|
|
110
|
+
echo "Session: $SESSION_BAR ${SESSION}%"
|
|
111
|
+
;;
|
|
112
|
+
weekly)
|
|
113
|
+
WEEKLY=$(jq -r '.seven_day.utilization // empty' "$CACHE_FILE" 2>/dev/null)
|
|
114
|
+
[[ -z "$WEEKLY" ]] && { echo "[Parse Error]"; exit 1; }
|
|
115
|
+
WEEKLY_INT=${WEEKLY%.*}
|
|
116
|
+
WEEKLY_BAR=$(make_bar "$WEEKLY_INT")
|
|
117
|
+
echo "Weekly: $WEEKLY_BAR ${WEEKLY}%"
|
|
118
|
+
;;
|
|
119
|
+
reset)
|
|
120
|
+
RESETS_AT=$(jq -r '.five_hour.resets_at // empty' "$CACHE_FILE" 2>/dev/null)
|
|
121
|
+
[[ -z "$RESETS_AT" ]] && { echo "[Parse Error]"; exit 1; }
|
|
122
|
+
RESET_EPOCH=$(parse_iso_date "$RESETS_AT")
|
|
123
|
+
NOW_EPOCH=$(date -u +%s)
|
|
124
|
+
if [[ -z "$RESET_EPOCH" ]]; then
|
|
125
|
+
echo "[Date Error]"
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
DIFF=$((RESET_EPOCH - NOW_EPOCH))
|
|
129
|
+
if [[ $DIFF -le 0 ]]; then
|
|
130
|
+
echo "Reset now"
|
|
131
|
+
else
|
|
132
|
+
HOURS=$((DIFF / 3600))
|
|
133
|
+
MINUTES=$(((DIFF % 3600) / 60))
|
|
134
|
+
echo "${HOURS}:$(printf '%02d' $MINUTES) hr"
|
|
135
|
+
fi
|
|
136
|
+
;;
|
|
137
|
+
*)
|
|
138
|
+
SESSION=$(jq -r '.five_hour.utilization // empty' "$CACHE_FILE" 2>/dev/null)
|
|
139
|
+
WEEKLY=$(jq -r '.seven_day.utilization // empty' "$CACHE_FILE" 2>/dev/null)
|
|
140
|
+
[[ -z "$SESSION" || -z "$WEEKLY" ]] && { echo "[Parse Error]"; exit 1; }
|
|
141
|
+
SESSION_INT=${SESSION%.*}
|
|
142
|
+
WEEKLY_INT=${WEEKLY%.*}
|
|
143
|
+
SESSION_BAR=$(make_bar "$SESSION_INT")
|
|
144
|
+
WEEKLY_BAR=$(make_bar "$WEEKLY_INT")
|
|
145
|
+
echo "Session: $SESSION_BAR ${SESSION}% | Weekly: $WEEKLY_BAR ${WEEKLY}%"
|
|
146
|
+
;;
|
|
147
|
+
esac
|