cc-usage-bar 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectColumns = detectColumns;
37
+ exports.visibleLength = visibleLength;
38
+ exports.decideWrap = decideWrap;
39
+ exports.compose = compose;
40
+ exports.main = main;
41
+ const node_child_process_1 = require("node:child_process");
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const fetch_1 = require("./fetch");
45
+ const FETCH_BIN = path.resolve(__dirname, '..', '..', 'bin', 'cc-usage-fetch.js');
46
+ const RESERVED = new Set(['--prefix', '--layout', '--help', '-h']);
47
+ function isLayout(s) {
48
+ return s === 'auto' || s === 'single' || s === 'multi';
49
+ }
50
+ function parseArgs(argv) {
51
+ const prefix = (0, fetch_1.parseArg)(argv, 'prefix') ?? process.env.CC_STATUSLINE_PREFIX ?? '';
52
+ const envLayout = process.env.CC_STATUSLINE_LAYOUT ?? '';
53
+ let layout = isLayout(envLayout) ? envLayout : 'auto';
54
+ const layoutArg = (0, fetch_1.parseArg)(argv, 'layout');
55
+ if (layoutArg && isLayout(layoutArg))
56
+ layout = layoutArg;
57
+ if (argv.includes('--help') || argv.includes('-h')) {
58
+ printHelp();
59
+ process.exit(0);
60
+ }
61
+ const fetchArgs = [];
62
+ for (let i = 0; i < argv.length; i++) {
63
+ const a = argv[i];
64
+ if (a.startsWith('--prefix=') || a.startsWith('--layout='))
65
+ continue;
66
+ if (RESERVED.has(a)) {
67
+ if (i + 1 < argv.length && !argv[i + 1].startsWith('--'))
68
+ i++;
69
+ continue;
70
+ }
71
+ fetchArgs.push(a);
72
+ }
73
+ return { prefix, layout, fetchArgs };
74
+ }
75
+ function readStdinSync() {
76
+ if (process.stdin.isTTY)
77
+ return '';
78
+ try {
79
+ return fs.readFileSync(0, 'utf8');
80
+ }
81
+ catch {
82
+ return '';
83
+ }
84
+ }
85
+ function spawnCapture(cmd, args, input, timeoutMs) {
86
+ return new Promise((resolve) => {
87
+ let out = '';
88
+ let settled = false;
89
+ const finish = (v) => {
90
+ if (settled)
91
+ return;
92
+ settled = true;
93
+ resolve(v);
94
+ };
95
+ let child;
96
+ try {
97
+ child = (0, node_child_process_1.spawn)(cmd, args, { stdio: ['pipe', 'pipe', 'ignore'], windowsHide: true });
98
+ }
99
+ catch {
100
+ return finish('');
101
+ }
102
+ const timer = setTimeout(() => {
103
+ try {
104
+ child.kill();
105
+ }
106
+ catch { }
107
+ finish(out);
108
+ }, timeoutMs);
109
+ child.stdout?.on('data', (b) => { out += b.toString('utf8'); });
110
+ child.on('close', () => { clearTimeout(timer); finish(out); });
111
+ child.on('error', () => { clearTimeout(timer); finish(''); });
112
+ try {
113
+ if (input)
114
+ child.stdin?.write(input);
115
+ child.stdin?.end();
116
+ }
117
+ catch {
118
+ clearTimeout(timer);
119
+ finish('');
120
+ }
121
+ });
122
+ }
123
+ function runShell(cmd, input) {
124
+ if (!cmd)
125
+ return Promise.resolve('');
126
+ const isWin = process.platform === 'win32';
127
+ return spawnCapture(isWin ? 'cmd.exe' : 'sh', isWin ? ['/c', cmd] : ['-c', cmd], input, 5000);
128
+ }
129
+ function runFetch(fetchArgs, input) {
130
+ return spawnCapture(process.execPath, [FETCH_BIN, ...fetchArgs], input, 8000);
131
+ }
132
+ function detectColumns() {
133
+ if (process.platform !== 'win32') {
134
+ const r = (0, node_child_process_1.spawnSync)('sh', ['-c', 'stty size </dev/tty 2>/dev/null'], {
135
+ stdio: ['ignore', 'pipe', 'ignore'],
136
+ encoding: 'utf8',
137
+ timeout: 500,
138
+ });
139
+ const m = (r.stdout ?? '').trim().match(/^(\d+)\s+(\d+)$/);
140
+ if (m)
141
+ return parseInt(m[2], 10);
142
+ }
143
+ else {
144
+ const r = (0, node_child_process_1.spawnSync)('cmd.exe', ['/c', 'mode', 'con'], {
145
+ stdio: ['ignore', 'pipe', 'ignore'],
146
+ encoding: 'utf8',
147
+ timeout: 2000,
148
+ windowsHide: true,
149
+ });
150
+ const m = (r.stdout ?? '').match(/Columns:\s*(\d+)/i);
151
+ if (m)
152
+ return parseInt(m[1], 10);
153
+ }
154
+ const env = process.env.COLUMNS;
155
+ if (env && /^\d+$/.test(env))
156
+ return parseInt(env, 10);
157
+ return 0;
158
+ }
159
+ const ANSI_CSI_RE = /\x1b\[[0-9;]*m/g;
160
+ const WIDE_RE = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-꓏가-힣豈-﫿︰-﹏＀-⦆¢-₩]|[\u{1F300}-\u{1FAFF}]|[\u{20000}-\u{3FFFD}]/u;
161
+ const ASCII_PRINTABLE_RE = /^[\x20-\x7E]*$/;
162
+ const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
163
+ function visibleLength(s) {
164
+ const stripped = s.replace(ANSI_CSI_RE, '');
165
+ if (ASCII_PRINTABLE_RE.test(stripped))
166
+ return stripped.length;
167
+ let w = 0;
168
+ for (const { segment } of segmenter.segment(stripped)) {
169
+ w += WIDE_RE.test(segment) ? 2 : 1;
170
+ }
171
+ return w;
172
+ }
173
+ function decideWrap(layout, prefix, bar, cols) {
174
+ if (layout === 'single')
175
+ return false;
176
+ if (layout === 'multi')
177
+ return true;
178
+ if (!prefix || !bar)
179
+ return false;
180
+ if (cols <= 0)
181
+ return true;
182
+ return visibleLength(prefix) + 1 + visibleLength(bar) > cols;
183
+ }
184
+ function compose(prefix, bar, wrap) {
185
+ if (!prefix && !bar)
186
+ return '';
187
+ if (!prefix)
188
+ return bar;
189
+ if (!bar)
190
+ return prefix;
191
+ return prefix + (wrap ? '\n' : ' ') + bar;
192
+ }
193
+ function printHelp() {
194
+ process.stdout.write(`cc-usage-bar-wrap — adaptive statusline composer.
195
+
196
+ Reads stdin (Claude Code statusline JSON), runs an optional prefix command and
197
+ cc-usage-fetch with the same stdin, then composes one or two lines based on the
198
+ real terminal width.
199
+
200
+ Usage: cc-usage-bar-wrap [options] [cc-usage-fetch args...]
201
+
202
+ Options:
203
+ --prefix=<command> Shell command rendering the prefix (default: $CC_STATUSLINE_PREFIX, empty)
204
+ --layout=<auto|single|multi> auto = single line if it fits, else wrap (default: auto, env: CC_STATUSLINE_LAYOUT)
205
+ --help Show this message
206
+
207
+ Any unrecognised argument is forwarded to cc-usage-fetch:
208
+ cc-usage-bar-wrap --prefix='sh ~/.claude/prefix.sh' --format=bar-time --bar-width=10
209
+ `);
210
+ }
211
+ async function main() {
212
+ const opts = parseArgs(process.argv.slice(2));
213
+ const input = readStdinSync();
214
+ const [prefixOut, barOut] = await Promise.all([runShell(opts.prefix, input), runFetch(opts.fetchArgs, input)]);
215
+ const prefix = prefixOut.replace(/\r?\n$/, '');
216
+ const bar = barOut.replace(/\r?\n$/, '');
217
+ const cols = opts.layout === 'auto' ? detectColumns() : 0;
218
+ const wrap = decideWrap(opts.layout, prefix, bar, cols);
219
+ const out = compose(prefix, bar, wrap);
220
+ if (out)
221
+ process.stdout.write(out);
222
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-usage-bar",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "Show your Claude Code subscription usage (5h / 7d, or balance) in the statusline. Supports Anthropic, Kimi, GLM, MiniMax, DeepSeek, StepFun, SiliconFlow, OpenRouter, Novita.",
5
5
  "keywords": [
6
6
  "claude",
@@ -32,11 +32,13 @@
32
32
  "bin": {
33
33
  "cc-usage-bar": "bin/cc-usage-statusline.js",
34
34
  "cc-usage-statusline": "bin/cc-usage-statusline.js",
35
- "cc-usage-fetch": "bin/cc-usage-fetch.js"
35
+ "cc-usage-fetch": "bin/cc-usage-fetch.js",
36
+ "cc-usage-bar-wrap": "bin/cc-usage-bar-wrap.js"
36
37
  },
37
38
  "files": [
38
39
  "dist/src",
39
40
  "bin",
41
+ "AGENTS.md",
40
42
  "README.md",
41
43
  "README.zh-CN.md",
42
44
  "LICENSE"