ctxline-claude 1.2.1 → 1.5.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/README.md +16 -4
- package/package.json +1 -1
- package/statusline.js +148 -48
package/README.md
CHANGED
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
<a href="https://github.com/MithunWijayasiri/ctxline-claude/stargazers">
|
|
18
18
|
<img src="https://img.shields.io/github/stars/MithunWijayasiri/ctxline-claude" alt="stars">
|
|
19
19
|
</a>
|
|
20
|
+
<a href="https://ko-fi.com/mithunwijayasiri">
|
|
21
|
+
<img src="https://img.shields.io/badge/Ko--fi-support-ff5e5b?logo=ko-fi&logoColor=white" alt="Support on Ko-fi">
|
|
22
|
+
</a>
|
|
20
23
|
</p>
|
|
21
24
|
|
|
22
25
|
<p align="center">
|
|
@@ -121,6 +124,7 @@ Remove-Item "$env:USERPROFILE\.claude\cache\usage-cache.json" -ErrorAction Silen
|
|
|
121
124
|
| **Context** | Visual bar of context-window usage |
|
|
122
125
|
| **Current** | Live 5-hour session limit + reset countdown (subscription users) |
|
|
123
126
|
| **Weekly** | Weekly usage allowance + time until the weekly reset (subscription users) |
|
|
127
|
+
| **Model limit** | Weekly limit scoped to a single model, when your account has one — labelled by the model's initial (`F` = Fable) |
|
|
124
128
|
| **Cost** | Running session cost in USD (e.g. `$0.42`) |
|
|
125
129
|
| **Task** | The in-progress todo, when there is one |
|
|
126
130
|
|
|
@@ -137,7 +141,7 @@ Remove-Item "$env:USERPROFILE\.claude\cache\usage-cache.json" -ErrorAction Silen
|
|
|
137
141
|
|
|
138
142
|
The statusline is zero-config by default. To **hide segments you don't want**, set the `CTXLINE_DISABLE` environment variable to a comma-separated list of any of:
|
|
139
143
|
|
|
140
|
-
`branch` · `effort` · `cost` · `task` · `usage` (5-hour + weekly)
|
|
144
|
+
`branch` · `effort` · `cost` · `task` · `usage` (5-hour + weekly + model-scoped)
|
|
141
145
|
|
|
142
146
|
Directory, model, and context always show; unknown names are ignored. Example below hides cost and the current task.
|
|
143
147
|
|
|
@@ -181,7 +185,8 @@ To re-enable a segment, remove it from the list (or delete the variable) and res
|
|
|
181
185
|
|
|
182
186
|
## How it works
|
|
183
187
|
|
|
184
|
-
- **Source** — context comes from Claude Code's session data.
|
|
188
|
+
- **Source** — context comes from Claude Code's session data. The 5-hour and weekly bars are read straight from the `rate_limits` field Claude Code pipes in (no network), falling back to `https://api.anthropic.com/api/oauth/usage` when that field isn't present yet. API-key users skip usage entirely.
|
|
189
|
+
- **Model-scoped limits** — `rate_limits` carries only `five_hour` and `seven_day`, so a model-scoped weekly limit can only come from `/usage` (its `limits` array). It's served from the same cache as everything else, so this costs at most one call per 30s no matter how often the line renders.
|
|
185
190
|
- **No network on the fast path** — when `rate_limits` is in the session data, there's no API call at all. The fetch below only runs as a fallback (e.g. the first render of a session, before the field appears).
|
|
186
191
|
- **Adaptive timing** — for the fallback fetch: 1.5s timeout on the first prompt (cold start), 1.2s after (connection reused).
|
|
187
192
|
- **Caching** — the fallback fetch is cached at `~/.claude/cache/usage-cache.json`, shared across sessions. Within 30s the cache renders directly (the API call is skipped); if a live call fails, the last value (up to 10 min old) is shown so the bar never vanishes. The reset countdown recomputes every render.
|
|
@@ -238,10 +243,17 @@ No. Your credentials never leave your machine. On the fast path no token is read
|
|
|
238
243
|
|
|
239
244
|
</details>
|
|
240
245
|
|
|
241
|
-
## License
|
|
242
246
|
|
|
243
|
-
|
|
247
|
+
## Support
|
|
248
|
+
|
|
249
|
+
If you find this project useful, consider supporting its development on [Ko-fi](https://ko-fi.com/mithunwijayasiri). Your donations help keep the project maintained, improve existing features, and fund new open-source tools.
|
|
250
|
+
|
|
251
|
+
Thank you for your support! ❤️
|
|
244
252
|
|
|
245
253
|
## Credits
|
|
246
254
|
|
|
247
255
|
Thanks to [@TahaSabir0](https://github.com/TahaSabir0) for the base config.
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT
|
package/package.json
CHANGED
package/statusline.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Claude Code Enhanced Statusline
|
|
3
|
-
// Shows: directory | model | context usage |
|
|
3
|
+
// Shows: directory | model | context usage | 5-hour + weekly + model-scoped usage | current task
|
|
4
4
|
// Auto-detects API key vs subscription usage
|
|
5
5
|
// https://github.com/MithunWijayasiri/ctxline-claude
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ const { execSync, execFileSync } = require('child_process');
|
|
|
13
13
|
const IS_API_KEY = !!process.env.ANTHROPIC_API_KEY;
|
|
14
14
|
|
|
15
15
|
// Optional segment opt-out: CTXLINE_DISABLE is a comma list of segments to hide.
|
|
16
|
-
// Recognized: branch, effort, cost, task, usage (H+W). dir/model/context always render.
|
|
16
|
+
// Recognized: branch, effort, cost, task, usage (H+W+model-scoped). dir/model/context always render.
|
|
17
17
|
// Unknown names are ignored. Disabling a segment also skips its work (git, todo read,
|
|
18
18
|
// usage fetch).
|
|
19
19
|
const DISABLED = new Set(
|
|
@@ -84,6 +84,13 @@ function getUsageColor(percentage) {
|
|
|
84
84
|
return colors.red;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// Model-scoped bars skip the H/W thresholds: a line can carry several at once, so a flat
|
|
88
|
+
// orange keeps them readable as one group. Red at >=90 is the one distinction kept — that
|
|
89
|
+
// bar is about to block the model it names.
|
|
90
|
+
function getScopedColor(percentage) {
|
|
91
|
+
return percentage >= 90 ? colors.red : colors.orange;
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
// Shorten verbose model names for the statusline: "Opus 4.8 (1M context)" -> "Opus 4.8 (1M)".
|
|
88
95
|
function shortenModel(name) {
|
|
89
96
|
return name.replace(/\s+context\)/i, ')');
|
|
@@ -125,7 +132,9 @@ function getGitBranch(dir) {
|
|
|
125
132
|
if (!gitDir) return '';
|
|
126
133
|
const head = fs.readFileSync(path.join(gitDir, 'HEAD'), 'utf8').trim();
|
|
127
134
|
const ref = head.match(/^ref:\s*refs\/heads\/(.+)$/);
|
|
128
|
-
|
|
135
|
+
// Strip control chars: HEAD is read raw (not git-validated), so a hand-crafted file
|
|
136
|
+
// in an untrusted archive could inject terminal escape sequences.
|
|
137
|
+
if (ref) return truncateBranch(ref[1].replace(/[\x00-\x1f\x7f]/g, ''));
|
|
129
138
|
if (/^[0-9a-f]{7,40}$/i.test(head)) return head.slice(0, 7); // detached HEAD -> short sha
|
|
130
139
|
return '';
|
|
131
140
|
} catch (e) {
|
|
@@ -220,7 +229,8 @@ function getContextBar(remaining) {
|
|
|
220
229
|
// Render a compact usage segment from raw data: "<label><pct> ↺ <countdown>"
|
|
221
230
|
// (e.g. "H81 ↺ 2h21m") — no bar. Called on every read (live or cached) so the reset
|
|
222
231
|
// countdown is always recomputed from resetsAt rather than frozen at fetch time.
|
|
223
|
-
|
|
232
|
+
// `color` overrides the threshold color — the model-scoped bars pass getScopedColor.
|
|
233
|
+
function buildUsageBar(label, percentage, resetsAt, color) {
|
|
224
234
|
let timeStr = '';
|
|
225
235
|
if (resetsAt) {
|
|
226
236
|
const diffMins = Math.max(0, Math.floor((new Date(resetsAt) - new Date()) / 60000));
|
|
@@ -232,18 +242,41 @@ function buildUsageBar(label, percentage, resetsAt) {
|
|
|
232
242
|
else timeStr = `${mins}m`;
|
|
233
243
|
}
|
|
234
244
|
|
|
235
|
-
const
|
|
245
|
+
const barColor = color || getUsageColor(percentage);
|
|
236
246
|
const timePart = timeStr ? `${colors.dim} ↺ ${timeStr}${colors.reset}` : '';
|
|
237
247
|
|
|
238
|
-
return `${
|
|
248
|
+
return `${barColor}${label}${percentage}${colors.reset}${timePart}`;
|
|
239
249
|
}
|
|
240
250
|
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
|
|
251
|
+
// Model-scoped weekly limits (e.g. "Fable weekly limit at 86%"), rendered after the
|
|
252
|
+
// account-wide W bar. The /usage payload reports these in a `limits` array, each entry
|
|
253
|
+
// carrying the model in scope.model.display_name:
|
|
254
|
+
//
|
|
255
|
+
// { kind: "weekly_scoped", percent: 86, severity: "warning",
|
|
256
|
+
// resets_at: "...", scope: { model: { display_name: "Fable" } } }
|
|
257
|
+
//
|
|
258
|
+
// The label is the model's first initial (Fable -> F), so a new model family needs no
|
|
259
|
+
// code change. Older payloads instead exposed flat seven_day_<model> keys, kept below as
|
|
260
|
+
// a fallback for accounts still reporting that shape.
|
|
261
|
+
//
|
|
262
|
+
// NOTE: these appear only in the API payload. Claude Code's statusline stdin carries just
|
|
263
|
+
// five_hour and seven_day under rate_limits, so the scoped limits always come from the
|
|
264
|
+
// cache/API path even when stdin supplies the H and W bars.
|
|
265
|
+
const LEGACY_MODEL_WEEKLY_KEYS = [
|
|
266
|
+
{ key: 'seven_day_opus', label: 'O' },
|
|
267
|
+
{ key: 'seven_day_sonnet', label: 'S' }
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
// Build the usage segments from raw entries. fiveHour/weekly are { percentage, resetsAt }
|
|
271
|
+
// or null/absent; models is an array of { label, percentage, resetsAt } (possibly empty).
|
|
272
|
+
// Returns { current, weekly, models } — the first two rendered strings or null, models a
|
|
273
|
+
// (possibly empty) array of rendered strings. Scoped bars use getScopedColor instead of the
|
|
274
|
+
// H/W thresholds, so the full threshold palette stays exclusive to H/W.
|
|
275
|
+
function buildUsageBars(fiveHour, weekly, models) {
|
|
244
276
|
return {
|
|
245
277
|
current: fiveHour ? buildUsageBar('H', fiveHour.percentage, fiveHour.resetsAt) : null,
|
|
246
|
-
weekly: weekly ? buildUsageBar('W', weekly.percentage, weekly.resetsAt) : null
|
|
278
|
+
weekly: weekly ? buildUsageBar('W', weekly.percentage, weekly.resetsAt) : null,
|
|
279
|
+
models: (models || []).map(m => buildUsageBar(m.label, m.percentage, m.resetsAt, getScopedColor(m.percentage)))
|
|
247
280
|
};
|
|
248
281
|
}
|
|
249
282
|
|
|
@@ -255,11 +288,42 @@ function normalizePercentage(value) {
|
|
|
255
288
|
return Math.max(0, Math.min(100, Math.round(value)));
|
|
256
289
|
}
|
|
257
290
|
|
|
291
|
+
// Extract the model-scoped weekly limits from a raw /usage payload as
|
|
292
|
+
// [{ label, percentage, resetsAt }], in payload order. Prefers the `limits` array;
|
|
293
|
+
// falls back to the legacy flat keys only when it yields nothing, so an account
|
|
294
|
+
// reporting both shapes doesn't render the same limit twice.
|
|
295
|
+
function parseScopedLimits(usage) {
|
|
296
|
+
const scoped = [];
|
|
297
|
+
|
|
298
|
+
if (Array.isArray(usage?.limits)) {
|
|
299
|
+
for (const entry of usage.limits) {
|
|
300
|
+
if (!entry || entry.kind !== 'weekly_scoped') continue;
|
|
301
|
+
const name = entry.scope?.model?.display_name;
|
|
302
|
+
const pct = normalizePercentage(entry.percent);
|
|
303
|
+
if (typeof name !== 'string' || !name.trim() || pct == null) continue;
|
|
304
|
+
scoped.push({
|
|
305
|
+
label: name.trim().charAt(0).toUpperCase(),
|
|
306
|
+
percentage: pct,
|
|
307
|
+
resetsAt: entry.resets_at || null
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (scoped.length) return scoped;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
for (const { key, label } of LEGACY_MODEL_WEEKLY_KEYS) {
|
|
314
|
+
const seg = usage?.[key];
|
|
315
|
+
const pct = seg ? normalizePercentage(seg.utilization) : null;
|
|
316
|
+
if (pct != null) scoped.push({ label, percentage: pct, resetsAt: seg.resets_at || null });
|
|
317
|
+
}
|
|
318
|
+
return scoped;
|
|
319
|
+
}
|
|
320
|
+
|
|
258
321
|
// Build usage bars from stdin `rate_limits` (Claude.ai Pro/Max, present only after the
|
|
259
322
|
// first API response of a session). Same data as the OAuth usage API, so reading it here
|
|
260
323
|
// skips the network/credentials/cache path entirely. `resets_at` is a Unix epoch in
|
|
261
|
-
// SECONDS (not ISO) — ×1000 before Date. Returns {
|
|
262
|
-
// rate_limits is absent or the required five_hour segment is unusable (caller falls
|
|
324
|
+
// SECONDS (not ISO) — ×1000 before Date. Returns raw { fiveHour, weekly } entries, or null
|
|
325
|
+
// when rate_limits is absent or the required five_hour segment is unusable (caller falls
|
|
326
|
+
// back). Model-scoped weekly limits are never present here — see LEGACY_MODEL_WEEKLY_KEYS.
|
|
263
327
|
function buildUsageFromStdin(data) {
|
|
264
328
|
const rl = data?.rate_limits;
|
|
265
329
|
if (!rl) return null;
|
|
@@ -282,8 +346,7 @@ function buildUsageFromStdin(data) {
|
|
|
282
346
|
|
|
283
347
|
const fiveHour = toEntry(rl.five_hour);
|
|
284
348
|
if (!fiveHour) return null; // five_hour is the required bar
|
|
285
|
-
|
|
286
|
-
return buildUsageBars(fiveHour, weekly);
|
|
349
|
+
return { fiveHour, weekly: toEntry(rl.seven_day) };
|
|
287
350
|
}
|
|
288
351
|
|
|
289
352
|
// Validate a single usage entry ({ percentage, resetsAt }). Returns true only for a
|
|
@@ -305,13 +368,18 @@ function readCachedUsage() {
|
|
|
305
368
|
const cache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, 'utf8'));
|
|
306
369
|
if (!cache || !Number.isFinite(cache.timestamp) || cache.timestamp <= 0) return null;
|
|
307
370
|
|
|
308
|
-
// Validate data. fiveHour is required; weekly
|
|
309
|
-
// This also rejects the legacy single-{percentage,resetsAt} format from
|
|
310
|
-
// versions, which had no fiveHour key, so stale caches are ignored on read.
|
|
371
|
+
// Validate data. fiveHour is required; weekly and models are optional (the API may
|
|
372
|
+
// omit either). This also rejects the legacy single-{percentage,resetsAt} format from
|
|
373
|
+
// older versions, which had no fiveHour key, so stale caches are ignored on read.
|
|
374
|
+
// A cache written before model bars existed simply has no models key — still valid.
|
|
311
375
|
const data = cache.data;
|
|
312
376
|
if (!data || typeof data !== 'object') return null;
|
|
313
377
|
if (!isValidUsageEntry(data.fiveHour)) return null;
|
|
314
378
|
if (data.weekly != null && !isValidUsageEntry(data.weekly)) return null;
|
|
379
|
+
if (data.models != null) {
|
|
380
|
+
if (!Array.isArray(data.models)) return null;
|
|
381
|
+
if (!data.models.every(m => typeof m?.label === 'string' && isValidUsageEntry(m))) return null;
|
|
382
|
+
}
|
|
315
383
|
|
|
316
384
|
return { age: Date.now() - cache.timestamp, data };
|
|
317
385
|
} catch (e) {
|
|
@@ -410,9 +478,13 @@ function getApiUsage(callback) {
|
|
|
410
478
|
resetsAt: usage.seven_day.resets_at || null
|
|
411
479
|
} : null;
|
|
412
480
|
|
|
413
|
-
//
|
|
414
|
-
|
|
415
|
-
|
|
481
|
+
// Model-scoped weekly limits, rendered only when the account reports them.
|
|
482
|
+
const models = parseScopedLimits(usage);
|
|
483
|
+
|
|
484
|
+
// Cache the raw data (shared across sessions); callers render from it.
|
|
485
|
+
const resolved = { fiveHour, weekly, models };
|
|
486
|
+
setCachedUsage(resolved);
|
|
487
|
+
callback(resolved);
|
|
416
488
|
} else {
|
|
417
489
|
callback(null);
|
|
418
490
|
}
|
|
@@ -434,28 +506,42 @@ function getApiUsage(callback) {
|
|
|
434
506
|
}
|
|
435
507
|
}
|
|
436
508
|
|
|
437
|
-
//
|
|
438
|
-
function
|
|
509
|
+
// Resolve raw usage data ({ fiveHour, weekly, models }), cache-first. Callers render it.
|
|
510
|
+
function getRawUsage(callback) {
|
|
439
511
|
const cached = readCachedUsage();
|
|
440
512
|
|
|
441
|
-
// Cache is fresh ->
|
|
513
|
+
// Cache is fresh -> use it and skip the API entirely (fewer calls, faster).
|
|
442
514
|
if (cached && cached.age < FRESH_TTL_MS) {
|
|
443
|
-
return callback(
|
|
515
|
+
return callback(cached.data);
|
|
444
516
|
}
|
|
445
517
|
|
|
446
518
|
// Cache is stale or missing -> refresh from the API.
|
|
447
|
-
getApiUsage((
|
|
448
|
-
if (
|
|
449
|
-
callback(
|
|
519
|
+
getApiUsage((fresh) => {
|
|
520
|
+
if (fresh) {
|
|
521
|
+
callback(fresh);
|
|
450
522
|
} else if (cached && cached.age < STALE_TTL_MS) {
|
|
451
523
|
// API failed/timed out, but recent cache exists -> show it instead of nothing.
|
|
452
|
-
callback(
|
|
524
|
+
callback(cached.data);
|
|
453
525
|
} else {
|
|
454
526
|
callback(null);
|
|
455
527
|
}
|
|
456
528
|
});
|
|
457
529
|
}
|
|
458
530
|
|
|
531
|
+
// Get usage, cache-first, rendered.
|
|
532
|
+
function getUsageWithCache(callback) {
|
|
533
|
+
getRawUsage((data) => {
|
|
534
|
+
callback(data ? buildUsageBars(data.fiveHour, data.weekly, data.models) : null);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Model-scoped weekly limits only, cache-first. Used alongside the stdin H/W bars, which
|
|
539
|
+
// can't carry them. Falls back to the stale cache and finally to [] so a failed or slow
|
|
540
|
+
// call costs the scoped bars but never the bars stdin already gave us.
|
|
541
|
+
function getScopedModels(callback) {
|
|
542
|
+
getRawUsage((data) => callback(data?.models || []));
|
|
543
|
+
}
|
|
544
|
+
|
|
459
545
|
// Session cost from stdin `cost.total_cost_usd` (USD float, computed client-side by
|
|
460
546
|
// Claude Code as tokens × per-model API pricing). Pure stdin — no network/cache.
|
|
461
547
|
// Returns "$0.00" rendered dim, or '' when absent/non-finite so the segment is omitted.
|
|
@@ -535,6 +621,7 @@ function outputStatus(data, usage) {
|
|
|
535
621
|
const line2 = [];
|
|
536
622
|
if (usage?.current) line2.push(usage.current);
|
|
537
623
|
if (usage?.weekly) line2.push(usage.weekly);
|
|
624
|
+
if (usage?.models?.length) line2.push(...usage.models);
|
|
538
625
|
if (cost) line2.push(cost);
|
|
539
626
|
if (task) line2.push(`${colors.dim}${task}${colors.reset}`);
|
|
540
627
|
|
|
@@ -549,6 +636,7 @@ function outputFallback(usage) {
|
|
|
549
636
|
const parts = ['~', 'Claude', contextBar];
|
|
550
637
|
if (usage?.current) parts.push(usage.current);
|
|
551
638
|
if (usage?.weekly) parts.push(usage.weekly);
|
|
639
|
+
if (usage?.models?.length) parts.push(...usage.models);
|
|
552
640
|
process.stdout.write(parts.join(' \u2502 '));
|
|
553
641
|
}
|
|
554
642
|
|
|
@@ -561,7 +649,12 @@ function resolveUsage(data, callback) {
|
|
|
561
649
|
}
|
|
562
650
|
const fromStdin = buildUsageFromStdin(data);
|
|
563
651
|
if (fromStdin) {
|
|
564
|
-
|
|
652
|
+
// stdin covers H and W with no network. Model-scoped weekly limits only exist in the
|
|
653
|
+
// API payload, so they come from the cache — refreshed on the same TTL as every other
|
|
654
|
+
// usage read, which keeps at most one call per FRESH_TTL_MS regardless of render rate.
|
|
655
|
+
return getScopedModels((models) => {
|
|
656
|
+
callback(buildUsageBars(fromStdin.fiveHour, fromStdin.weekly, models));
|
|
657
|
+
});
|
|
565
658
|
}
|
|
566
659
|
getUsageWithCache(callback);
|
|
567
660
|
}
|
|
@@ -589,24 +682,31 @@ function emit(data) {
|
|
|
589
682
|
});
|
|
590
683
|
}
|
|
591
684
|
|
|
592
|
-
|
|
593
|
-
|
|
685
|
+
// Entry point, guarded so tests can require this file to exercise payload parsing
|
|
686
|
+
// directly (the /usage response shape is the easiest thing here to get wrong, and it
|
|
687
|
+
// can't be reached through stdin). Running the script normally is unchanged.
|
|
688
|
+
if (require.main === module) {
|
|
689
|
+
if (process.stdin.isTTY) {
|
|
690
|
+
emit(null);
|
|
691
|
+
} else {
|
|
692
|
+
let input = '';
|
|
693
|
+
let timeoutReached = false;
|
|
694
|
+
|
|
695
|
+
const overallTimeout = IS_API_KEY ? 500 : (fs.existsSync(USAGE_CACHE_FILE) ? 1300 : 1600);
|
|
696
|
+
|
|
697
|
+
const timeout = setTimeout(() => {
|
|
698
|
+
timeoutReached = true;
|
|
699
|
+
emit(parseInput(input));
|
|
700
|
+
}, overallTimeout);
|
|
701
|
+
|
|
702
|
+
process.stdin.setEncoding('utf8');
|
|
703
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
704
|
+
process.stdin.on('end', () => {
|
|
705
|
+
if (timeoutReached) return;
|
|
706
|
+
clearTimeout(timeout);
|
|
707
|
+
emit(parseInput(input));
|
|
708
|
+
});
|
|
709
|
+
}
|
|
594
710
|
} else {
|
|
595
|
-
|
|
596
|
-
let timeoutReached = false;
|
|
597
|
-
|
|
598
|
-
const overallTimeout = IS_API_KEY ? 500 : (fs.existsSync(USAGE_CACHE_FILE) ? 1300 : 1600);
|
|
599
|
-
|
|
600
|
-
const timeout = setTimeout(() => {
|
|
601
|
-
timeoutReached = true;
|
|
602
|
-
emit(parseInput(input));
|
|
603
|
-
}, overallTimeout);
|
|
604
|
-
|
|
605
|
-
process.stdin.setEncoding('utf8');
|
|
606
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
607
|
-
process.stdin.on('end', () => {
|
|
608
|
-
if (timeoutReached) return;
|
|
609
|
-
clearTimeout(timeout);
|
|
610
|
-
emit(parseInput(input));
|
|
611
|
-
});
|
|
711
|
+
module.exports = { parseScopedLimits, normalizePercentage };
|
|
612
712
|
}
|