@claudinho/cli 0.8.7 → 0.8.9
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 +92 -15
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2762,6 +2762,10 @@ var schedule_2026_default = [
|
|
|
2762
2762
|
updatedAt: "2026-06-24T03:57:24.994Z"
|
|
2763
2763
|
}
|
|
2764
2764
|
];
|
|
2765
|
+
var PLACEHOLDER_FLAG = "\u{1F3F3}\uFE0F";
|
|
2766
|
+
function isResolvedNation(team) {
|
|
2767
|
+
return team.flag !== PLACEHOLDER_FLAG;
|
|
2768
|
+
}
|
|
2765
2769
|
var SCHEDULE = schedule_2026_default;
|
|
2766
2770
|
function allFixtures() {
|
|
2767
2771
|
return SCHEDULE;
|
|
@@ -3858,6 +3862,20 @@ async function getNextFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Da
|
|
|
3858
3862
|
const source = fixture && liveById?.has(fixture.id) ? adapter.name : void 0;
|
|
3859
3863
|
return { fixture, degraded, source };
|
|
3860
3864
|
}
|
|
3865
|
+
async function getKnockoutFixtures(adapter, now = /* @__PURE__ */ new Date()) {
|
|
3866
|
+
if (!adapter.fetchWindow) return { fixtures: [], degraded: true };
|
|
3867
|
+
let live;
|
|
3868
|
+
try {
|
|
3869
|
+
live = await adapter.fetchWindow(KNOCKOUT_WINDOW_START, KNOCKOUT_WINDOW_END);
|
|
3870
|
+
} catch {
|
|
3871
|
+
return { fixtures: [], degraded: true };
|
|
3872
|
+
}
|
|
3873
|
+
const nowMs = now.getTime();
|
|
3874
|
+
const fixtures = live.filter(
|
|
3875
|
+
(m) => m.stage !== "GROUP" && m.stage !== "FRIENDLY" && Date.parse(m.kickoff) >= nowMs && isResolvedNation(m.home) && isResolvedNation(m.away)
|
|
3876
|
+
).sort(byKickoff);
|
|
3877
|
+
return { fixtures, degraded: false };
|
|
3878
|
+
}
|
|
3861
3879
|
async function getMatchById(adapter, id) {
|
|
3862
3880
|
const base = allFixtures().find((m) => m.id === id);
|
|
3863
3881
|
if (!base) return { match: void 0, degraded: false };
|
|
@@ -4955,6 +4973,11 @@ function ageMs(state, now = Date.now()) {
|
|
|
4955
4973
|
const t2 = Date.parse(state.updatedAt);
|
|
4956
4974
|
return Number.isFinite(t2) ? now - t2 : Infinity;
|
|
4957
4975
|
}
|
|
4976
|
+
function fixturesAgeMs(state, now = Date.now()) {
|
|
4977
|
+
if (!state?.fixturesUpdatedAt) return Infinity;
|
|
4978
|
+
const t2 = Date.parse(state.fixturesUpdatedAt);
|
|
4979
|
+
return Number.isFinite(t2) ? now - t2 : Infinity;
|
|
4980
|
+
}
|
|
4958
4981
|
function lockAgeMs(now = Date.now()) {
|
|
4959
4982
|
const lp = lockPath();
|
|
4960
4983
|
try {
|
|
@@ -5026,8 +5049,11 @@ var LIVE_TTL_MS = 15e3;
|
|
|
5026
5049
|
function inLiveWindow(now = Date.now(), fixtures = allFixtures()) {
|
|
5027
5050
|
return fixturesInLiveWindow(now, fixtures).length > 0;
|
|
5028
5051
|
}
|
|
5052
|
+
function isResolvedFixture(m) {
|
|
5053
|
+
return isResolvedNation(m.home) && isResolvedNation(m.away);
|
|
5054
|
+
}
|
|
5029
5055
|
function nextOverall(now, fixtures = allFixtures()) {
|
|
5030
|
-
return [...fixtures].sort(byKickoff).find((m) => Date.parse(m.kickoff) >= now);
|
|
5056
|
+
return [...fixtures].sort(byKickoff).find((m) => Date.parse(m.kickoff) >= now && isResolvedFixture(m));
|
|
5031
5057
|
}
|
|
5032
5058
|
function teamTok(t2, flags) {
|
|
5033
5059
|
return flags ? t2.flag : t2.code;
|
|
@@ -5055,6 +5081,8 @@ function renderPrompt(state, opts = {}) {
|
|
|
5055
5081
|
const flags = opts.flags ?? true;
|
|
5056
5082
|
const team = opts.team?.toUpperCase();
|
|
5057
5083
|
const live = liveMatchesFromCache(state, nowMs);
|
|
5084
|
+
const cachedFixtures = Array.isArray(state?.fixtures) ? state.fixtures : [];
|
|
5085
|
+
const schedule = cachedFixtures.length ? mergeLive(allFixtures(), cachedFixtures) : void 0;
|
|
5058
5086
|
if (team) {
|
|
5059
5087
|
const mine = live.find((m) => m.home?.code === team || m.away?.code === team);
|
|
5060
5088
|
if (mine) return `\u26BD ${matchSegment(mine, compact, flags)}`;
|
|
@@ -5068,17 +5096,18 @@ function renderPrompt(state, opts = {}) {
|
|
|
5068
5096
|
}
|
|
5069
5097
|
const cacheFresh = !!state && state.degraded !== true && ageMs(state, nowMs) < DISPLAY_STALE_MS;
|
|
5070
5098
|
if (!cacheFresh) {
|
|
5071
|
-
const win = fixturesInLiveWindow(nowMs).filter(
|
|
5099
|
+
const win = fixturesInLiveWindow(nowMs, schedule).filter(
|
|
5072
5100
|
(m) => !team || m.home.code === team || m.away.code === team
|
|
5073
5101
|
);
|
|
5074
5102
|
const first = win[0];
|
|
5075
5103
|
if (first) {
|
|
5076
5104
|
const more = win.length - 1;
|
|
5077
|
-
|
|
5105
|
+
const matchup = isResolvedFixture(first) ? `${teamTok(first.home, flags)} vs ${teamTok(first.away, flags)} ` : "";
|
|
5106
|
+
return `\u26BD ${matchup}live \xB7 syncing\u2026` + (more > 0 ? ` +${more}` : "");
|
|
5078
5107
|
}
|
|
5079
5108
|
}
|
|
5080
|
-
const next = team ? nextFixtureForTeam(team, { from: now }) : nextOverall(nowMs);
|
|
5081
|
-
if (next) {
|
|
5109
|
+
const next = team ? nextFixtureForTeam(team, { from: now, fixtures: schedule }) : nextOverall(nowMs, schedule);
|
|
5110
|
+
if (next && isResolvedFixture(next)) {
|
|
5082
5111
|
return `${teamTok(next.home, flags)} vs ${teamTok(next.away, flags)} in ${countdown(next.kickoff, now)}`;
|
|
5083
5112
|
}
|
|
5084
5113
|
return "\u26BD \u2014";
|
|
@@ -5112,6 +5141,20 @@ ${lines}`;
|
|
|
5112
5141
|
// src/refresh.ts
|
|
5113
5142
|
import { spawn } from "child_process";
|
|
5114
5143
|
var MIN_REFRESH_MS = 12e3;
|
|
5144
|
+
var FIXTURES_TTL_MS = 15 * 6e4;
|
|
5145
|
+
var FIXTURES_EMPTY_TTL_MS = 6e4;
|
|
5146
|
+
function fixturesStale(state, now) {
|
|
5147
|
+
const ttl = (state?.fixtures?.length ?? 0) > 0 ? FIXTURES_TTL_MS : FIXTURES_EMPTY_TTL_MS;
|
|
5148
|
+
return fixturesAgeMs(state, now) > ttl;
|
|
5149
|
+
}
|
|
5150
|
+
function nextStaticUpcoming(nowMs) {
|
|
5151
|
+
return [...allFixtures()].sort(byKickoff).find((m) => Date.parse(m.kickoff) >= nowMs);
|
|
5152
|
+
}
|
|
5153
|
+
function inKnockoutPhase(nowMs) {
|
|
5154
|
+
if (resolveCompetition() !== DEFAULT_COMPETITION) return false;
|
|
5155
|
+
const next = nextStaticUpcoming(nowMs);
|
|
5156
|
+
return !!next && next.stage !== "GROUP" && next.stage !== "FRIENDLY";
|
|
5157
|
+
}
|
|
5115
5158
|
function liveWindowActive(nowMs) {
|
|
5116
5159
|
if (resolveCompetition() !== DEFAULT_COMPETITION) return true;
|
|
5117
5160
|
return inLiveWindow(nowMs);
|
|
@@ -5125,17 +5168,23 @@ function liveAdapter() {
|
|
|
5125
5168
|
}
|
|
5126
5169
|
async function runRefresh(opts = {}) {
|
|
5127
5170
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
5171
|
+
const nowMs = now.getTime();
|
|
5128
5172
|
const source = opts.source ?? "espn";
|
|
5129
5173
|
const competition = resolveCompetition();
|
|
5130
5174
|
const cached = readState();
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5175
|
+
const sameScope = !!cached && cached.source === source && cached.competition === competition;
|
|
5176
|
+
const base = sameScope ? cached : void 0;
|
|
5177
|
+
const needLive = liveWindowActive(nowMs) && (!base || ageMs(base, nowMs) >= MIN_REFRESH_MS);
|
|
5178
|
+
const needFixtures = inKnockoutPhase(nowMs) && fixturesStale(base, nowMs);
|
|
5179
|
+
if (!needLive && !needFixtures) return;
|
|
5134
5180
|
if (!acquireLock()) return;
|
|
5135
5181
|
try {
|
|
5136
|
-
let live = [];
|
|
5137
|
-
let degraded = false;
|
|
5138
|
-
|
|
5182
|
+
let live = base?.live ?? [];
|
|
5183
|
+
let degraded = base?.degraded ?? false;
|
|
5184
|
+
let updatedAt = base?.updatedAt ?? now.toISOString();
|
|
5185
|
+
let fixtures = base?.fixtures;
|
|
5186
|
+
let fixturesUpdatedAt = base?.fixturesUpdatedAt;
|
|
5187
|
+
if (needLive) {
|
|
5139
5188
|
try {
|
|
5140
5189
|
const r = await getLiveMatches(liveAdapter(), now);
|
|
5141
5190
|
live = r.matches;
|
|
@@ -5143,8 +5192,27 @@ async function runRefresh(opts = {}) {
|
|
|
5143
5192
|
} catch {
|
|
5144
5193
|
degraded = true;
|
|
5145
5194
|
}
|
|
5195
|
+
updatedAt = now.toISOString();
|
|
5196
|
+
}
|
|
5197
|
+
if (needFixtures) {
|
|
5198
|
+
try {
|
|
5199
|
+
const r = await getKnockoutFixtures(liveAdapter(), now);
|
|
5200
|
+
if (!r.degraded) {
|
|
5201
|
+
fixtures = r.fixtures;
|
|
5202
|
+
fixturesUpdatedAt = now.toISOString();
|
|
5203
|
+
}
|
|
5204
|
+
} catch {
|
|
5205
|
+
}
|
|
5146
5206
|
}
|
|
5147
|
-
writeState({
|
|
5207
|
+
writeState({
|
|
5208
|
+
updatedAt,
|
|
5209
|
+
live,
|
|
5210
|
+
degraded,
|
|
5211
|
+
source,
|
|
5212
|
+
competition,
|
|
5213
|
+
...fixtures ? { fixtures } : {},
|
|
5214
|
+
...fixturesUpdatedAt ? { fixturesUpdatedAt } : {}
|
|
5215
|
+
});
|
|
5148
5216
|
} finally {
|
|
5149
5217
|
releaseLock();
|
|
5150
5218
|
}
|
|
@@ -5154,6 +5222,11 @@ function shouldRefresh(now = Date.now()) {
|
|
|
5154
5222
|
if (isLockFresh(now)) return false;
|
|
5155
5223
|
return ageMs(readState(), now) > LIVE_TTL_MS;
|
|
5156
5224
|
}
|
|
5225
|
+
function shouldRefreshFixtures(now = Date.now(), state = readState()) {
|
|
5226
|
+
if (!inKnockoutPhase(now)) return false;
|
|
5227
|
+
if (isLockFresh(now)) return false;
|
|
5228
|
+
return fixturesStale(state, now);
|
|
5229
|
+
}
|
|
5157
5230
|
function spawnRefresh(source) {
|
|
5158
5231
|
try {
|
|
5159
5232
|
const entry = process.argv[1];
|
|
@@ -5654,7 +5727,9 @@ function cmdPrompt({ cfg }) {
|
|
|
5654
5727
|
const state = readCurrentState(cfg.source, resolveCompetition());
|
|
5655
5728
|
const scoreLine = renderPrompt(state, { team, compact, max, flags: flagsEnabled() });
|
|
5656
5729
|
out(renderPromptOutput(scoreLine, payload));
|
|
5657
|
-
if (!state || shouldRefresh()
|
|
5730
|
+
if (!state || shouldRefresh() || shouldRefreshFixtures(Date.now(), state)) {
|
|
5731
|
+
spawnRefresh(cfg.source);
|
|
5732
|
+
}
|
|
5658
5733
|
} catch {
|
|
5659
5734
|
out("");
|
|
5660
5735
|
}
|
|
@@ -5665,7 +5740,9 @@ function cmdHook({ cfg }) {
|
|
|
5665
5740
|
const state = readCurrentState(cfg.source, resolveCompetition());
|
|
5666
5741
|
const ctx = renderHook(state, { team, flags: flagsEnabled() });
|
|
5667
5742
|
if (ctx) out(ctx);
|
|
5668
|
-
if (!state || shouldRefresh()
|
|
5743
|
+
if (!state || shouldRefresh() || shouldRefreshFixtures(Date.now(), state)) {
|
|
5744
|
+
spawnRefresh(cfg.source);
|
|
5745
|
+
}
|
|
5669
5746
|
} catch {
|
|
5670
5747
|
}
|
|
5671
5748
|
}
|
|
@@ -6234,7 +6311,7 @@ function handlePipeError(stream) {
|
|
|
6234
6311
|
}
|
|
6235
6312
|
handlePipeError(process.stdout);
|
|
6236
6313
|
handlePipeError(process.stderr);
|
|
6237
|
-
var VERSION = "0.8.
|
|
6314
|
+
var VERSION = "0.8.9";
|
|
6238
6315
|
var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
|
|
6239
6316
|
function ctxFrom(cmd) {
|
|
6240
6317
|
let root = cmd;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
4
4
|
"description": "Live scores, fixtures, group tables, and read-only prediction-market signals for the 2026 men's football tournament — in your terminal, Claude Code, and Cursor CLI statusline. No API keys. Not affiliated with FIFA or Anthropic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"tsup": "^8.0.0",
|
|
62
62
|
"typescript": "^5.7.0",
|
|
63
63
|
"vitest": "^4.1.9",
|
|
64
|
-
"@claudinho/core": "0.8.
|
|
64
|
+
"@claudinho/core": "0.8.9"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsup",
|