claudeline 1.8.0 → 1.10.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 +225 -323
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -166,7 +166,7 @@ function listComponents() {
|
|
|
166
166
|
{
|
|
167
167
|
name: "Claude/Session",
|
|
168
168
|
prefix: "claude",
|
|
169
|
-
items: ["model", "model-id", "model-letter", "effort", "effort-icon", "version", "session", "session-full", "style"]
|
|
169
|
+
items: ["model", "model-id", "model-letter", "model-family", "model-short", "effort", "effort-icon", "version", "session", "session-full", "style"]
|
|
170
170
|
},
|
|
171
171
|
{
|
|
172
172
|
name: "Context Window",
|
|
@@ -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: "
|
|
442
|
+
luca: "claude:model-family claude:effort-icon dim:sep:middot nerd:nf-md-source_branch git:repo sep:none text:: sep:none git:branch if:subdir(sep:none text:/ sep:none fs:relative) if:dirty(dim:sep:middot git:dirty) dim:sep:middot dim: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 "
|
|
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,150 +651,220 @@ 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
|
-
function
|
|
847
|
-
if (delta > 10) return "\x1B[0;33m";
|
|
848
|
-
if (delta < -10) return "\x1B[0;36m";
|
|
849
|
-
return "\x1B[0;32m";
|
|
850
|
-
}
|
|
851
|
-
function formatPaceIcon(delta, noColor) {
|
|
662
|
+
function formatPaceIcon(delta, noColor2) {
|
|
852
663
|
const icon = delta > 10 ? "\uF0E7" : delta < -10 ? "\uF017" : "\uF00C";
|
|
853
|
-
if (
|
|
854
|
-
|
|
664
|
+
if (noColor2) return icon;
|
|
665
|
+
const { fg, bold } = barColorFromDelta(delta);
|
|
666
|
+
if (fg === null) return icon;
|
|
667
|
+
return `\x1B[0;${bold ? "1;" : ""}${fg}m${icon}\x1B[0m`;
|
|
855
668
|
}
|
|
856
|
-
function formatPaceDelta(delta,
|
|
669
|
+
function formatPaceDelta(delta, noColor2) {
|
|
857
670
|
const rounded = Math.round(delta);
|
|
858
671
|
let text;
|
|
859
672
|
if (rounded > 0) text = "+" + rounded + "%";
|
|
860
673
|
else if (rounded < 0) text = rounded + "%";
|
|
861
674
|
else text = "\xB10%";
|
|
862
|
-
if (
|
|
863
|
-
|
|
675
|
+
if (noColor2) return text;
|
|
676
|
+
const { fg, bold } = barColorFromDelta(delta);
|
|
677
|
+
if (fg === null) return text;
|
|
678
|
+
return `\x1B[0;${bold ? "1;" : ""}${fg}m${text}\x1B[0m`;
|
|
864
679
|
}
|
|
865
|
-
function
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
return
|
|
680
|
+
function barColorFromDelta(delta) {
|
|
681
|
+
if (delta > 15) return { fg: 31, bold: true };
|
|
682
|
+
if (delta > 5) return { fg: 31, bold: false };
|
|
683
|
+
return { fg: null, bold: false };
|
|
869
684
|
}
|
|
870
|
-
function
|
|
871
|
-
const
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
685
|
+
function makeBar(pct, width, noColor2, delta = 0, label) {
|
|
686
|
+
const clamped = Math.max(0, Math.min(100, pct));
|
|
687
|
+
const filled = Math.round(clamped / 100 * width);
|
|
688
|
+
const empty = width - filled;
|
|
689
|
+
const filledStr = "\u2588".repeat(filled);
|
|
690
|
+
const emptyStr = "\u2591".repeat(empty);
|
|
691
|
+
if (noColor2) return (label || "") + filledStr + emptyStr;
|
|
692
|
+
const { fg, bold } = barColorFromDelta(delta);
|
|
693
|
+
if (fg === null) {
|
|
694
|
+
const bar2 = `${filledStr}\x1B[2m${emptyStr}\x1B[0m`;
|
|
695
|
+
return label ? label + bar2 : bar2;
|
|
878
696
|
}
|
|
697
|
+
const boldStr = bold ? "1;" : "";
|
|
698
|
+
const fillColor = `\x1B[0;${boldStr}${fg}m`;
|
|
699
|
+
const dimColor = `\x1B[0;2;${fg}m`;
|
|
700
|
+
const bar = `${fillColor}${filledStr}\x1B[0m${dimColor}${emptyStr}\x1B[0m`;
|
|
701
|
+
return label ? label + bar : bar;
|
|
879
702
|
}
|
|
880
|
-
function evaluateUsageComponent(key, args,
|
|
881
|
-
const
|
|
882
|
-
if (!
|
|
703
|
+
function evaluateUsageComponent(key, data, args, noColor2 = false) {
|
|
704
|
+
const limits = data.rate_limits;
|
|
705
|
+
if (!limits) return "";
|
|
706
|
+
const fiveHour = limits.five_hour;
|
|
707
|
+
const sevenDay = limits.seven_day;
|
|
883
708
|
switch (key) {
|
|
884
709
|
case "5h":
|
|
885
|
-
return Math.round(
|
|
710
|
+
return Math.round(fiveHour.used_percentage) + "%";
|
|
886
711
|
case "week":
|
|
887
712
|
case "7d":
|
|
888
|
-
return Math.round(
|
|
713
|
+
return Math.round(sevenDay.used_percentage) + "%";
|
|
889
714
|
case "5h-reset":
|
|
890
|
-
return formatTimeUntil(
|
|
715
|
+
return formatTimeUntil(fiveHour.resets_at);
|
|
891
716
|
case "week-reset":
|
|
892
717
|
case "7d-reset":
|
|
893
|
-
return formatTimeUntil(
|
|
718
|
+
return formatTimeUntil(sevenDay.resets_at);
|
|
894
719
|
case "5h-bar": {
|
|
895
720
|
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
896
|
-
|
|
721
|
+
const { delta } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
722
|
+
return makeBar(fiveHour.used_percentage, width, noColor2, delta);
|
|
897
723
|
}
|
|
898
724
|
case "week-bar":
|
|
899
725
|
case "7d-bar": {
|
|
900
726
|
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
901
|
-
|
|
727
|
+
const { delta } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
728
|
+
return makeBar(sevenDay.used_percentage, width, noColor2, delta);
|
|
902
729
|
}
|
|
903
730
|
case "5h-icon": {
|
|
904
|
-
const
|
|
731
|
+
const { delta } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
732
|
+
const { fg, bold } = barColorFromDelta(delta);
|
|
905
733
|
const icon = "\uF111";
|
|
906
|
-
if (
|
|
907
|
-
|
|
908
|
-
if (pct < 90) return `\x1B[0;31m${icon}\x1B[0m`;
|
|
909
|
-
return `\x1B[0;1;31m${icon}\x1B[0m`;
|
|
734
|
+
if (noColor2 || fg === null) return icon;
|
|
735
|
+
return `\x1B[0;${bold ? "1;" : ""}${fg}m${icon}\x1B[0m`;
|
|
910
736
|
}
|
|
911
737
|
case "week-icon":
|
|
912
738
|
case "7d-icon": {
|
|
913
|
-
const
|
|
739
|
+
const { delta } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
740
|
+
const { fg, bold } = barColorFromDelta(delta);
|
|
914
741
|
const icon = "\uF111";
|
|
915
|
-
if (
|
|
916
|
-
|
|
917
|
-
if (pct < 90) return `\x1B[0;31m${icon}\x1B[0m`;
|
|
918
|
-
return `\x1B[0;1;31m${icon}\x1B[0m`;
|
|
742
|
+
if (noColor2 || fg === null) return icon;
|
|
743
|
+
return `\x1B[0;${bold ? "1;" : ""}${fg}m${icon}\x1B[0m`;
|
|
919
744
|
}
|
|
920
745
|
case "5h-target": {
|
|
921
|
-
const { target } = calculatePace(
|
|
746
|
+
const { target } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
922
747
|
return Math.round(target) + "%";
|
|
923
748
|
}
|
|
924
749
|
case "week-target":
|
|
925
750
|
case "7d-target": {
|
|
926
|
-
const { target } = calculatePace(
|
|
751
|
+
const { target } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
927
752
|
return Math.round(target) + "%";
|
|
928
753
|
}
|
|
929
754
|
case "5h-pace": {
|
|
930
|
-
const { delta } = calculatePace(
|
|
931
|
-
return formatPaceDelta(delta,
|
|
755
|
+
const { delta } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
756
|
+
return formatPaceDelta(delta, noColor2);
|
|
932
757
|
}
|
|
933
758
|
case "week-pace":
|
|
934
759
|
case "7d-pace": {
|
|
935
|
-
const { delta } = calculatePace(
|
|
936
|
-
return formatPaceDelta(delta,
|
|
760
|
+
const { delta } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
761
|
+
return formatPaceDelta(delta, noColor2);
|
|
937
762
|
}
|
|
938
763
|
case "5h-pace-icon": {
|
|
939
|
-
const { delta } = calculatePace(
|
|
940
|
-
return formatPaceIcon(delta,
|
|
764
|
+
const { delta } = calculatePace(fiveHour.used_percentage, fiveHour.resets_at, FIVE_HOUR_MS);
|
|
765
|
+
return formatPaceIcon(delta, noColor2);
|
|
941
766
|
}
|
|
942
767
|
case "week-pace-icon":
|
|
943
768
|
case "7d-pace-icon": {
|
|
944
|
-
const { delta } = calculatePace(
|
|
945
|
-
return formatPaceIcon(delta,
|
|
769
|
+
const { delta } = calculatePace(sevenDay.used_percentage, sevenDay.resets_at, SEVEN_DAY_MS);
|
|
770
|
+
return formatPaceIcon(delta, noColor2);
|
|
946
771
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
772
|
+
default:
|
|
773
|
+
return "";
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// src/account.ts
|
|
778
|
+
import * as fs2 from "fs";
|
|
779
|
+
import * as path2 from "path";
|
|
780
|
+
import * as os2 from "os";
|
|
781
|
+
import * as crypto from "crypto";
|
|
782
|
+
import { execSync } from "child_process";
|
|
783
|
+
var PROFILE_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
784
|
+
function extractToken(raw) {
|
|
785
|
+
try {
|
|
786
|
+
const parsed = JSON.parse(raw);
|
|
787
|
+
if (typeof parsed === "string") return parsed;
|
|
788
|
+
if (parsed.claudeAiOauth?.accessToken) return parsed.claudeAiOauth.accessToken;
|
|
789
|
+
if (parsed.accessToken) return parsed.accessToken;
|
|
790
|
+
if (parsed.access_token) return parsed.access_token;
|
|
791
|
+
} catch {
|
|
792
|
+
}
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
function getKeychainService() {
|
|
796
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
797
|
+
if (configDir) {
|
|
798
|
+
const suffix = crypto.createHash("sha256").update(configDir).digest("hex").slice(0, 8);
|
|
799
|
+
return `Claude Code-credentials-${suffix}`;
|
|
800
|
+
}
|
|
801
|
+
return "Claude Code-credentials";
|
|
802
|
+
}
|
|
803
|
+
function getOAuthToken() {
|
|
804
|
+
try {
|
|
805
|
+
if (process.platform === "darwin") {
|
|
806
|
+
const service = getKeychainService();
|
|
807
|
+
const raw = execSync(
|
|
808
|
+
`security find-generic-password -s "${service}" -w 2>/dev/null`,
|
|
809
|
+
{ encoding: "utf8" }
|
|
810
|
+
).trim();
|
|
811
|
+
return extractToken(raw);
|
|
966
812
|
}
|
|
967
|
-
|
|
968
|
-
const
|
|
969
|
-
|
|
813
|
+
if (process.platform === "linux") {
|
|
814
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path2.join(os2.homedir(), ".claude");
|
|
815
|
+
const raw = fs2.readFileSync(path2.join(configDir, ".credentials.json"), "utf8").trim();
|
|
816
|
+
return extractToken(raw);
|
|
970
817
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
818
|
+
} catch {
|
|
819
|
+
}
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
function getCacheFile(token) {
|
|
823
|
+
const hash = crypto.createHash("sha256").update(token).digest("hex").slice(0, 12);
|
|
824
|
+
const dir = path2.join(getClaudeConfigDir(), "cache");
|
|
825
|
+
try {
|
|
826
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
827
|
+
} catch {
|
|
828
|
+
}
|
|
829
|
+
return path2.join(dir, `claudeline-profile-${hash}.json`);
|
|
830
|
+
}
|
|
831
|
+
function readCachedProfile(cacheFile) {
|
|
832
|
+
try {
|
|
833
|
+
const cached = JSON.parse(fs2.readFileSync(cacheFile, "utf8"));
|
|
834
|
+
if (Date.now() - cached.fetched_at < PROFILE_CACHE_TTL_MS) {
|
|
835
|
+
return cached.email;
|
|
974
836
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
837
|
+
} catch {
|
|
838
|
+
}
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
function fetchEmail(token) {
|
|
842
|
+
try {
|
|
843
|
+
const result = execSync(
|
|
844
|
+
`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"`,
|
|
845
|
+
{ encoding: "utf8", env: { ...process.env, __CLAUDE_TOKEN: token } }
|
|
846
|
+
).trim();
|
|
847
|
+
const data = JSON.parse(result);
|
|
848
|
+
return data.account?.email ?? null;
|
|
849
|
+
} catch {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
function evaluateAccountComponent(key) {
|
|
854
|
+
if (key !== "email") return "";
|
|
855
|
+
const token = getOAuthToken();
|
|
856
|
+
if (!token) return "";
|
|
857
|
+
const cacheFile = getCacheFile(token);
|
|
858
|
+
const cached = readCachedProfile(cacheFile);
|
|
859
|
+
if (cached) return cached;
|
|
860
|
+
const email = fetchEmail(token);
|
|
861
|
+
if (email) {
|
|
862
|
+
try {
|
|
863
|
+
fs2.writeFileSync(cacheFile, JSON.stringify({ email, fetched_at: Date.now() }));
|
|
864
|
+
} catch {
|
|
978
865
|
}
|
|
979
|
-
default:
|
|
980
|
-
return "";
|
|
981
866
|
}
|
|
867
|
+
return email ?? "";
|
|
982
868
|
}
|
|
983
869
|
|
|
984
870
|
// src/nerdfonts.ts
|
|
@@ -1131,57 +1017,55 @@ function execCommand(cmd) {
|
|
|
1131
1017
|
return "";
|
|
1132
1018
|
}
|
|
1133
1019
|
}
|
|
1134
|
-
function applyStyles(text, styles,
|
|
1135
|
-
if (
|
|
1020
|
+
function applyStyles(text, styles, noColor2) {
|
|
1021
|
+
if (noColor2 || styles.length === 0 || !text) return text;
|
|
1136
1022
|
const codes = styles.map((s) => COLORS[s]).filter(Boolean);
|
|
1137
1023
|
if (codes.length === 0) return text;
|
|
1138
1024
|
return `\x1B[0;${codes.join(";")}m${text}\x1B[${RESET}m`;
|
|
1139
1025
|
}
|
|
1140
|
-
var
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
return cached.data;
|
|
1146
|
-
}
|
|
1026
|
+
var VALID_EFFORTS = ["low", "medium", "high"];
|
|
1027
|
+
function isValidEffort(value) {
|
|
1028
|
+
return typeof value === "string" && VALID_EFFORTS.includes(value);
|
|
1029
|
+
}
|
|
1030
|
+
function readEffortFromSettings(filePath) {
|
|
1147
1031
|
try {
|
|
1148
1032
|
const data = JSON.parse(fs3.readFileSync(filePath, "utf8"));
|
|
1149
|
-
|
|
1150
|
-
return data;
|
|
1033
|
+
return isValidEffort(data.effortLevel) ? data.effortLevel : null;
|
|
1151
1034
|
} catch {
|
|
1152
|
-
return
|
|
1035
|
+
return null;
|
|
1153
1036
|
}
|
|
1154
1037
|
}
|
|
1155
|
-
var VALID_EFFORTS = ["low", "medium", "high"];
|
|
1156
1038
|
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;
|
|
1039
|
+
if (isValidEffort(data.model?.reasoning_effort)) {
|
|
1040
|
+
return data.model.reasoning_effort;
|
|
1166
1041
|
}
|
|
1042
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path3.join(os3.homedir(), ".claude");
|
|
1043
|
+
let effort = readEffortFromSettings(path3.join(configDir, "settings.json"));
|
|
1167
1044
|
const projectDir = data.workspace?.project_dir;
|
|
1168
1045
|
if (projectDir) {
|
|
1169
|
-
|
|
1170
|
-
const projectSettings = readSettingsFile(projectPath);
|
|
1171
|
-
if (typeof projectSettings.effortLevel === "string" && VALID_EFFORTS.includes(projectSettings.effortLevel)) {
|
|
1172
|
-
effort = projectSettings.effortLevel;
|
|
1173
|
-
}
|
|
1046
|
+
effort = readEffortFromSettings(path3.join(projectDir, ".claude", "settings.json")) ?? effort;
|
|
1174
1047
|
}
|
|
1175
1048
|
return effort;
|
|
1176
1049
|
}
|
|
1177
|
-
function evaluateClaudeComponent(key, data,
|
|
1050
|
+
function evaluateClaudeComponent(key, data, noColor2 = false, noIcons = false) {
|
|
1178
1051
|
switch (key) {
|
|
1179
1052
|
case "model":
|
|
1180
1053
|
return data.model?.display_name || "Claude";
|
|
1054
|
+
case "model-short": {
|
|
1055
|
+
const name = data.model?.display_name || "Claude";
|
|
1056
|
+
return name.replace(/\s*\(.*\)$/, "");
|
|
1057
|
+
}
|
|
1181
1058
|
case "model-id":
|
|
1182
1059
|
return data.model?.id || "";
|
|
1183
1060
|
case "model-letter":
|
|
1184
1061
|
return (data.model?.display_name || "C")[0].toUpperCase();
|
|
1062
|
+
case "model-family": {
|
|
1063
|
+
const name = (data.model?.display_name || "").toLowerCase();
|
|
1064
|
+
if (name.includes("opus")) return "Opus";
|
|
1065
|
+
if (name.includes("sonnet")) return "Sonnet";
|
|
1066
|
+
if (name.includes("haiku")) return "Haiku";
|
|
1067
|
+
return data.model?.display_name || "Claude";
|
|
1068
|
+
}
|
|
1185
1069
|
case "version":
|
|
1186
1070
|
return data.version || "";
|
|
1187
1071
|
case "session":
|
|
@@ -1191,17 +1075,18 @@ function evaluateClaudeComponent(key, data, noColor = false, noIcons = false) {
|
|
|
1191
1075
|
case "effort":
|
|
1192
1076
|
case "effort-icon": {
|
|
1193
1077
|
const effort = resolveEffort(data);
|
|
1078
|
+
if (!effort) return "";
|
|
1194
1079
|
if (key === "effort-icon" && noIcons) return "";
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
return
|
|
1080
|
+
if (key === "effort-icon") {
|
|
1081
|
+
const icons = {
|
|
1082
|
+
low: "\u25CB",
|
|
1083
|
+
medium: "\u25D1",
|
|
1084
|
+
high: "\u25CF",
|
|
1085
|
+
max: "\u25C9"
|
|
1086
|
+
};
|
|
1087
|
+
return icons[effort] || "\u25CF";
|
|
1088
|
+
}
|
|
1089
|
+
return effort;
|
|
1205
1090
|
}
|
|
1206
1091
|
case "style":
|
|
1207
1092
|
return data.output_style?.name || "default";
|
|
@@ -1235,7 +1120,7 @@ function evaluateFsComponent(key, data) {
|
|
|
1235
1120
|
return "";
|
|
1236
1121
|
}
|
|
1237
1122
|
}
|
|
1238
|
-
function evaluateGitComponent(key,
|
|
1123
|
+
function evaluateGitComponent(key, noColor2 = false, noIcons = false) {
|
|
1239
1124
|
switch (key) {
|
|
1240
1125
|
case "branch":
|
|
1241
1126
|
return execCommand("git branch --show-current 2>/dev/null");
|
|
@@ -1299,7 +1184,7 @@ function evaluateGitComponent(key, noColor = false, noIcons = false) {
|
|
|
1299
1184
|
const untracked = parseInt(execCommand("git ls-files --others --exclude-standard 2>/dev/null | wc -l")) || 0;
|
|
1300
1185
|
if (staged === 0 && modified === 0 && untracked === 0) return "";
|
|
1301
1186
|
const parts = [];
|
|
1302
|
-
if (
|
|
1187
|
+
if (noColor2) {
|
|
1303
1188
|
if (staged > 0) parts.push("+" + staged);
|
|
1304
1189
|
if (untracked > 0) parts.push("-" + untracked);
|
|
1305
1190
|
if (modified > 0) parts.push("~" + modified);
|
|
@@ -1355,10 +1240,18 @@ function evaluateContextComponent(key, data, args, noIcons = false) {
|
|
|
1355
1240
|
case "size":
|
|
1356
1241
|
return formatTokens(ctx?.context_window_size || 2e5);
|
|
1357
1242
|
case "bar": {
|
|
1358
|
-
const pct = ctx?.used_percentage || 0;
|
|
1243
|
+
const pct = Math.max(0, Math.min(100, ctx?.used_percentage || 0));
|
|
1359
1244
|
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
1360
1245
|
const filled = Math.round(pct / 100 * width);
|
|
1361
|
-
|
|
1246
|
+
const empty = width - filled;
|
|
1247
|
+
const filledStr = "\u2588".repeat(filled);
|
|
1248
|
+
const emptyStr = "\u2591".repeat(empty);
|
|
1249
|
+
if (noColor) return filledStr + emptyStr;
|
|
1250
|
+
const color = pct < 50 ? COLORS.green : pct < 75 ? COLORS.yellow : COLORS.red;
|
|
1251
|
+
const bold = pct >= 90 ? `;${COLORS.bold}` : "";
|
|
1252
|
+
const fillColor = `\x1B[0;${color}${bold}m`;
|
|
1253
|
+
const dimColor = `\x1B[0;2;${color}m`;
|
|
1254
|
+
return `${fillColor}${filledStr}\x1B[${RESET}m${dimColor}${emptyStr}\x1B[${RESET}m`;
|
|
1362
1255
|
}
|
|
1363
1256
|
case "icon": {
|
|
1364
1257
|
if (noIcons) return "";
|
|
@@ -1582,7 +1475,7 @@ function evaluateComponent(comp, data, options) {
|
|
|
1582
1475
|
result = evaluateEnvComponent(comp.key);
|
|
1583
1476
|
break;
|
|
1584
1477
|
case "usage":
|
|
1585
|
-
result = evaluateUsageComponent(comp.key, comp.args, options.noColor);
|
|
1478
|
+
result = evaluateUsageComponent(comp.key, data, comp.args, options.noColor);
|
|
1586
1479
|
break;
|
|
1587
1480
|
case "account":
|
|
1588
1481
|
result = evaluateAccountComponent(comp.key);
|
|
@@ -1616,14 +1509,14 @@ function evaluateComponent(comp, data, options) {
|
|
|
1616
1509
|
}
|
|
1617
1510
|
case "conditional": {
|
|
1618
1511
|
if (comp.children && evaluateCondition(comp.key, data)) {
|
|
1619
|
-
result = evaluateComponents(comp.children, data, options);
|
|
1512
|
+
result = evaluateComponents(comp.children, data, options, true);
|
|
1620
1513
|
}
|
|
1621
1514
|
break;
|
|
1622
1515
|
}
|
|
1623
1516
|
}
|
|
1624
1517
|
return applyStyles(result, comp.styles, options.noColor);
|
|
1625
1518
|
}
|
|
1626
|
-
function evaluateComponents(components, data, options) {
|
|
1519
|
+
function evaluateComponents(components, data, options, preserveEdgeSeparators = false) {
|
|
1627
1520
|
const evaluated = components.map((comp) => ({
|
|
1628
1521
|
type: comp.type,
|
|
1629
1522
|
value: evaluateComponent(comp, data, options)
|
|
@@ -1648,7 +1541,11 @@ function evaluateComponents(components, data, options) {
|
|
|
1648
1541
|
}
|
|
1649
1542
|
break;
|
|
1650
1543
|
}
|
|
1651
|
-
if (
|
|
1544
|
+
if (preserveEdgeSeparators) {
|
|
1545
|
+
if (!hasBefore && !hasAfter) continue;
|
|
1546
|
+
} else {
|
|
1547
|
+
if (!hasBefore || !hasAfter) continue;
|
|
1548
|
+
}
|
|
1652
1549
|
}
|
|
1653
1550
|
filtered.push(entry);
|
|
1654
1551
|
}
|
|
@@ -1656,8 +1553,13 @@ function evaluateComponents(components, data, options) {
|
|
|
1656
1553
|
for (let i = 0; i < filtered.length; i++) {
|
|
1657
1554
|
const entry = filtered[i];
|
|
1658
1555
|
const prev = filtered[i - 1];
|
|
1659
|
-
if (i > 0 && entry.type !== "sep" &&
|
|
1660
|
-
|
|
1556
|
+
if (i > 0 && entry.type !== "sep" && prev?.type !== "sep" && entry.value) {
|
|
1557
|
+
if (entry.type === "conditional") {
|
|
1558
|
+
const stripped = entry.value.replace(/\x1b\[[0-9;]*m/g, "");
|
|
1559
|
+
if (!stripped.startsWith(" ")) parts.push(" ");
|
|
1560
|
+
} else {
|
|
1561
|
+
parts.push(" ");
|
|
1562
|
+
}
|
|
1661
1563
|
}
|
|
1662
1564
|
if (entry.value) {
|
|
1663
1565
|
parts.push(entry.value);
|
|
@@ -1676,7 +1578,7 @@ function evaluateFormat(format, data, options = {}) {
|
|
|
1676
1578
|
}
|
|
1677
1579
|
|
|
1678
1580
|
// src/index.ts
|
|
1679
|
-
var VERSION = "1.
|
|
1581
|
+
var VERSION = true ? "1.10.0" : "dev";
|
|
1680
1582
|
async function readStdin() {
|
|
1681
1583
|
return new Promise((resolve, reject) => {
|
|
1682
1584
|
let input = "";
|