@ekkos/cli 1.0.19 → 1.0.21

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.
@@ -697,6 +697,13 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
697
697
  footerBox.left = H_PAD;
698
698
  footerBox.width = contentWidth;
699
699
  footerBox.height = layout.footer.height;
700
+ // Force blessed-contrib to re-render the chart canvas at the new dimensions
701
+ if (lastChartSeries) {
702
+ try {
703
+ tokenChart.setData(lastChartSeries);
704
+ }
705
+ catch { }
706
+ }
700
707
  }
701
708
  // Track geometry so we can re-anchor widgets even if tmux resize events are flaky
702
709
  let lastLayoutW = screen.width || 0;
@@ -766,6 +773,7 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
766
773
  // ── Update function ──
767
774
  let lastFileSize = 0;
768
775
  let lastData = null;
776
+ let lastChartSeries = null;
769
777
  let lastScrollPerc = 0; // Preserve scroll position across updates
770
778
  const debugLog = path.join(os.homedir(), '.ekkos', 'dashboard.log');
771
779
  function dlog(msg) {
@@ -848,11 +856,12 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
848
856
  const recent = data.turns.slice(-30);
849
857
  if (recent.length >= 2) {
850
858
  const x = recent.map(t => String(t.turn));
851
- tokenChart.setData([
859
+ lastChartSeries = [
852
860
  { title: 'Rd', x, y: recent.map(t => Math.round(t.cacheRead / 1000)), style: { line: 'green' } },
853
861
  { title: 'Wr', x, y: recent.map(t => Math.round(t.cacheCreate / 1000)), style: { line: 'yellow' } },
854
862
  { title: 'Out', x, y: recent.map(t => Math.round(t.output / 1000)), style: { line: 'cyan' } },
855
- ]);
863
+ ];
864
+ tokenChart.setData(lastChartSeries);
856
865
  }
857
866
  }
858
867
  catch (err) {
@@ -999,12 +1008,20 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
999
1008
  */
1000
1009
  async function fetchAnthropicUsage() {
1001
1010
  try {
1002
- if (process.platform !== 'darwin')
1003
- return null; // macOS keychain only
1004
- const { execSync } = require('child_process');
1005
- const credsJson = execSync('security find-generic-password -s "Claude Code-credentials" -w', { encoding: 'utf-8', timeout: 5000 }).trim();
1006
- const creds = JSON.parse(credsJson);
1007
- const token = creds?.claudeAiOauth?.accessToken;
1011
+ let token = null;
1012
+ if (process.platform === 'darwin') {
1013
+ const { execSync } = require('child_process');
1014
+ const credsJson = execSync('security find-generic-password -s "Claude Code-credentials" -w', { encoding: 'utf-8', timeout: 5000 }).trim();
1015
+ token = JSON.parse(credsJson)?.claudeAiOauth?.accessToken ?? null;
1016
+ }
1017
+ else if (process.platform === 'win32') {
1018
+ // Windows: Claude Code stores credentials in ~/.claude/.credentials.json
1019
+ const credsPath = path.join(os.homedir(), '.claude', '.credentials.json');
1020
+ if (fs.existsSync(credsPath)) {
1021
+ const creds = JSON.parse(fs.readFileSync(credsPath, 'utf-8'));
1022
+ token = creds?.claudeAiOauth?.accessToken ?? null;
1023
+ }
1024
+ }
1008
1025
  if (!token)
1009
1026
  return null;
1010
1027
  const resp = await fetch('https://api.anthropic.com/api/oauth/usage', {
@@ -1070,10 +1087,19 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
1070
1087
  screen.on('resize', () => {
1071
1088
  try {
1072
1089
  ensureLayoutSynced();
1073
- if (lastData)
1090
+ if (lastData) {
1074
1091
  updateDashboard();
1075
- else
1092
+ }
1093
+ else {
1094
+ // Even without data, re-apply chart series so the canvas redraws at new size
1095
+ if (lastChartSeries) {
1096
+ try {
1097
+ tokenChart.setData(lastChartSeries);
1098
+ }
1099
+ catch { }
1100
+ }
1076
1101
  screen.render();
1102
+ }
1077
1103
  }
1078
1104
  catch (err) {
1079
1105
  dlog(`Resize: ${err.message}`);
@@ -582,12 +582,19 @@ async function launchSwarmDashboard(launchTs, refreshMs) {
582
582
  // ── Usage window (Anthropic OAuth) ──
583
583
  async function fetchAnthropicUsage() {
584
584
  try {
585
- if (process.platform !== 'darwin')
586
- return null; // macOS keychain only
587
- const { execSync } = require('child_process');
588
- const credsJson = execSync('security find-generic-password -s "Claude Code-credentials" -w', { encoding: 'utf-8', timeout: 5000 }).trim();
589
- const creds = JSON.parse(credsJson);
590
- const token = creds?.claudeAiOauth?.accessToken;
585
+ let token = null;
586
+ if (process.platform === 'darwin') {
587
+ const { execSync } = require('child_process');
588
+ const credsJson = execSync('security find-generic-password -s "Claude Code-credentials" -w', { encoding: 'utf-8', timeout: 5000 }).trim();
589
+ token = JSON.parse(credsJson)?.claudeAiOauth?.accessToken ?? null;
590
+ }
591
+ else if (process.platform === 'win32') {
592
+ const credsPath = path.join(os.homedir(), '.claude', '.credentials.json');
593
+ if (require('fs').existsSync(credsPath)) {
594
+ const creds = JSON.parse(require('fs').readFileSync(credsPath, 'utf-8'));
595
+ token = creds?.claudeAiOauth?.accessToken ?? null;
596
+ }
597
+ }
591
598
  if (!token)
592
599
  return null;
593
600
  const resp = await fetch('https://api.anthropic.com/api/oauth/usage', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Setup ekkOS memory for AI coding assistants (Claude Code, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {