claudeline 1.8.0 → 1.9.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/dist/index.js +195 -300
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -262,7 +262,7 @@ function listComponents() {
|
|
|
262
262
|
{
|
|
263
263
|
name: "Account",
|
|
264
264
|
prefix: "account",
|
|
265
|
-
items: ["email"
|
|
265
|
+
items: ["email"]
|
|
266
266
|
},
|
|
267
267
|
{
|
|
268
268
|
name: "Usage/Limits",
|
|
@@ -439,7 +439,7 @@ var THEMES = {
|
|
|
439
439
|
nerd: "nerd:nf-dev-nodejs_small env:node-short sep:dot nerd:nf-cod-folder fs:dir sep:dot nerd:nf-dev-git_branch git:branch git:status",
|
|
440
440
|
compact: "claude:model sep:slash fs:dir sep:slash git:branch",
|
|
441
441
|
colorful: "bold:magenta:claude:model sep:arrow cyan:nerd:nf-cod-folder cyan:fs:dir sep:arrow green:nerd:nf-dev-git_branch green:git:branch yellow:git:status sep:arrow blue:nerd:nf-fa-gauge blue:ctx:percent",
|
|
442
|
-
luca: "bold:magenta:claude:model if:effort(dim:sep:
|
|
442
|
+
luca: "bold:magenta:claude:model-letter if:effort(dim:sep:dot claude:effort-icon claude:effort) dim:sep:middot cyan:nerd:nf-md-source_branch cyan:git:repo sep:none text:: sep:none green:git:branch if:subdir(sep:none white:text:/ sep:none white:fs:relative) if:dirty(dim:sep:middot git:dirty) dim:sep:middot white:account:email sep:newline dim:nerd:nf-md-clock_fast usage:5h-bar:10 usage:5h usage:5h-pace dim:usage:5h-reset dim:sep:middot dim:nerd:nf-md-calendar_week usage:week-bar:10 usage:week usage:week-pace dim:usage:week-reset dim:sep:middot white:cost:total"
|
|
443
443
|
};
|
|
444
444
|
function getTheme(name) {
|
|
445
445
|
return THEMES[name] || null;
|
|
@@ -636,193 +636,9 @@ function getSeparator(name) {
|
|
|
636
636
|
}
|
|
637
637
|
|
|
638
638
|
// src/usage.ts
|
|
639
|
-
|
|
640
|
-
import * as path2 from "path";
|
|
641
|
-
import * as os2 from "os";
|
|
642
|
-
import * as crypto from "crypto";
|
|
643
|
-
import { execSync } from "child_process";
|
|
644
|
-
var CLUSAGE_API_FILE = path2.join(os2.homedir(), ".claude", "clusage-api.json");
|
|
645
|
-
var CLUSAGE_STALE_MS = 10 * 60 * 1e3;
|
|
646
|
-
var _clusageCache = null;
|
|
647
|
-
function readClusageAPI() {
|
|
648
|
-
try {
|
|
649
|
-
const raw = fs2.readFileSync(CLUSAGE_API_FILE, "utf8");
|
|
650
|
-
const payload = JSON.parse(raw);
|
|
651
|
-
if (!payload.updatedAt || !payload.accounts?.length) return null;
|
|
652
|
-
const age = Date.now() - new Date(payload.updatedAt).getTime();
|
|
653
|
-
if (age > CLUSAGE_STALE_MS) return null;
|
|
654
|
-
_clusageCache = payload;
|
|
655
|
-
return payload;
|
|
656
|
-
} catch {
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
function getClusageAccount() {
|
|
661
|
-
const payload = _clusageCache ?? readClusageAPI();
|
|
662
|
-
if (!payload || payload.accounts.length === 0) return null;
|
|
663
|
-
return payload.accounts[0];
|
|
664
|
-
}
|
|
665
|
-
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
666
|
-
var PROFILE_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
667
|
-
var ANTHROPIC_BETA_HEADER = "oauth-2025-04-20";
|
|
668
|
-
function tokenHash(token) {
|
|
669
|
-
return crypto.createHash("sha256").update(token).digest("hex").slice(0, 12);
|
|
670
|
-
}
|
|
671
|
-
function getCacheFile(token, type) {
|
|
672
|
-
const dir = path2.join(getClaudeConfigDir(), "cache");
|
|
673
|
-
try {
|
|
674
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
675
|
-
} catch {
|
|
676
|
-
}
|
|
677
|
-
return path2.join(dir, `claudeline-${type}-${tokenHash(token)}.json`);
|
|
678
|
-
}
|
|
679
|
-
function readCache(cacheFile, ttlMs) {
|
|
680
|
-
try {
|
|
681
|
-
const raw = fs2.readFileSync(cacheFile, "utf8");
|
|
682
|
-
const cached = JSON.parse(raw);
|
|
683
|
-
if (Date.now() - cached.fetched_at < ttlMs) {
|
|
684
|
-
return cached.data;
|
|
685
|
-
}
|
|
686
|
-
} catch {
|
|
687
|
-
}
|
|
688
|
-
return null;
|
|
689
|
-
}
|
|
690
|
-
function writeCache(cacheFile, data) {
|
|
691
|
-
try {
|
|
692
|
-
fs2.writeFileSync(cacheFile, JSON.stringify({ data, fetched_at: Date.now() }));
|
|
693
|
-
} catch {
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
function extractToken(raw) {
|
|
697
|
-
try {
|
|
698
|
-
const parsed = JSON.parse(raw);
|
|
699
|
-
if (typeof parsed === "string") return parsed;
|
|
700
|
-
if (parsed.claudeAiOauth?.accessToken) return parsed.claudeAiOauth.accessToken;
|
|
701
|
-
if (parsed.accessToken) return parsed.accessToken;
|
|
702
|
-
if (parsed.access_token) return parsed.access_token;
|
|
703
|
-
} catch {
|
|
704
|
-
}
|
|
705
|
-
return null;
|
|
706
|
-
}
|
|
707
|
-
function getKeychainService() {
|
|
708
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
709
|
-
if (configDir) {
|
|
710
|
-
const suffix = crypto.createHash("sha256").update(configDir).digest("hex").slice(0, 8);
|
|
711
|
-
return `Claude Code-credentials-${suffix}`;
|
|
712
|
-
}
|
|
713
|
-
return "Claude Code-credentials";
|
|
714
|
-
}
|
|
715
|
-
function getOAuthTokenMacOS() {
|
|
716
|
-
try {
|
|
717
|
-
const service = getKeychainService();
|
|
718
|
-
const raw = execSync(
|
|
719
|
-
`security find-generic-password -s "${service}" -w 2>/dev/null`,
|
|
720
|
-
{ encoding: "utf8" }
|
|
721
|
-
).trim();
|
|
722
|
-
return extractToken(raw);
|
|
723
|
-
} catch {
|
|
724
|
-
return null;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
function getOAuthTokenLinux() {
|
|
728
|
-
try {
|
|
729
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR || path2.join(os2.homedir(), ".claude");
|
|
730
|
-
const credPath = path2.join(configDir, ".credentials.json");
|
|
731
|
-
const raw = fs2.readFileSync(credPath, "utf8").trim();
|
|
732
|
-
return extractToken(raw);
|
|
733
|
-
} catch {
|
|
734
|
-
return null;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
function getOAuthToken() {
|
|
738
|
-
switch (process.platform) {
|
|
739
|
-
case "darwin":
|
|
740
|
-
return getOAuthTokenMacOS();
|
|
741
|
-
case "linux":
|
|
742
|
-
return getOAuthTokenLinux();
|
|
743
|
-
default:
|
|
744
|
-
return null;
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
function apiGet(token, urlPath) {
|
|
748
|
-
try {
|
|
749
|
-
return execSync(
|
|
750
|
-
`curl -s --max-time 5 -H "Content-Type: application/json" -H "User-Agent: claude-code/2.1.69" -H "Authorization: Bearer $__CLAUDE_TOKEN" -H "anthropic-beta: ${ANTHROPIC_BETA_HEADER}" "https://api.anthropic.com${urlPath}"`,
|
|
751
|
-
{ encoding: "utf8", env: { ...process.env, __CLAUDE_TOKEN: token } }
|
|
752
|
-
).trim();
|
|
753
|
-
} catch {
|
|
754
|
-
return null;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
function fetchUsageDirect(token) {
|
|
758
|
-
try {
|
|
759
|
-
const result = apiGet(token, "/api/oauth/usage");
|
|
760
|
-
if (!result) return null;
|
|
761
|
-
const data = JSON.parse(result);
|
|
762
|
-
if (data.five_hour && data.seven_day) return data;
|
|
763
|
-
return null;
|
|
764
|
-
} catch {
|
|
765
|
-
return null;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
function fetchProfileDirect(token) {
|
|
769
|
-
try {
|
|
770
|
-
const result = apiGet(token, "/api/oauth/profile");
|
|
771
|
-
if (!result) return null;
|
|
772
|
-
const data = JSON.parse(result);
|
|
773
|
-
if (data.account?.email) return { email: data.account.email };
|
|
774
|
-
return null;
|
|
775
|
-
} catch {
|
|
776
|
-
return null;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
function getUsageDataDirect() {
|
|
780
|
-
const token = getOAuthToken();
|
|
781
|
-
if (!token) return null;
|
|
782
|
-
const cacheFile = getCacheFile(token, "usage");
|
|
783
|
-
const cached = readCache(cacheFile, CACHE_TTL_MS);
|
|
784
|
-
if (cached) return cached;
|
|
785
|
-
const data = fetchUsageDirect(token);
|
|
786
|
-
if (data) writeCache(cacheFile, data);
|
|
787
|
-
return data;
|
|
788
|
-
}
|
|
789
|
-
function getProfileDataDirect() {
|
|
790
|
-
const token = getOAuthToken();
|
|
791
|
-
if (!token) return null;
|
|
792
|
-
const cacheFile = getCacheFile(token, "profile");
|
|
793
|
-
const cached = readCache(cacheFile, PROFILE_CACHE_TTL_MS);
|
|
794
|
-
if (cached) return cached;
|
|
795
|
-
const data = fetchProfileDirect(token);
|
|
796
|
-
if (data) writeCache(cacheFile, data);
|
|
797
|
-
return data;
|
|
798
|
-
}
|
|
799
|
-
function getUsageData() {
|
|
800
|
-
const account = getClusageAccount();
|
|
801
|
-
if (account) {
|
|
802
|
-
return {
|
|
803
|
-
five_hour: {
|
|
804
|
-
utilization: account.fiveHour.utilization,
|
|
805
|
-
resets_at: account.fiveHour.resetsAt
|
|
806
|
-
},
|
|
807
|
-
seven_day: {
|
|
808
|
-
utilization: account.sevenDay.utilization,
|
|
809
|
-
resets_at: account.sevenDay.resetsAt
|
|
810
|
-
}
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
return getUsageDataDirect();
|
|
814
|
-
}
|
|
815
|
-
function getProfileData() {
|
|
816
|
-
const account = getClusageAccount();
|
|
817
|
-
if (account?.profile) {
|
|
818
|
-
return { email: account.profile.email };
|
|
819
|
-
}
|
|
820
|
-
return getProfileDataDirect();
|
|
821
|
-
}
|
|
822
|
-
function formatTimeUntil(isoDate) {
|
|
823
|
-
const reset = new Date(isoDate).getTime();
|
|
639
|
+
function formatTimeUntil(resetUnix) {
|
|
824
640
|
const now = Date.now();
|
|
825
|
-
let diff = Math.max(0,
|
|
641
|
+
let diff = Math.max(0, resetUnix * 1e3 - now);
|
|
826
642
|
if (diff === 0) return "now";
|
|
827
643
|
const days = Math.floor(diff / 864e5);
|
|
828
644
|
diff %= 864e5;
|
|
@@ -835,12 +651,12 @@ function formatTimeUntil(isoDate) {
|
|
|
835
651
|
}
|
|
836
652
|
var FIVE_HOUR_MS = 5 * 60 * 60 * 1e3;
|
|
837
653
|
var SEVEN_DAY_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
838
|
-
function calculatePace(
|
|
839
|
-
const resetMs =
|
|
654
|
+
function calculatePace(usedPct, resetUnix, windowDurationMs) {
|
|
655
|
+
const resetMs = resetUnix * 1e3;
|
|
840
656
|
const remainingMs = Math.max(0, resetMs - Date.now());
|
|
841
657
|
const elapsedFraction = 1 - remainingMs / windowDurationMs;
|
|
842
658
|
const target = elapsedFraction * 100;
|
|
843
|
-
const delta =
|
|
659
|
+
const delta = usedPct - target;
|
|
844
660
|
return { target, delta };
|
|
845
661
|
}
|
|
846
662
|
function paceColor(delta) {
|
|
@@ -848,60 +664,67 @@ function paceColor(delta) {
|
|
|
848
664
|
if (delta < -10) return "\x1B[0;36m";
|
|
849
665
|
return "\x1B[0;32m";
|
|
850
666
|
}
|
|
851
|
-
function formatPaceIcon(delta,
|
|
667
|
+
function formatPaceIcon(delta, noColor2) {
|
|
852
668
|
const icon = delta > 10 ? "\uF0E7" : delta < -10 ? "\uF017" : "\uF00C";
|
|
853
|
-
if (
|
|
669
|
+
if (noColor2) return icon;
|
|
854
670
|
return `${paceColor(delta)}${icon}\x1B[0m`;
|
|
855
671
|
}
|
|
856
|
-
function formatPaceDelta(delta,
|
|
672
|
+
function formatPaceDelta(delta, noColor2) {
|
|
857
673
|
const rounded = Math.round(delta);
|
|
858
674
|
let text;
|
|
859
675
|
if (rounded > 0) text = "+" + rounded + "%";
|
|
860
676
|
else if (rounded < 0) text = rounded + "%";
|
|
861
677
|
else text = "\xB10%";
|
|
862
|
-
if (
|
|
678
|
+
if (noColor2) return text;
|
|
863
679
|
return `${paceColor(delta)}${text}\x1B[0m`;
|
|
864
680
|
}
|
|
865
|
-
function
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
681
|
+
function barColor(pct) {
|
|
682
|
+
if (pct < 50) return "\x1B[0;32m";
|
|
683
|
+
if (pct < 75) return "\x1B[0;33m";
|
|
684
|
+
if (pct < 90) return "\x1B[0;31m";
|
|
685
|
+
return "\x1B[0;1;31m";
|
|
869
686
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
687
|
+
var BLOCKS = ["\u2591", "\u258E", "\u258C", "\u258A", "\u2588"];
|
|
688
|
+
function makeBar(pct, width, noColor2, label) {
|
|
689
|
+
const clamped = Math.max(0, Math.min(100, pct));
|
|
690
|
+
const fillExact = clamped / 100 * width;
|
|
691
|
+
const fullCells = Math.floor(fillExact);
|
|
692
|
+
const fractional = fillExact - fullCells;
|
|
693
|
+
const partialIdx = Math.round(fractional * 4);
|
|
694
|
+
const partialChar = BLOCKS[partialIdx];
|
|
695
|
+
const emptyCells = width - fullCells - 1;
|
|
696
|
+
const filled = "\u2588".repeat(fullCells) + partialChar + "\u2591".repeat(Math.max(0, emptyCells));
|
|
697
|
+
const color = barColor(clamped);
|
|
698
|
+
const bar = noColor2 ? filled : `${color}${filled}\x1B[0m`;
|
|
699
|
+
return label ? label + bar : bar;
|
|
879
700
|
}
|
|
880
|
-
function evaluateUsageComponent(key, args,
|
|
881
|
-
const
|
|
882
|
-
if (!
|
|
701
|
+
function evaluateUsageComponent(key, data, args, noColor2 = false) {
|
|
702
|
+
const limits = data.rate_limits;
|
|
703
|
+
if (!limits) return "";
|
|
704
|
+
const fiveHour = limits.five_hour;
|
|
705
|
+
const sevenDay = limits.seven_day;
|
|
883
706
|
switch (key) {
|
|
884
707
|
case "5h":
|
|
885
|
-
return Math.round(
|
|
708
|
+
return Math.round(fiveHour.used_percentage) + "%";
|
|
886
709
|
case "week":
|
|
887
710
|
case "7d":
|
|
888
|
-
return Math.round(
|
|
711
|
+
return Math.round(sevenDay.used_percentage) + "%";
|
|
889
712
|
case "5h-reset":
|
|
890
|
-
return formatTimeUntil(
|
|
713
|
+
return formatTimeUntil(fiveHour.resets_at);
|
|
891
714
|
case "week-reset":
|
|
892
715
|
case "7d-reset":
|
|
893
|
-
return formatTimeUntil(
|
|
716
|
+
return formatTimeUntil(sevenDay.resets_at);
|
|
894
717
|
case "5h-bar": {
|
|
895
718
|
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
896
|
-
return makeBar(
|
|
719
|
+
return makeBar(fiveHour.used_percentage, width, noColor2);
|
|
897
720
|
}
|
|
898
721
|
case "week-bar":
|
|
899
722
|
case "7d-bar": {
|
|
900
723
|
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
901
|
-
return makeBar(
|
|
724
|
+
return makeBar(sevenDay.used_percentage, width, noColor2);
|
|
902
725
|
}
|
|
903
726
|
case "5h-icon": {
|
|
904
|
-
const pct =
|
|
727
|
+
const pct = fiveHour.used_percentage;
|
|
905
728
|
const icon = "\uF111";
|
|
906
729
|
if (pct < 50) return `\x1B[0;32m${icon}\x1B[0m`;
|
|
907
730
|
if (pct < 75) return `\x1B[0;33m${icon}\x1B[0m`;
|
|
@@ -910,7 +733,7 @@ function evaluateUsageComponent(key, args, noColor = false) {
|
|
|
910
733
|
}
|
|
911
734
|
case "week-icon":
|
|
912
735
|
case "7d-icon": {
|
|
913
|
-
const pct =
|
|
736
|
+
const pct = sevenDay.used_percentage;
|
|
914
737
|
const icon = "\uF111";
|
|
915
738
|
if (pct < 50) return `\x1B[0;32m${icon}\x1B[0m`;
|
|
916
739
|
if (pct < 75) return `\x1B[0;33m${icon}\x1B[0m`;
|
|
@@ -918,67 +741,128 @@ function evaluateUsageComponent(key, args, noColor = false) {
|
|
|
918
741
|
return `\x1B[0;1;31m${icon}\x1B[0m`;
|
|
919
742
|
}
|
|
920
743
|
case "5h-target": {
|
|
921
|
-
const { target } = calculatePace(
|
|
744
|
+
const { target } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
922
745
|
return Math.round(target) + "%";
|
|
923
746
|
}
|
|
924
747
|
case "week-target":
|
|
925
748
|
case "7d-target": {
|
|
926
|
-
const { target } = calculatePace(
|
|
749
|
+
const { target } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
927
750
|
return Math.round(target) + "%";
|
|
928
751
|
}
|
|
929
752
|
case "5h-pace": {
|
|
930
|
-
const { delta } = calculatePace(
|
|
931
|
-
return formatPaceDelta(delta,
|
|
753
|
+
const { delta } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
754
|
+
return formatPaceDelta(delta, noColor2);
|
|
932
755
|
}
|
|
933
756
|
case "week-pace":
|
|
934
757
|
case "7d-pace": {
|
|
935
|
-
const { delta } = calculatePace(
|
|
936
|
-
return formatPaceDelta(delta,
|
|
758
|
+
const { delta } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
759
|
+
return formatPaceDelta(delta, noColor2);
|
|
937
760
|
}
|
|
938
761
|
case "5h-pace-icon": {
|
|
939
|
-
const { delta } = calculatePace(
|
|
940
|
-
return formatPaceIcon(delta,
|
|
762
|
+
const { delta } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
763
|
+
return formatPaceIcon(delta, noColor2);
|
|
941
764
|
}
|
|
942
765
|
case "week-pace-icon":
|
|
943
766
|
case "7d-pace-icon": {
|
|
944
|
-
const { delta } = calculatePace(
|
|
945
|
-
return formatPaceIcon(delta,
|
|
946
|
-
}
|
|
947
|
-
// Clusage-only momentum/projection data (no direct API fallback)
|
|
948
|
-
case "velocity": {
|
|
949
|
-
const m = getClusageAccount()?.momentum;
|
|
950
|
-
return m ? m.velocity.toFixed(1) + " pp/hr" : "";
|
|
767
|
+
const { delta } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
768
|
+
return formatPaceIcon(delta, noColor2);
|
|
951
769
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
770
|
+
default:
|
|
771
|
+
return "";
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/account.ts
|
|
776
|
+
import * as fs2 from "fs";
|
|
777
|
+
import * as path2 from "path";
|
|
778
|
+
import * as os2 from "os";
|
|
779
|
+
import * as crypto from "crypto";
|
|
780
|
+
import { execSync } from "child_process";
|
|
781
|
+
var PROFILE_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
782
|
+
function extractToken(raw) {
|
|
783
|
+
try {
|
|
784
|
+
const parsed = JSON.parse(raw);
|
|
785
|
+
if (typeof parsed === "string") return parsed;
|
|
786
|
+
if (parsed.claudeAiOauth?.accessToken) return parsed.claudeAiOauth.accessToken;
|
|
787
|
+
if (parsed.accessToken) return parsed.accessToken;
|
|
788
|
+
if (parsed.access_token) return parsed.access_token;
|
|
789
|
+
} catch {
|
|
790
|
+
}
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
function getKeychainService() {
|
|
794
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
795
|
+
if (configDir) {
|
|
796
|
+
const suffix = crypto.createHash("sha256").update(configDir).digest("hex").slice(0, 8);
|
|
797
|
+
return `Claude Code-credentials-${suffix}`;
|
|
798
|
+
}
|
|
799
|
+
return "Claude Code-credentials";
|
|
800
|
+
}
|
|
801
|
+
function getOAuthToken() {
|
|
802
|
+
try {
|
|
803
|
+
if (process.platform === "darwin") {
|
|
804
|
+
const service = getKeychainService();
|
|
805
|
+
const raw = execSync(
|
|
806
|
+
`security find-generic-password -s "${service}" -w 2>/dev/null`,
|
|
807
|
+
{ encoding: "utf8" }
|
|
808
|
+
).trim();
|
|
809
|
+
return extractToken(raw);
|
|
966
810
|
}
|
|
967
|
-
|
|
968
|
-
const
|
|
969
|
-
|
|
811
|
+
if (process.platform === "linux") {
|
|
812
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path2.join(os2.homedir(), ".claude");
|
|
813
|
+
const raw = fs2.readFileSync(path2.join(configDir, ".credentials.json"), "utf8").trim();
|
|
814
|
+
return extractToken(raw);
|
|
970
815
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
816
|
+
} catch {
|
|
817
|
+
}
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
function getCacheFile(token) {
|
|
821
|
+
const hash = crypto.createHash("sha256").update(token).digest("hex").slice(0, 12);
|
|
822
|
+
const dir = path2.join(getClaudeConfigDir(), "cache");
|
|
823
|
+
try {
|
|
824
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
825
|
+
} catch {
|
|
826
|
+
}
|
|
827
|
+
return path2.join(dir, `claudeline-profile-${hash}.json`);
|
|
828
|
+
}
|
|
829
|
+
function readCachedProfile(cacheFile) {
|
|
830
|
+
try {
|
|
831
|
+
const cached = JSON.parse(fs2.readFileSync(cacheFile, "utf8"));
|
|
832
|
+
if (Date.now() - cached.fetched_at < PROFILE_CACHE_TTL_MS) {
|
|
833
|
+
return cached.email;
|
|
974
834
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
835
|
+
} catch {
|
|
836
|
+
}
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
function fetchEmail(token) {
|
|
840
|
+
try {
|
|
841
|
+
const result = execSync(
|
|
842
|
+
`curl -s --max-time 5 -H "Content-Type: application/json" -H "User-Agent: claude-code/2.1.80" -H "Authorization: Bearer $__CLAUDE_TOKEN" -H "anthropic-beta: oauth-2025-04-20" "https://api.anthropic.com/api/oauth/profile"`,
|
|
843
|
+
{ encoding: "utf8", env: { ...process.env, __CLAUDE_TOKEN: token } }
|
|
844
|
+
).trim();
|
|
845
|
+
const data = JSON.parse(result);
|
|
846
|
+
return data.account?.email ?? null;
|
|
847
|
+
} catch {
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
function evaluateAccountComponent(key) {
|
|
852
|
+
if (key !== "email") return "";
|
|
853
|
+
const token = getOAuthToken();
|
|
854
|
+
if (!token) return "";
|
|
855
|
+
const cacheFile = getCacheFile(token);
|
|
856
|
+
const cached = readCachedProfile(cacheFile);
|
|
857
|
+
if (cached) return cached;
|
|
858
|
+
const email = fetchEmail(token);
|
|
859
|
+
if (email) {
|
|
860
|
+
try {
|
|
861
|
+
fs2.writeFileSync(cacheFile, JSON.stringify({ email, fetched_at: Date.now() }));
|
|
862
|
+
} catch {
|
|
978
863
|
}
|
|
979
|
-
default:
|
|
980
|
-
return "";
|
|
981
864
|
}
|
|
865
|
+
return email ?? "";
|
|
982
866
|
}
|
|
983
867
|
|
|
984
868
|
// src/nerdfonts.ts
|
|
@@ -1131,53 +1015,44 @@ function execCommand(cmd) {
|
|
|
1131
1015
|
return "";
|
|
1132
1016
|
}
|
|
1133
1017
|
}
|
|
1134
|
-
function applyStyles(text, styles,
|
|
1135
|
-
if (
|
|
1018
|
+
function applyStyles(text, styles, noColor2) {
|
|
1019
|
+
if (noColor2 || styles.length === 0 || !text) return text;
|
|
1136
1020
|
const codes = styles.map((s) => COLORS[s]).filter(Boolean);
|
|
1137
1021
|
if (codes.length === 0) return text;
|
|
1138
1022
|
return `\x1B[0;${codes.join(";")}m${text}\x1B[${RESET}m`;
|
|
1139
1023
|
}
|
|
1140
|
-
var
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
return cached.data;
|
|
1146
|
-
}
|
|
1024
|
+
var VALID_EFFORTS = ["low", "medium", "high"];
|
|
1025
|
+
function isValidEffort(value) {
|
|
1026
|
+
return typeof value === "string" && VALID_EFFORTS.includes(value);
|
|
1027
|
+
}
|
|
1028
|
+
function readEffortFromSettings(filePath) {
|
|
1147
1029
|
try {
|
|
1148
1030
|
const data = JSON.parse(fs3.readFileSync(filePath, "utf8"));
|
|
1149
|
-
|
|
1150
|
-
return data;
|
|
1031
|
+
return isValidEffort(data.effortLevel) ? data.effortLevel : null;
|
|
1151
1032
|
} catch {
|
|
1152
|
-
return
|
|
1033
|
+
return null;
|
|
1153
1034
|
}
|
|
1154
1035
|
}
|
|
1155
|
-
var VALID_EFFORTS = ["low", "medium", "high"];
|
|
1156
1036
|
function resolveEffort(data) {
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
return stdinEffort;
|
|
1160
|
-
}
|
|
1161
|
-
let effort = "high";
|
|
1162
|
-
const globalPath = path3.join(os3.homedir(), ".claude", "settings.json");
|
|
1163
|
-
const globalSettings = readSettingsFile(globalPath);
|
|
1164
|
-
if (typeof globalSettings.effortLevel === "string" && VALID_EFFORTS.includes(globalSettings.effortLevel)) {
|
|
1165
|
-
effort = globalSettings.effortLevel;
|
|
1037
|
+
if (isValidEffort(data.model?.reasoning_effort)) {
|
|
1038
|
+
return data.model.reasoning_effort;
|
|
1166
1039
|
}
|
|
1040
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path3.join(os3.homedir(), ".claude");
|
|
1041
|
+
let effort = readEffortFromSettings(path3.join(configDir, "settings.json"));
|
|
1167
1042
|
const projectDir = data.workspace?.project_dir;
|
|
1168
1043
|
if (projectDir) {
|
|
1169
|
-
|
|
1170
|
-
const projectSettings = readSettingsFile(projectPath);
|
|
1171
|
-
if (typeof projectSettings.effortLevel === "string" && VALID_EFFORTS.includes(projectSettings.effortLevel)) {
|
|
1172
|
-
effort = projectSettings.effortLevel;
|
|
1173
|
-
}
|
|
1044
|
+
effort = readEffortFromSettings(path3.join(projectDir, ".claude", "settings.json")) ?? effort;
|
|
1174
1045
|
}
|
|
1175
1046
|
return effort;
|
|
1176
1047
|
}
|
|
1177
|
-
function evaluateClaudeComponent(key, data,
|
|
1048
|
+
function evaluateClaudeComponent(key, data, noColor2 = false, noIcons = false) {
|
|
1178
1049
|
switch (key) {
|
|
1179
1050
|
case "model":
|
|
1180
1051
|
return data.model?.display_name || "Claude";
|
|
1052
|
+
case "model-short": {
|
|
1053
|
+
const name = data.model?.display_name || "Claude";
|
|
1054
|
+
return name.replace(/\s*\(.*\)$/, "");
|
|
1055
|
+
}
|
|
1181
1056
|
case "model-id":
|
|
1182
1057
|
return data.model?.id || "";
|
|
1183
1058
|
case "model-letter":
|
|
@@ -1191,9 +1066,10 @@ function evaluateClaudeComponent(key, data, noColor = false, noIcons = false) {
|
|
|
1191
1066
|
case "effort":
|
|
1192
1067
|
case "effort-icon": {
|
|
1193
1068
|
const effort = resolveEffort(data);
|
|
1069
|
+
if (!effort) return "";
|
|
1194
1070
|
if (key === "effort-icon" && noIcons) return "";
|
|
1195
1071
|
const text = key === "effort-icon" ? "\u{F09D1}" : effort;
|
|
1196
|
-
if (
|
|
1072
|
+
if (noColor2) return text;
|
|
1197
1073
|
const r = `\x1B[${RESET}m`;
|
|
1198
1074
|
const effortColors = {
|
|
1199
1075
|
low: COLORS.green,
|
|
@@ -1235,7 +1111,7 @@ function evaluateFsComponent(key, data) {
|
|
|
1235
1111
|
return "";
|
|
1236
1112
|
}
|
|
1237
1113
|
}
|
|
1238
|
-
function evaluateGitComponent(key,
|
|
1114
|
+
function evaluateGitComponent(key, noColor2 = false, noIcons = false) {
|
|
1239
1115
|
switch (key) {
|
|
1240
1116
|
case "branch":
|
|
1241
1117
|
return execCommand("git branch --show-current 2>/dev/null");
|
|
@@ -1299,7 +1175,7 @@ function evaluateGitComponent(key, noColor = false, noIcons = false) {
|
|
|
1299
1175
|
const untracked = parseInt(execCommand("git ls-files --others --exclude-standard 2>/dev/null | wc -l")) || 0;
|
|
1300
1176
|
if (staged === 0 && modified === 0 && untracked === 0) return "";
|
|
1301
1177
|
const parts = [];
|
|
1302
|
-
if (
|
|
1178
|
+
if (noColor2) {
|
|
1303
1179
|
if (staged > 0) parts.push("+" + staged);
|
|
1304
1180
|
if (untracked > 0) parts.push("-" + untracked);
|
|
1305
1181
|
if (modified > 0) parts.push("~" + modified);
|
|
@@ -1355,10 +1231,20 @@ function evaluateContextComponent(key, data, args, noIcons = false) {
|
|
|
1355
1231
|
case "size":
|
|
1356
1232
|
return formatTokens(ctx?.context_window_size || 2e5);
|
|
1357
1233
|
case "bar": {
|
|
1358
|
-
const pct = ctx?.used_percentage || 0;
|
|
1234
|
+
const pct = Math.max(0, Math.min(100, ctx?.used_percentage || 0));
|
|
1359
1235
|
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1236
|
+
const BLOCKS2 = ["\u2591", "\u258E", "\u258C", "\u258A", "\u2588"];
|
|
1237
|
+
const fillExact = pct / 100 * width;
|
|
1238
|
+
const fullCells = Math.floor(fillExact);
|
|
1239
|
+
const fractional = fillExact - fullCells;
|
|
1240
|
+
const partialIdx = Math.round(fractional * 4);
|
|
1241
|
+
const partialChar = BLOCKS2[partialIdx];
|
|
1242
|
+
const emptyCells = width - fullCells - 1;
|
|
1243
|
+
const barStr = "\u2588".repeat(fullCells) + partialChar + "\u2591".repeat(Math.max(0, emptyCells));
|
|
1244
|
+
if (noColor) return barStr;
|
|
1245
|
+
const color = pct < 50 ? COLORS.green : pct < 75 ? COLORS.yellow : COLORS.red;
|
|
1246
|
+
const bold = pct >= 90 ? `;${COLORS.bold}` : "";
|
|
1247
|
+
return `\x1B[0;${color}${bold}m${barStr}\x1B[${RESET}m`;
|
|
1362
1248
|
}
|
|
1363
1249
|
case "icon": {
|
|
1364
1250
|
if (noIcons) return "";
|
|
@@ -1582,7 +1468,7 @@ function evaluateComponent(comp, data, options) {
|
|
|
1582
1468
|
result = evaluateEnvComponent(comp.key);
|
|
1583
1469
|
break;
|
|
1584
1470
|
case "usage":
|
|
1585
|
-
result = evaluateUsageComponent(comp.key, comp.args, options.noColor);
|
|
1471
|
+
result = evaluateUsageComponent(comp.key, data, comp.args, options.noColor);
|
|
1586
1472
|
break;
|
|
1587
1473
|
case "account":
|
|
1588
1474
|
result = evaluateAccountComponent(comp.key);
|
|
@@ -1616,14 +1502,14 @@ function evaluateComponent(comp, data, options) {
|
|
|
1616
1502
|
}
|
|
1617
1503
|
case "conditional": {
|
|
1618
1504
|
if (comp.children && evaluateCondition(comp.key, data)) {
|
|
1619
|
-
result = evaluateComponents(comp.children, data, options);
|
|
1505
|
+
result = evaluateComponents(comp.children, data, options, true);
|
|
1620
1506
|
}
|
|
1621
1507
|
break;
|
|
1622
1508
|
}
|
|
1623
1509
|
}
|
|
1624
1510
|
return applyStyles(result, comp.styles, options.noColor);
|
|
1625
1511
|
}
|
|
1626
|
-
function evaluateComponents(components, data, options) {
|
|
1512
|
+
function evaluateComponents(components, data, options, preserveEdgeSeparators = false) {
|
|
1627
1513
|
const evaluated = components.map((comp) => ({
|
|
1628
1514
|
type: comp.type,
|
|
1629
1515
|
value: evaluateComponent(comp, data, options)
|
|
@@ -1648,7 +1534,11 @@ function evaluateComponents(components, data, options) {
|
|
|
1648
1534
|
}
|
|
1649
1535
|
break;
|
|
1650
1536
|
}
|
|
1651
|
-
if (
|
|
1537
|
+
if (preserveEdgeSeparators) {
|
|
1538
|
+
if (!hasBefore && !hasAfter) continue;
|
|
1539
|
+
} else {
|
|
1540
|
+
if (!hasBefore || !hasAfter) continue;
|
|
1541
|
+
}
|
|
1652
1542
|
}
|
|
1653
1543
|
filtered.push(entry);
|
|
1654
1544
|
}
|
|
@@ -1656,8 +1546,13 @@ function evaluateComponents(components, data, options) {
|
|
|
1656
1546
|
for (let i = 0; i < filtered.length; i++) {
|
|
1657
1547
|
const entry = filtered[i];
|
|
1658
1548
|
const prev = filtered[i - 1];
|
|
1659
|
-
if (i > 0 && entry.type !== "sep" &&
|
|
1660
|
-
|
|
1549
|
+
if (i > 0 && entry.type !== "sep" && prev?.type !== "sep" && entry.value) {
|
|
1550
|
+
if (entry.type === "conditional") {
|
|
1551
|
+
const stripped = entry.value.replace(/\x1b\[[0-9;]*m/g, "");
|
|
1552
|
+
if (!stripped.startsWith(" ")) parts.push(" ");
|
|
1553
|
+
} else {
|
|
1554
|
+
parts.push(" ");
|
|
1555
|
+
}
|
|
1661
1556
|
}
|
|
1662
1557
|
if (entry.value) {
|
|
1663
1558
|
parts.push(entry.value);
|
|
@@ -1676,7 +1571,7 @@ function evaluateFormat(format, data, options = {}) {
|
|
|
1676
1571
|
}
|
|
1677
1572
|
|
|
1678
1573
|
// src/index.ts
|
|
1679
|
-
var VERSION = "1.
|
|
1574
|
+
var VERSION = true ? "1.9.0" : "dev";
|
|
1680
1575
|
async function readStdin() {
|
|
1681
1576
|
return new Promise((resolve, reject) => {
|
|
1682
1577
|
let input = "";
|