@claudinho/cli 0.8.7 → 0.8.8
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 +81 -12
- 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;
|
|
@@ -5077,8 +5103,10 @@ function renderPrompt(state, opts = {}) {
|
|
|
5077
5103
|
return `\u26BD ${teamTok(first.home, flags)} vs ${teamTok(first.away, flags)} live \xB7 syncing\u2026` + (more > 0 ? ` +${more}` : "");
|
|
5078
5104
|
}
|
|
5079
5105
|
}
|
|
5080
|
-
const
|
|
5081
|
-
|
|
5106
|
+
const cachedFixtures = Array.isArray(state?.fixtures) ? state.fixtures : [];
|
|
5107
|
+
const schedule = cachedFixtures.length ? mergeLive(allFixtures(), cachedFixtures) : void 0;
|
|
5108
|
+
const next = team ? nextFixtureForTeam(team, { from: now, fixtures: schedule }) : nextOverall(nowMs, schedule);
|
|
5109
|
+
if (next && isResolvedFixture(next)) {
|
|
5082
5110
|
return `${teamTok(next.home, flags)} vs ${teamTok(next.away, flags)} in ${countdown(next.kickoff, now)}`;
|
|
5083
5111
|
}
|
|
5084
5112
|
return "\u26BD \u2014";
|
|
@@ -5112,6 +5140,15 @@ ${lines}`;
|
|
|
5112
5140
|
// src/refresh.ts
|
|
5113
5141
|
import { spawn } from "child_process";
|
|
5114
5142
|
var MIN_REFRESH_MS = 12e3;
|
|
5143
|
+
var FIXTURES_TTL_MS = 15 * 6e4;
|
|
5144
|
+
function nextStaticUpcoming(nowMs) {
|
|
5145
|
+
return [...allFixtures()].sort(byKickoff).find((m) => Date.parse(m.kickoff) >= nowMs);
|
|
5146
|
+
}
|
|
5147
|
+
function inKnockoutPhase(nowMs) {
|
|
5148
|
+
if (resolveCompetition() !== DEFAULT_COMPETITION) return false;
|
|
5149
|
+
const next = nextStaticUpcoming(nowMs);
|
|
5150
|
+
return !!next && next.stage !== "GROUP" && next.stage !== "FRIENDLY";
|
|
5151
|
+
}
|
|
5115
5152
|
function liveWindowActive(nowMs) {
|
|
5116
5153
|
if (resolveCompetition() !== DEFAULT_COMPETITION) return true;
|
|
5117
5154
|
return inLiveWindow(nowMs);
|
|
@@ -5125,17 +5162,23 @@ function liveAdapter() {
|
|
|
5125
5162
|
}
|
|
5126
5163
|
async function runRefresh(opts = {}) {
|
|
5127
5164
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
5165
|
+
const nowMs = now.getTime();
|
|
5128
5166
|
const source = opts.source ?? "espn";
|
|
5129
5167
|
const competition = resolveCompetition();
|
|
5130
5168
|
const cached = readState();
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5169
|
+
const sameScope = !!cached && cached.source === source && cached.competition === competition;
|
|
5170
|
+
const base = sameScope ? cached : void 0;
|
|
5171
|
+
const needLive = liveWindowActive(nowMs) && (!base || ageMs(base, nowMs) >= MIN_REFRESH_MS);
|
|
5172
|
+
const needFixtures = inKnockoutPhase(nowMs) && (!base || fixturesAgeMs(base, nowMs) >= FIXTURES_TTL_MS);
|
|
5173
|
+
if (!needLive && !needFixtures) return;
|
|
5134
5174
|
if (!acquireLock()) return;
|
|
5135
5175
|
try {
|
|
5136
|
-
let live = [];
|
|
5137
|
-
let degraded = false;
|
|
5138
|
-
|
|
5176
|
+
let live = base?.live ?? [];
|
|
5177
|
+
let degraded = base?.degraded ?? false;
|
|
5178
|
+
let updatedAt = base?.updatedAt ?? now.toISOString();
|
|
5179
|
+
let fixtures = base?.fixtures;
|
|
5180
|
+
let fixturesUpdatedAt = base?.fixturesUpdatedAt;
|
|
5181
|
+
if (needLive) {
|
|
5139
5182
|
try {
|
|
5140
5183
|
const r = await getLiveMatches(liveAdapter(), now);
|
|
5141
5184
|
live = r.matches;
|
|
@@ -5143,8 +5186,27 @@ async function runRefresh(opts = {}) {
|
|
|
5143
5186
|
} catch {
|
|
5144
5187
|
degraded = true;
|
|
5145
5188
|
}
|
|
5189
|
+
updatedAt = now.toISOString();
|
|
5190
|
+
}
|
|
5191
|
+
if (needFixtures) {
|
|
5192
|
+
try {
|
|
5193
|
+
const r = await getKnockoutFixtures(liveAdapter(), now);
|
|
5194
|
+
if (!r.degraded) {
|
|
5195
|
+
fixtures = r.fixtures;
|
|
5196
|
+
fixturesUpdatedAt = now.toISOString();
|
|
5197
|
+
}
|
|
5198
|
+
} catch {
|
|
5199
|
+
}
|
|
5146
5200
|
}
|
|
5147
|
-
writeState({
|
|
5201
|
+
writeState({
|
|
5202
|
+
updatedAt,
|
|
5203
|
+
live,
|
|
5204
|
+
degraded,
|
|
5205
|
+
source,
|
|
5206
|
+
competition,
|
|
5207
|
+
...fixtures ? { fixtures } : {},
|
|
5208
|
+
...fixturesUpdatedAt ? { fixturesUpdatedAt } : {}
|
|
5209
|
+
});
|
|
5148
5210
|
} finally {
|
|
5149
5211
|
releaseLock();
|
|
5150
5212
|
}
|
|
@@ -5154,6 +5216,11 @@ function shouldRefresh(now = Date.now()) {
|
|
|
5154
5216
|
if (isLockFresh(now)) return false;
|
|
5155
5217
|
return ageMs(readState(), now) > LIVE_TTL_MS;
|
|
5156
5218
|
}
|
|
5219
|
+
function shouldRefreshFixtures(now = Date.now(), state = readState()) {
|
|
5220
|
+
if (!inKnockoutPhase(now)) return false;
|
|
5221
|
+
if (isLockFresh(now)) return false;
|
|
5222
|
+
return fixturesAgeMs(state, now) > FIXTURES_TTL_MS;
|
|
5223
|
+
}
|
|
5157
5224
|
function spawnRefresh(source) {
|
|
5158
5225
|
try {
|
|
5159
5226
|
const entry = process.argv[1];
|
|
@@ -5654,7 +5721,9 @@ function cmdPrompt({ cfg }) {
|
|
|
5654
5721
|
const state = readCurrentState(cfg.source, resolveCompetition());
|
|
5655
5722
|
const scoreLine = renderPrompt(state, { team, compact, max, flags: flagsEnabled() });
|
|
5656
5723
|
out(renderPromptOutput(scoreLine, payload));
|
|
5657
|
-
if (!state || shouldRefresh()
|
|
5724
|
+
if (!state || shouldRefresh() || shouldRefreshFixtures(Date.now(), state)) {
|
|
5725
|
+
spawnRefresh(cfg.source);
|
|
5726
|
+
}
|
|
5658
5727
|
} catch {
|
|
5659
5728
|
out("");
|
|
5660
5729
|
}
|
|
@@ -6234,7 +6303,7 @@ function handlePipeError(stream) {
|
|
|
6234
6303
|
}
|
|
6235
6304
|
handlePipeError(process.stdout);
|
|
6236
6305
|
handlePipeError(process.stderr);
|
|
6237
|
-
var VERSION = "0.8.
|
|
6306
|
+
var VERSION = "0.8.8";
|
|
6238
6307
|
var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
|
|
6239
6308
|
function ctxFrom(cmd) {
|
|
6240
6309
|
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.8",
|
|
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.8"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsup",
|