@claudinho/cli 0.9.4 → 0.10.1
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 +645 -197
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -274,6 +274,8 @@ var EN = {
|
|
|
274
274
|
"bracket.slot.tbd": "TBD",
|
|
275
275
|
"live.data": "Live data: {source}",
|
|
276
276
|
"standings.unavailable": "Live standings unavailable.",
|
|
277
|
+
"standings.partial": "Partial table \u2014 {n} rows could not be read.",
|
|
278
|
+
"competition.unsupported": "Not available for this competition yet.",
|
|
277
279
|
"share.tryIt": "Try it: {line}",
|
|
278
280
|
"stage.group": "Group {group}",
|
|
279
281
|
"stage.groupStage": "Group stage",
|
|
@@ -304,6 +306,8 @@ var ES = {
|
|
|
304
306
|
"bracket.slot.tbd": "Por definir",
|
|
305
307
|
"live.data": "Datos en vivo: {source}",
|
|
306
308
|
"standings.unavailable": "Tabla en vivo no disponible.",
|
|
309
|
+
"standings.partial": "Tabla parcial \u2014 no se pudieron leer {n} filas.",
|
|
310
|
+
"competition.unsupported": "A\xFAn no disponible para esta competici\xF3n.",
|
|
307
311
|
"share.tryIt": "Pru\xE9balo: {line}",
|
|
308
312
|
"stage.group": "Grupo {group}",
|
|
309
313
|
"stage.groupStage": "Fase de grupos",
|
|
@@ -334,6 +338,8 @@ var PT = {
|
|
|
334
338
|
"bracket.slot.tbd": "A definir",
|
|
335
339
|
"live.data": "Dados ao vivo: {source}",
|
|
336
340
|
"standings.unavailable": "Classifica\xE7\xE3o ao vivo indispon\xEDvel.",
|
|
341
|
+
"standings.partial": "Tabela parcial \u2014 {n} linhas n\xE3o puderam ser lidas.",
|
|
342
|
+
"competition.unsupported": "Ainda n\xE3o dispon\xEDvel para esta competi\xE7\xE3o.",
|
|
337
343
|
"share.tryIt": "Experimente: {line}",
|
|
338
344
|
"stage.group": "Grupo {group}",
|
|
339
345
|
"stage.groupStage": "Fase de grupos",
|
|
@@ -364,6 +370,8 @@ var FR = {
|
|
|
364
370
|
"bracket.slot.tbd": "\xC0 d\xE9finir",
|
|
365
371
|
"live.data": "Donn\xE9es en direct : {source}",
|
|
366
372
|
"standings.unavailable": "Classement en direct indisponible.",
|
|
373
|
+
"standings.partial": "Classement partiel \u2014 {n} lignes n'ont pas pu \xEAtre lues.",
|
|
374
|
+
"competition.unsupported": "Pas encore disponible pour cette comp\xE9tition.",
|
|
367
375
|
"share.tryIt": "Essayez : {line}",
|
|
368
376
|
"stage.group": "Groupe {group}",
|
|
369
377
|
"stage.groupStage": "Phase de groupes",
|
|
@@ -2848,11 +2856,13 @@ function fixturesByGroup(group, fixtures = SCHEDULE) {
|
|
|
2848
2856
|
const g = group.toUpperCase();
|
|
2849
2857
|
return fixtures.filter((m) => (m.group ?? "").toUpperCase() === g).sort(byKickoff);
|
|
2850
2858
|
}
|
|
2859
|
+
function isUpcoming(m, now = /* @__PURE__ */ new Date()) {
|
|
2860
|
+
if (m.status === "CANCELLED" || m.status === "POSTPONED") return false;
|
|
2861
|
+
return Date.parse(m.kickoff) >= now.getTime();
|
|
2862
|
+
}
|
|
2851
2863
|
function nextFixtureForTeam(code, opts = {}) {
|
|
2852
2864
|
const from = opts.from ?? /* @__PURE__ */ new Date();
|
|
2853
|
-
return fixturesByTeam(code, opts.fixtures ?? SCHEDULE).find(
|
|
2854
|
-
(m) => new Date(m.kickoff).getTime() >= from.getTime()
|
|
2855
|
-
);
|
|
2865
|
+
return fixturesByTeam(code, opts.fixtures ?? SCHEDULE).find((m) => isUpcoming(m, from));
|
|
2856
2866
|
}
|
|
2857
2867
|
var LIVE_WINDOW_MS = 140 * 6e4;
|
|
2858
2868
|
var KNOCKOUT_EXTRA_TIME_MS = 60 * 6e4;
|
|
@@ -2980,8 +2990,8 @@ function humanLabel(value, maxColumns = MAX_LABEL_COLUMNS) {
|
|
|
2980
2990
|
const capped = value.length > MAX_LABEL_INPUT_UNITS ? value.slice(0, MAX_LABEL_INPUT_UNITS) : value;
|
|
2981
2991
|
return visible(runToFixedPoint(capped, maxColumns));
|
|
2982
2992
|
}
|
|
2983
|
-
function visible(
|
|
2984
|
-
return
|
|
2993
|
+
function visible(label2) {
|
|
2994
|
+
return label2 !== "" && displayWidth(label2) === 0 ? "" : label2;
|
|
2985
2995
|
}
|
|
2986
2996
|
function runToFixedPoint(capped, maxColumns) {
|
|
2987
2997
|
const first = sealLabelOnce(capped, maxColumns);
|
|
@@ -3209,7 +3219,7 @@ function toParticipant(raw) {
|
|
|
3209
3219
|
const providerId = opaqueId(raw.team?.id, ESPN_ID);
|
|
3210
3220
|
const known = productFlag(name) !== nationToFlag("");
|
|
3211
3221
|
return valid(
|
|
3212
|
-
providerId && known ? { kind: "team", providerId, team } : { kind: "slot", team }
|
|
3222
|
+
providerId && known ? { kind: "team", providerId, team } : { kind: "slot", ...providerId ? { providerId } : {}, team }
|
|
3213
3223
|
);
|
|
3214
3224
|
}
|
|
3215
3225
|
function mapStatus(st) {
|
|
@@ -3278,7 +3288,7 @@ function parseEspnEvent(raw, ctx = {}) {
|
|
|
3278
3288
|
if (awayP.kind !== "valid") return awayP;
|
|
3279
3289
|
const h = homeP.value;
|
|
3280
3290
|
const a = awayP.value;
|
|
3281
|
-
if (h.
|
|
3291
|
+
if (h.providerId !== void 0 && h.providerId === a.providerId) {
|
|
3282
3292
|
return definitiveNone("both competitors are the same team");
|
|
3283
3293
|
}
|
|
3284
3294
|
const home = homeP.value.team;
|
|
@@ -3430,9 +3440,13 @@ function parseEspnStandings(raw) {
|
|
|
3430
3440
|
const seenGroups = /* @__PURE__ */ new Set();
|
|
3431
3441
|
const seenProviderIds = /* @__PURE__ */ new Set();
|
|
3432
3442
|
for (const child of children) {
|
|
3433
|
-
const
|
|
3434
|
-
const letter =
|
|
3435
|
-
if (!letter)
|
|
3443
|
+
const label2 = humanLabel(child?.name ?? child?.abbreviation);
|
|
3444
|
+
const letter = label2.match(/Group\s+([A-L])(?![A-Za-z0-9])/i)?.[1]?.toUpperCase();
|
|
3445
|
+
if (!letter) {
|
|
3446
|
+
const rows = child?.standings?.entries;
|
|
3447
|
+
if (Array.isArray(rows) && rows.length > 0) complete = false;
|
|
3448
|
+
continue;
|
|
3449
|
+
}
|
|
3436
3450
|
if (seenGroups.has(letter)) {
|
|
3437
3451
|
complete = false;
|
|
3438
3452
|
continue;
|
|
@@ -3443,30 +3457,37 @@ function parseEspnStandings(raw) {
|
|
|
3443
3457
|
complete = false;
|
|
3444
3458
|
continue;
|
|
3445
3459
|
}
|
|
3460
|
+
let omitted = 0;
|
|
3446
3461
|
if (rawEntries.length > MAX_GROUP_ROWS) {
|
|
3447
3462
|
rowsTruncated = true;
|
|
3448
3463
|
complete = false;
|
|
3464
|
+
omitted += rawEntries.length - MAX_GROUP_ROWS;
|
|
3449
3465
|
}
|
|
3450
3466
|
const entries = takeBounded(rawEntries, MAX_GROUP_ROWS);
|
|
3451
|
-
const
|
|
3467
|
+
const seenCodes = /* @__PURE__ */ new Map();
|
|
3452
3468
|
const seenRanks = /* @__PURE__ */ new Set();
|
|
3453
3469
|
const ranked = [];
|
|
3454
3470
|
for (const e of entries) {
|
|
3455
3471
|
const r = entryToRow(e);
|
|
3456
3472
|
if (r.kind !== "valid") {
|
|
3457
3473
|
if (r.kind !== "definitive-none") complete = false;
|
|
3474
|
+
omitted += 1;
|
|
3458
3475
|
continue;
|
|
3459
3476
|
}
|
|
3460
|
-
const
|
|
3461
|
-
|
|
3477
|
+
const { providerId, providerRank } = r.value;
|
|
3478
|
+
const code = r.value.team.code;
|
|
3479
|
+
const priorHadId = seenCodes.get(code);
|
|
3480
|
+
const codeCollision = priorHadId !== void 0 && (providerId === void 0 || priorHadId === false);
|
|
3481
|
+
if (codeCollision || seenRanks.has(providerRank) || providerId !== void 0 && seenProviderIds.has(providerId)) {
|
|
3462
3482
|
complete = false;
|
|
3483
|
+
omitted += 1;
|
|
3463
3484
|
continue;
|
|
3464
3485
|
}
|
|
3465
|
-
|
|
3466
|
-
seenRanks.add(
|
|
3467
|
-
if (
|
|
3486
|
+
seenCodes.set(code, providerId !== void 0);
|
|
3487
|
+
seenRanks.add(providerRank);
|
|
3488
|
+
if (providerId !== void 0) seenProviderIds.add(providerId);
|
|
3468
3489
|
const { providerId: _dropId, providerRank: rank, ...row } = r.value;
|
|
3469
|
-
ranked.push({ row, rank });
|
|
3490
|
+
ranked.push({ row: { ...row, rank }, rank });
|
|
3470
3491
|
}
|
|
3471
3492
|
ranked.sort((a, b) => {
|
|
3472
3493
|
if (a.rank && b.rank && a.rank !== b.rank) return a.rank - b.rank;
|
|
@@ -3478,7 +3499,11 @@ function parseEspnStandings(raw) {
|
|
|
3478
3499
|
complete = false;
|
|
3479
3500
|
continue;
|
|
3480
3501
|
}
|
|
3481
|
-
out2.push({
|
|
3502
|
+
out2.push({
|
|
3503
|
+
group: letter,
|
|
3504
|
+
rows: ranked.map((x) => x.row),
|
|
3505
|
+
...omitted > 0 ? { partial: { omitted } } : {}
|
|
3506
|
+
});
|
|
3482
3507
|
}
|
|
3483
3508
|
return {
|
|
3484
3509
|
items: out2,
|
|
@@ -3489,11 +3514,72 @@ function parseEspnStandings(raw) {
|
|
|
3489
3514
|
complete: complete && !rowsTruncated
|
|
3490
3515
|
};
|
|
3491
3516
|
}
|
|
3517
|
+
var ResponseTooLargeError = class extends Error {
|
|
3518
|
+
constructor(bytes, limit) {
|
|
3519
|
+
super(`response body exceeds ${limit} bytes (${bytes} seen)`);
|
|
3520
|
+
this.bytes = bytes;
|
|
3521
|
+
this.limit = limit;
|
|
3522
|
+
this.name = "ResponseTooLargeError";
|
|
3523
|
+
}
|
|
3524
|
+
bytes;
|
|
3525
|
+
limit;
|
|
3526
|
+
};
|
|
3527
|
+
function isStream(v) {
|
|
3528
|
+
return typeof v?.getReader === "function";
|
|
3529
|
+
}
|
|
3530
|
+
async function readJsonBounded(res, maxBytes) {
|
|
3531
|
+
const r = res;
|
|
3532
|
+
const declared = Number(r.headers?.get?.("content-length"));
|
|
3533
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
3534
|
+
if (isStream(r.body)) await r.body.cancel().catch(() => {
|
|
3535
|
+
});
|
|
3536
|
+
throw new ResponseTooLargeError(declared, maxBytes);
|
|
3537
|
+
}
|
|
3538
|
+
if (isStream(r.body)) {
|
|
3539
|
+
const reader = r.body.getReader();
|
|
3540
|
+
const chunks = [];
|
|
3541
|
+
let total = 0;
|
|
3542
|
+
for (; ; ) {
|
|
3543
|
+
const { done, value } = await reader.read();
|
|
3544
|
+
if (done) break;
|
|
3545
|
+
total += value.byteLength;
|
|
3546
|
+
if (total > maxBytes) {
|
|
3547
|
+
await reader.cancel().catch(() => {
|
|
3548
|
+
});
|
|
3549
|
+
throw new ResponseTooLargeError(total, maxBytes);
|
|
3550
|
+
}
|
|
3551
|
+
chunks.push(value);
|
|
3552
|
+
}
|
|
3553
|
+
return JSON.parse(new TextDecoder().decode(Buffer.concat(chunks)));
|
|
3554
|
+
}
|
|
3555
|
+
if (typeof r.text === "function") {
|
|
3556
|
+
const text = await r.text();
|
|
3557
|
+
const size = Buffer.byteLength(text);
|
|
3558
|
+
if (size > maxBytes) throw new ResponseTooLargeError(size, maxBytes);
|
|
3559
|
+
return JSON.parse(text);
|
|
3560
|
+
}
|
|
3561
|
+
if (typeof r.json === "function") return r.json();
|
|
3562
|
+
throw new TypeError("response has no readable body");
|
|
3563
|
+
}
|
|
3492
3564
|
var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
|
|
3493
3565
|
var DEFAULT_COMPETITION = "fifa.world";
|
|
3494
3566
|
var DEFAULT_BASE = `${ESPN_SOCCER}/${DEFAULT_COMPETITION}`;
|
|
3495
|
-
var USER_AGENT = `claudinho/${"0.
|
|
3567
|
+
var USER_AGENT = `claudinho/${"0.10.1"} (+https://github.com/arturogarrido/claudinho)`;
|
|
3496
3568
|
var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
3569
|
+
var DEFAULT_COOLDOWN_MS = 5 * 6e4;
|
|
3570
|
+
var MAX_COOLDOWN_MS = 15 * 6e4;
|
|
3571
|
+
function retryAfterMs(header2, nowMs) {
|
|
3572
|
+
if (typeof header2 !== "string" || header2.trim() === "") return DEFAULT_COOLDOWN_MS;
|
|
3573
|
+
const h = header2.trim();
|
|
3574
|
+
let ms;
|
|
3575
|
+
if (/^\d+$/.test(h)) ms = Number(h) * 1e3;
|
|
3576
|
+
else {
|
|
3577
|
+
const at = Date.parse(h);
|
|
3578
|
+
if (Number.isFinite(at)) ms = at - nowMs;
|
|
3579
|
+
}
|
|
3580
|
+
if (ms === void 0 || !Number.isFinite(ms)) return DEFAULT_COOLDOWN_MS;
|
|
3581
|
+
return Math.min(Math.max(ms, 0), MAX_COOLDOWN_MS);
|
|
3582
|
+
}
|
|
3497
3583
|
function competitionBase(slug) {
|
|
3498
3584
|
return `${ESPN_SOCCER}/${slug}`;
|
|
3499
3585
|
}
|
|
@@ -3502,6 +3588,8 @@ var STANDINGS_SHARE_MS = 3e4;
|
|
|
3502
3588
|
var ProviderError = class extends Error {
|
|
3503
3589
|
kind;
|
|
3504
3590
|
status;
|
|
3591
|
+
/** For a throttle: how long the adapter will refuse to fetch (bounded). */
|
|
3592
|
+
retryAfterMs;
|
|
3505
3593
|
constructor(message, kind, status) {
|
|
3506
3594
|
super(message);
|
|
3507
3595
|
this.name = "ProviderError";
|
|
@@ -3525,6 +3613,7 @@ function usableProviderItems(kind, parsed, hasUsableRecord = parsed.items.length
|
|
|
3525
3613
|
var EspnAdapter = class {
|
|
3526
3614
|
constructor(opts = {}) {
|
|
3527
3615
|
this.opts = opts;
|
|
3616
|
+
this.clock = opts.now ?? (() => Date.now());
|
|
3528
3617
|
const expected = opts.expectedStandingsGroups ?? (opts.baseUrl === void 0 ? groups() : void 0);
|
|
3529
3618
|
this.expectedStandingsGroups = expected ? [...expected] : void 0;
|
|
3530
3619
|
this.standingsFallbackGroups = opts.baseUrl === void 0 && expected ? [...expected] : void 0;
|
|
@@ -3550,6 +3639,53 @@ var EspnAdapter = class {
|
|
|
3550
3639
|
* throttle (persist a backoff) from an ordinary blip.
|
|
3551
3640
|
*/
|
|
3552
3641
|
lastError;
|
|
3642
|
+
/**
|
|
3643
|
+
* A retained throttle (audit A12): after a 429/403 every call inside the
|
|
3644
|
+
* window throws the provider's last answer WITHOUT a request. A server-
|
|
3645
|
+
* lifetime MCP adapter is covered by this alone; the CLI pre-arms each
|
|
3646
|
+
* process from its persisted cache via `armCooldown`.
|
|
3647
|
+
*/
|
|
3648
|
+
cooldownUntilMs;
|
|
3649
|
+
cooldownError;
|
|
3650
|
+
cooldownListeners = /* @__PURE__ */ new Set();
|
|
3651
|
+
clock;
|
|
3652
|
+
/** Epoch ms until which requests are refused, when a cooldown is armed. */
|
|
3653
|
+
get cooldownUntil() {
|
|
3654
|
+
return this.cooldownUntilMs;
|
|
3655
|
+
}
|
|
3656
|
+
/**
|
|
3657
|
+
* Arm the cooldown from outside — a fresh CLI process reading the backoff
|
|
3658
|
+
* its refresher persisted. The retained error reads as a throttle so every
|
|
3659
|
+
* caller's `degraded` path and the refresher's persistence treat it as one.
|
|
3660
|
+
*/
|
|
3661
|
+
armCooldown(untilMs, reason) {
|
|
3662
|
+
const nowMs = this.clock();
|
|
3663
|
+
const error = reason ?? new ProviderError("ESPN request skipped: provider cooldown in effect", "http", 429);
|
|
3664
|
+
error.retryAfterMs = Math.max(0, untilMs - nowMs);
|
|
3665
|
+
this.arm(untilMs, error);
|
|
3666
|
+
}
|
|
3667
|
+
/**
|
|
3668
|
+
* Be told whenever the cooldown window is armed or EXTENDED — the way a
|
|
3669
|
+
* caller persists a throttle that arrives from a still-running request after
|
|
3670
|
+
* its own call already returned (review P2 on #128). Returns unsubscribe.
|
|
3671
|
+
*/
|
|
3672
|
+
onCooldown(listener) {
|
|
3673
|
+
this.cooldownListeners.add(listener);
|
|
3674
|
+
return () => {
|
|
3675
|
+
this.cooldownListeners.delete(listener);
|
|
3676
|
+
};
|
|
3677
|
+
}
|
|
3678
|
+
/**
|
|
3679
|
+
* The ONE place a window is set. Concurrent requests can each carry a
|
|
3680
|
+
* Retry-After; the LATEST expiry wins — a shorter one arriving second must
|
|
3681
|
+
* never shorten a longer active window (review P2 on #128).
|
|
3682
|
+
*/
|
|
3683
|
+
arm(untilMs, error) {
|
|
3684
|
+
if (this.cooldownUntilMs !== void 0 && untilMs <= this.cooldownUntilMs) return;
|
|
3685
|
+
this.cooldownUntilMs = untilMs;
|
|
3686
|
+
this.cooldownError = error;
|
|
3687
|
+
for (const listener of this.cooldownListeners) listener(untilMs);
|
|
3688
|
+
}
|
|
3553
3689
|
async fetchByDate(dateISO) {
|
|
3554
3690
|
return this.fetchScoreboard(toEspnDate(dateISO));
|
|
3555
3691
|
}
|
|
@@ -3637,6 +3773,11 @@ var EspnAdapter = class {
|
|
|
3637
3773
|
return usableProviderItems("scoreboard", parsed);
|
|
3638
3774
|
}
|
|
3639
3775
|
async get(url) {
|
|
3776
|
+
const nowMs = this.clock();
|
|
3777
|
+
if (this.cooldownError && this.cooldownUntilMs !== void 0 && nowMs < this.cooldownUntilMs) {
|
|
3778
|
+
this.lastError = this.cooldownError;
|
|
3779
|
+
throw this.cooldownError;
|
|
3780
|
+
}
|
|
3640
3781
|
const doFetch = this.opts.fetchImpl ?? fetch;
|
|
3641
3782
|
const controller = new AbortController();
|
|
3642
3783
|
const timer = setTimeout(
|
|
@@ -3658,19 +3799,24 @@ var EspnAdapter = class {
|
|
|
3658
3799
|
throw e?.name === "AbortError" ? new ProviderError(`ESPN request timed out: ${url}`, "timeout") : new ProviderError(`ESPN request failed: ${e?.message ?? e}`, "http");
|
|
3659
3800
|
}
|
|
3660
3801
|
if (!res.ok) {
|
|
3661
|
-
|
|
3802
|
+
const pe = new ProviderError(
|
|
3662
3803
|
`ESPN request failed: ${res.status} ${res.statusText}`,
|
|
3663
3804
|
"http",
|
|
3664
3805
|
res.status
|
|
3665
3806
|
);
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3807
|
+
if (pe.throttled) {
|
|
3808
|
+
const receiptMs = this.clock();
|
|
3809
|
+
pe.retryAfterMs = retryAfterMs(res.headers?.get?.("retry-after"), receiptMs);
|
|
3810
|
+
this.arm(receiptMs + pe.retryAfterMs, pe);
|
|
3811
|
+
}
|
|
3812
|
+
throw pe;
|
|
3670
3813
|
}
|
|
3671
3814
|
try {
|
|
3672
|
-
return await res
|
|
3815
|
+
return await readJsonBounded(res, MAX_RESPONSE_BYTES);
|
|
3673
3816
|
} catch (e) {
|
|
3817
|
+
if (e instanceof ResponseTooLargeError) {
|
|
3818
|
+
throw new ProviderError(`ESPN response too large: ${e.bytes} bytes`, "parse");
|
|
3819
|
+
}
|
|
3674
3820
|
throw new ProviderError(
|
|
3675
3821
|
`ESPN response unparseable: ${e?.message ?? e}`,
|
|
3676
3822
|
"parse"
|
|
@@ -3713,14 +3859,30 @@ function hasGroupStarted(group, tables) {
|
|
|
3713
3859
|
function matchesPerTeamInGroup(teamCount) {
|
|
3714
3860
|
return Math.max(0, teamCount - 1);
|
|
3715
3861
|
}
|
|
3716
|
-
function isGroupStandingsComplete(table) {
|
|
3717
|
-
|
|
3862
|
+
function isGroupStandingsComplete(table, expectedTeams) {
|
|
3863
|
+
if (!table || table.partial) return false;
|
|
3864
|
+
const n = table.rows.length;
|
|
3718
3865
|
if (n < 2) return false;
|
|
3719
|
-
|
|
3866
|
+
if (expectedTeams !== void 0 && n < expectedTeams) return false;
|
|
3867
|
+
const required = matchesPerTeamInGroup(Math.max(n, expectedTeams ?? n));
|
|
3720
3868
|
return table.rows.every((r) => r.played >= required);
|
|
3721
3869
|
}
|
|
3870
|
+
function bundledGroupSize(group) {
|
|
3871
|
+
const codes = /* @__PURE__ */ new Set();
|
|
3872
|
+
for (const m of fixturesByGroup(group)) {
|
|
3873
|
+
codes.add(m.home.code);
|
|
3874
|
+
codes.add(m.away.code);
|
|
3875
|
+
}
|
|
3876
|
+
return codes.size > 0 ? codes.size : void 0;
|
|
3877
|
+
}
|
|
3722
3878
|
function isGroupComplete(group, tables) {
|
|
3723
|
-
return isGroupStandingsComplete(
|
|
3879
|
+
return isGroupStandingsComplete(
|
|
3880
|
+
tables.find((t2) => t2.group === group),
|
|
3881
|
+
bundledGroupSize(group)
|
|
3882
|
+
);
|
|
3883
|
+
}
|
|
3884
|
+
function isGroupPartial(group, tables) {
|
|
3885
|
+
return tables.find((t2) => t2.group === group)?.partial !== void 0;
|
|
3724
3886
|
}
|
|
3725
3887
|
function resolveWinner(match) {
|
|
3726
3888
|
if (!isFinished(match.status)) return void 0;
|
|
@@ -3754,8 +3916,8 @@ function resolveLoser(match) {
|
|
|
3754
3916
|
function participant(team, status) {
|
|
3755
3917
|
return { label: team.name, flag: team.flag, code: team.code, status };
|
|
3756
3918
|
}
|
|
3757
|
-
function tbd(
|
|
3758
|
-
return { label, flag: "\u{1F3F3}\uFE0F", status: "tbd" };
|
|
3919
|
+
function tbd(label2) {
|
|
3920
|
+
return { label: label2, flag: "\u{1F3F3}\uFE0F", status: "tbd" };
|
|
3759
3921
|
}
|
|
3760
3922
|
function confirmedLiveParticipant(team) {
|
|
3761
3923
|
if (!team || team.flag === "\u{1F3F3}\uFE0F") return void 0;
|
|
@@ -3775,15 +3937,15 @@ function resolveSlot(ref, ctx, liveTeam, fixtureInMergedSet = false) {
|
|
|
3775
3937
|
return tbd(ref.label);
|
|
3776
3938
|
case "group": {
|
|
3777
3939
|
if (liveParticipant) return liveParticipant;
|
|
3778
|
-
if (!ctx.standingsDegraded && hasGroupStarted(ref.group, ctx.tables)) {
|
|
3940
|
+
if (!ctx.standingsDegraded && !isGroupPartial(ref.group, ctx.tables) && hasGroupStarted(ref.group, ctx.tables)) {
|
|
3779
3941
|
const team = teamFromStandings(ref.group, ref.position, ctx.tables);
|
|
3780
3942
|
if (team) {
|
|
3781
3943
|
const status = isGroupComplete(ref.group, ctx.tables) ? "confirmed" : "projected";
|
|
3782
3944
|
return participant(team, status);
|
|
3783
3945
|
}
|
|
3784
3946
|
}
|
|
3785
|
-
const
|
|
3786
|
-
return tbd(
|
|
3947
|
+
const label2 = ref.position === 1 ? t(ctx.lang, "bracket.slot.groupWinner", { group: ref.group }) : t(ctx.lang, "bracket.slot.groupSecond", { group: ref.group });
|
|
3948
|
+
return tbd(label2);
|
|
3787
3949
|
}
|
|
3788
3950
|
case "third":
|
|
3789
3951
|
if (liveParticipant) return liveParticipant;
|
|
@@ -4409,13 +4571,17 @@ function resolveCompetition(explicit) {
|
|
|
4409
4571
|
}
|
|
4410
4572
|
return DEFAULT_COMPETITION;
|
|
4411
4573
|
}
|
|
4574
|
+
var BUNDLE_COMPETITION = DEFAULT_COMPETITION;
|
|
4575
|
+
function bundleApplies(competition = resolveCompetition()) {
|
|
4576
|
+
return competition === BUNDLE_COMPETITION;
|
|
4577
|
+
}
|
|
4412
4578
|
var KNOWN_SOURCES = ["espn"];
|
|
4413
|
-
function makeAdapter(source = "espn") {
|
|
4579
|
+
function makeAdapter(source = "espn", opts = {}) {
|
|
4414
4580
|
switch (source) {
|
|
4415
4581
|
case "espn": {
|
|
4416
4582
|
const competition = resolveCompetition();
|
|
4417
4583
|
const baseUrl = competition === DEFAULT_COMPETITION ? void 0 : competitionBase(competition);
|
|
4418
|
-
return new EspnAdapter({ baseUrl });
|
|
4584
|
+
return new EspnAdapter({ baseUrl, enrichGroups: opts.enrichGroups, now: opts.now });
|
|
4419
4585
|
}
|
|
4420
4586
|
default:
|
|
4421
4587
|
throw new Error(
|
|
@@ -4433,7 +4599,7 @@ function liveSourceLabel(source) {
|
|
|
4433
4599
|
return known[source] ?? source.charAt(0).toUpperCase() + source.slice(1);
|
|
4434
4600
|
}
|
|
4435
4601
|
async function getMatchesForDate(adapter, dateISO) {
|
|
4436
|
-
const base = allFixtures();
|
|
4602
|
+
const base = bundleApplies() ? allFixtures() : [];
|
|
4437
4603
|
const day = dateISO.slice(0, 10);
|
|
4438
4604
|
try {
|
|
4439
4605
|
const live = adapter.fetchWindow ? await adapter.fetchWindow(shiftUtcDate(day, -1), shiftUtcDate(day, 1)) : await adapter.fetchByDate(day);
|
|
@@ -4478,21 +4644,27 @@ function knockoutWindow() {
|
|
|
4478
4644
|
return knockoutWindowMemo;
|
|
4479
4645
|
}
|
|
4480
4646
|
async function getBracket(adapter, opts = {}) {
|
|
4647
|
+
if (!bundleApplies()) {
|
|
4648
|
+
const view2 = { stages: [], degraded: false, standingsDegraded: false, unsupported: true };
|
|
4649
|
+
return { view: view2, degraded: false, standingsDegraded: false, unsupported: true };
|
|
4650
|
+
}
|
|
4481
4651
|
const topology = loadBracketTopology();
|
|
4482
4652
|
const base = allFixtures().filter((m) => m.stage !== "GROUP" && m.stage !== "FRIENDLY");
|
|
4483
4653
|
let matches = base;
|
|
4484
4654
|
let liveDegraded = true;
|
|
4485
4655
|
let source;
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4656
|
+
const win = knockoutWindow();
|
|
4657
|
+
if (adapter.fetchWindow && win) {
|
|
4658
|
+
try {
|
|
4659
|
+
const live = await adapter.fetchWindow(win.start, win.end);
|
|
4660
|
+
matches = mergeLive(base, live);
|
|
4661
|
+
liveDegraded = false;
|
|
4662
|
+
source = adapter.name;
|
|
4663
|
+
} catch {
|
|
4664
|
+
}
|
|
4493
4665
|
}
|
|
4494
4666
|
const standings = await getStandings(adapter);
|
|
4495
|
-
if (!source && !standings.degraded && standings.source) {
|
|
4667
|
+
if (!source && !standings.degraded && standings.source && standings.tables.length > 0) {
|
|
4496
4668
|
source = standings.source;
|
|
4497
4669
|
}
|
|
4498
4670
|
const view = buildBracketView(
|
|
@@ -4514,18 +4686,18 @@ async function getBracket(adapter, opts = {}) {
|
|
|
4514
4686
|
}
|
|
4515
4687
|
var EXTRA_TIME_SLACK_MS = 60 * 6e4;
|
|
4516
4688
|
async function marketFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Date()) {
|
|
4689
|
+
if (!bundleApplies()) return { match: void 0, degraded: false, unsupported: true };
|
|
4517
4690
|
const nowMs = now.getTime();
|
|
4518
4691
|
let fixtures = allFixtures();
|
|
4519
4692
|
let overlayFailed = false;
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
fixtures = mergeLive(
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
);
|
|
4693
|
+
const win = knockoutWindow();
|
|
4694
|
+
if (adapter.fetchWindow && win) {
|
|
4695
|
+
try {
|
|
4696
|
+
fixtures = mergeLive(fixtures, await adapter.fetchWindow(win.start, win.end));
|
|
4697
|
+
} catch {
|
|
4698
|
+
overlayFailed = true;
|
|
4527
4699
|
}
|
|
4528
|
-
}
|
|
4700
|
+
} else {
|
|
4529
4701
|
overlayFailed = true;
|
|
4530
4702
|
}
|
|
4531
4703
|
const candidate = fixturesByTeam(code, fixtures).find((m) => {
|
|
@@ -4541,23 +4713,27 @@ async function marketFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Dat
|
|
|
4541
4713
|
return { match: next, degraded: overlayFailed };
|
|
4542
4714
|
}
|
|
4543
4715
|
async function getNextFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Date()) {
|
|
4716
|
+
if (!bundleApplies()) return { fixture: void 0, degraded: false, unsupported: true };
|
|
4544
4717
|
const base = allFixtures();
|
|
4545
4718
|
let matches = base;
|
|
4546
4719
|
let degraded = true;
|
|
4547
4720
|
let liveById;
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4721
|
+
const win = knockoutWindow();
|
|
4722
|
+
if (adapter.fetchWindow && win) {
|
|
4723
|
+
try {
|
|
4724
|
+
const live = await adapter.fetchWindow(win.start, win.end);
|
|
4725
|
+
matches = mergeLive(base, live);
|
|
4726
|
+
degraded = false;
|
|
4727
|
+
liveById = new Set(live.map((m) => m.id));
|
|
4728
|
+
} catch {
|
|
4729
|
+
}
|
|
4555
4730
|
}
|
|
4556
4731
|
const fixture = nextFixtureForTeam(code, { from: now, fixtures: matches });
|
|
4557
4732
|
const source = fixture && liveById?.has(fixture.id) ? adapter.name : void 0;
|
|
4558
4733
|
return { fixture, degraded, source };
|
|
4559
4734
|
}
|
|
4560
4735
|
async function getKnockoutFixtures(adapter, now = /* @__PURE__ */ new Date()) {
|
|
4736
|
+
if (!bundleApplies()) return { fixtures: [], degraded: true, unsupported: true };
|
|
4561
4737
|
const win = knockoutWindow();
|
|
4562
4738
|
if (!adapter.fetchWindow || !win) return { fixtures: [], degraded: true };
|
|
4563
4739
|
let live;
|
|
@@ -4566,13 +4742,13 @@ async function getKnockoutFixtures(adapter, now = /* @__PURE__ */ new Date()) {
|
|
|
4566
4742
|
} catch {
|
|
4567
4743
|
return { fixtures: [], degraded: true };
|
|
4568
4744
|
}
|
|
4569
|
-
const nowMs = now.getTime();
|
|
4570
4745
|
const fixtures = live.filter(
|
|
4571
|
-
(m) => m.stage !== "GROUP" && m.stage !== "FRIENDLY" &&
|
|
4746
|
+
(m) => m.stage !== "GROUP" && m.stage !== "FRIENDLY" && isUpcoming(m, now) && isResolvedNation(m.home) && isResolvedNation(m.away)
|
|
4572
4747
|
).sort(byKickoff);
|
|
4573
4748
|
return { fixtures, degraded: false };
|
|
4574
4749
|
}
|
|
4575
4750
|
async function getMatchById(adapter, id) {
|
|
4751
|
+
if (!bundleApplies()) return { match: void 0, degraded: false, unsupported: true };
|
|
4576
4752
|
const base = allFixtures().find((m) => m.id === id);
|
|
4577
4753
|
if (!base) return { match: void 0, degraded: false };
|
|
4578
4754
|
const day = base.kickoff.slice(0, 10);
|
|
@@ -5013,11 +5189,15 @@ var PolymarketProvider = class {
|
|
|
5013
5189
|
if (!res.ok) {
|
|
5014
5190
|
throw new Error(`Polymarket request failed: ${res.status} ${res.statusText}`);
|
|
5015
5191
|
}
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5192
|
+
let data;
|
|
5193
|
+
try {
|
|
5194
|
+
data = await readJsonBounded(res, MAX_RESPONSE_BYTES);
|
|
5195
|
+
} catch (e) {
|
|
5196
|
+
if (e instanceof ResponseTooLargeError) {
|
|
5197
|
+
throw new Error(`Polymarket response too large: ${e.bytes} bytes`);
|
|
5198
|
+
}
|
|
5199
|
+
throw e;
|
|
5019
5200
|
}
|
|
5020
|
-
const data = await res.json();
|
|
5021
5201
|
if (Array.isArray(data) && data.length > 1) {
|
|
5022
5202
|
return ambiguous("slug returned more than one event");
|
|
5023
5203
|
}
|
|
@@ -5097,7 +5277,7 @@ var PolymarketProvider = class {
|
|
|
5097
5277
|
const outcomes = [];
|
|
5098
5278
|
let asOf = canonicalTimestamp(event.updatedAt);
|
|
5099
5279
|
let liquidity;
|
|
5100
|
-
for (const [kind, market, teamCode2,
|
|
5280
|
+
for (const [kind, market, teamCode2, label2] of legs) {
|
|
5101
5281
|
if (!market) continue;
|
|
5102
5282
|
if (typeof market.closed !== "boolean" || typeof market.active !== "boolean") {
|
|
5103
5283
|
return malformed("market active/closed is not a boolean");
|
|
@@ -5110,7 +5290,7 @@ var PolymarketProvider = class {
|
|
|
5110
5290
|
}
|
|
5111
5291
|
const yes = yesPrice(market);
|
|
5112
5292
|
if (yes == null) return malformed("market is not a readable Yes/No binary");
|
|
5113
|
-
outcomes.push({ kind, teamCode: teamCode2, label, probability: yes });
|
|
5293
|
+
outcomes.push({ kind, teamCode: teamCode2, label: label2, probability: yes });
|
|
5114
5294
|
const marketAsOf = canonicalTimestamp(market.updatedAt);
|
|
5115
5295
|
if (!marketAsOf) {
|
|
5116
5296
|
return malformed("market updatedAt missing or unparseable");
|
|
@@ -5278,6 +5458,10 @@ function numberish(v) {
|
|
|
5278
5458
|
}
|
|
5279
5459
|
return void 0;
|
|
5280
5460
|
}
|
|
5461
|
+
var MARKET_COMPETITIONS = /* @__PURE__ */ new Set([DEFAULT_COMPETITION]);
|
|
5462
|
+
function marketsCoverCompetition(competition = resolveCompetition()) {
|
|
5463
|
+
return MARKET_COMPETITIONS.has(competition);
|
|
5464
|
+
}
|
|
5281
5465
|
function resolveMarketSource(explicit) {
|
|
5282
5466
|
if (explicit) return explicit;
|
|
5283
5467
|
if (typeof process !== "undefined" && process.env?.CLAUDINHO_MARKETS_SOURCE) {
|
|
@@ -5294,6 +5478,7 @@ function makeMarketProvider(source) {
|
|
|
5294
5478
|
return new FakeMarketProvider();
|
|
5295
5479
|
// no synth → yields no signals, no network
|
|
5296
5480
|
default:
|
|
5481
|
+
if (!marketsCoverCompetition()) return new FakeMarketProvider();
|
|
5297
5482
|
return new PolymarketProvider();
|
|
5298
5483
|
}
|
|
5299
5484
|
}
|
|
@@ -5419,10 +5604,13 @@ function formatShareTable(input, options = {}) {
|
|
|
5419
5604
|
input.emptyNote ?? (input.degraded ? "Live standings unavailable." : "No standings available.")
|
|
5420
5605
|
);
|
|
5421
5606
|
} else {
|
|
5422
|
-
for (const { group, rows } of input.tables) {
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5607
|
+
for (const { group, rows, partial } of input.tables) {
|
|
5608
|
+
const lines = [`Group ${group} \xB7 standings`, "", ...rows.map((r, i) => tableRow(r, r.rank ?? i + 1))];
|
|
5609
|
+
if (partial) {
|
|
5610
|
+
const n = partial.omitted;
|
|
5611
|
+
lines.push("", `(partial table \u2014 ${n} row${n === 1 ? "" : "s"} unreadable; positions are the provider's ranks)`);
|
|
5612
|
+
}
|
|
5613
|
+
blocks.push(lines.join("\n"));
|
|
5426
5614
|
}
|
|
5427
5615
|
if (input.degraded) {
|
|
5428
5616
|
blocks.push("(Live standings unavailable \u2014 group roster, not live results.)");
|
|
@@ -5606,11 +5794,56 @@ function resolveConfig(opts) {
|
|
|
5606
5794
|
|
|
5607
5795
|
// src/cursorPayload.ts
|
|
5608
5796
|
import { readFileSync } from "fs";
|
|
5797
|
+
var MAX_CURSOR_PAYLOAD_BYTES = 64 * 1024;
|
|
5798
|
+
var MAX_CURSOR_LABEL_COLUMNS = 40;
|
|
5799
|
+
function label(v) {
|
|
5800
|
+
const s = humanLabel(v, MAX_CURSOR_LABEL_COLUMNS);
|
|
5801
|
+
return s === "" ? void 0 : s;
|
|
5802
|
+
}
|
|
5803
|
+
function finite(v) {
|
|
5804
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
5805
|
+
}
|
|
5806
|
+
function record(v) {
|
|
5807
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
5808
|
+
}
|
|
5809
|
+
function validateCursorPayload(parsed) {
|
|
5810
|
+
const p = record(parsed);
|
|
5811
|
+
if (!p) return void 0;
|
|
5812
|
+
const out2 = {};
|
|
5813
|
+
const model = record(p.model);
|
|
5814
|
+
if (model) {
|
|
5815
|
+
const display_name = label(model.display_name);
|
|
5816
|
+
const param_summary = label(model.param_summary);
|
|
5817
|
+
if (display_name || param_summary) {
|
|
5818
|
+
out2.model = {
|
|
5819
|
+
...display_name ? { display_name } : {},
|
|
5820
|
+
...param_summary ? { param_summary } : {}
|
|
5821
|
+
};
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
const cw = record(p.context_window);
|
|
5825
|
+
if (cw) {
|
|
5826
|
+
if (cw.used_percentage === null) out2.context_window = { used_percentage: null };
|
|
5827
|
+
else {
|
|
5828
|
+
const pct2 = finite(cw.used_percentage);
|
|
5829
|
+
if (pct2 !== void 0) out2.context_window = { used_percentage: pct2 };
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
const wt = record(p.worktree);
|
|
5833
|
+
const name = wt ? label(wt.name) : void 0;
|
|
5834
|
+
if (name) out2.worktree = { name };
|
|
5835
|
+
const vim = record(p.vim);
|
|
5836
|
+
const mode = vim ? label(vim.mode) : void 0;
|
|
5837
|
+
if (mode) out2.vim = { mode };
|
|
5838
|
+
const width = finite(p.render_width_chars);
|
|
5839
|
+
if (width !== void 0 && width > 0) out2.render_width_chars = Math.floor(width);
|
|
5840
|
+
return out2;
|
|
5841
|
+
}
|
|
5609
5842
|
function parseCursorPayload(raw) {
|
|
5610
5843
|
try {
|
|
5611
5844
|
const trimmed = raw.trim();
|
|
5612
5845
|
if (!trimmed) return void 0;
|
|
5613
|
-
return JSON.parse(trimmed);
|
|
5846
|
+
return validateCursorPayload(JSON.parse(trimmed));
|
|
5614
5847
|
} catch {
|
|
5615
5848
|
return void 0;
|
|
5616
5849
|
}
|
|
@@ -5623,19 +5856,27 @@ function readCursorPayload() {
|
|
|
5623
5856
|
return void 0;
|
|
5624
5857
|
}
|
|
5625
5858
|
}
|
|
5626
|
-
function readCursorPayloadBounded(timeoutMs = 100, stdin = process.stdin) {
|
|
5859
|
+
function readCursorPayloadBounded(timeoutMs = 100, stdin = process.stdin, maxBytes = MAX_CURSOR_PAYLOAD_BYTES) {
|
|
5627
5860
|
if (stdin.isTTY) return Promise.resolve(void 0);
|
|
5628
5861
|
return new Promise((resolve) => {
|
|
5629
5862
|
const chunks = [];
|
|
5863
|
+
let total = 0;
|
|
5864
|
+
let overflow = false;
|
|
5630
5865
|
const done = () => {
|
|
5631
5866
|
clearTimeout(timer);
|
|
5632
5867
|
stdin.off("data", onData);
|
|
5633
5868
|
stdin.off("end", done);
|
|
5634
5869
|
stdin.off("error", done);
|
|
5635
5870
|
stdin.pause();
|
|
5636
|
-
resolve(parseCursorPayload(Buffer.concat(chunks).toString("utf8")));
|
|
5871
|
+
resolve(overflow ? void 0 : parseCursorPayload(Buffer.concat(chunks).toString("utf8")));
|
|
5637
5872
|
};
|
|
5638
5873
|
const onData = (c) => {
|
|
5874
|
+
total += c.length;
|
|
5875
|
+
if (total > maxBytes) {
|
|
5876
|
+
overflow = true;
|
|
5877
|
+
done();
|
|
5878
|
+
return;
|
|
5879
|
+
}
|
|
5639
5880
|
chunks.push(c);
|
|
5640
5881
|
};
|
|
5641
5882
|
const timer = setTimeout(done, timeoutMs);
|
|
@@ -5658,18 +5899,19 @@ function cursorMetaEnabled(payload) {
|
|
|
5658
5899
|
}
|
|
5659
5900
|
function renderCursorMetaLine(payload) {
|
|
5660
5901
|
const parts = [];
|
|
5661
|
-
const model = payload.model?.display_name;
|
|
5902
|
+
const model = label(payload.model?.display_name);
|
|
5662
5903
|
if (model) {
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
parts.push(label);
|
|
5904
|
+
const summary = label(payload.model?.param_summary);
|
|
5905
|
+
parts.push(summary ? `${model} ${summary}` : model);
|
|
5666
5906
|
}
|
|
5667
5907
|
const pct2 = payload.context_window?.used_percentage;
|
|
5668
5908
|
if (typeof pct2 === "number" && Number.isFinite(pct2)) {
|
|
5669
5909
|
parts.push(`ctx ${Math.floor(pct2)}%`);
|
|
5670
5910
|
}
|
|
5671
|
-
|
|
5672
|
-
if (
|
|
5911
|
+
const wt = label(payload.worktree?.name);
|
|
5912
|
+
if (wt) parts.push(`wt ${wt}`);
|
|
5913
|
+
const vim = label(payload.vim?.mode);
|
|
5914
|
+
if (vim) parts.push(vim);
|
|
5673
5915
|
if (parts.length === 0) return void 0;
|
|
5674
5916
|
return `\x1B[90m${parts.join(" ")}\x1B[0m`;
|
|
5675
5917
|
}
|
|
@@ -5703,6 +5945,8 @@ var EN2 = {
|
|
|
5703
5945
|
"table.degraded": "Live standings unavailable \u2014 showing the group roster.",
|
|
5704
5946
|
"table.unavailable": "Live standings unavailable.",
|
|
5705
5947
|
"table.empty": "No standings available.",
|
|
5948
|
+
"table.partial": "Partial table \u2014 {n} rows could not be read.",
|
|
5949
|
+
"competition.unsupported": "Not available for this competition yet.",
|
|
5706
5950
|
"match.none": "No match found with id {id}.",
|
|
5707
5951
|
"status.live": "LIVE",
|
|
5708
5952
|
"status.ht": "HT",
|
|
@@ -5744,6 +5988,8 @@ var ES2 = {
|
|
|
5744
5988
|
"table.degraded": "Tabla en vivo no disponible \u2014 mostrando la lista del grupo.",
|
|
5745
5989
|
"table.unavailable": "Tabla en vivo no disponible.",
|
|
5746
5990
|
"table.empty": "No hay clasificaci\xF3n disponible.",
|
|
5991
|
+
"table.partial": "Tabla parcial \u2014 no se pudieron leer {n} filas.",
|
|
5992
|
+
"competition.unsupported": "A\xFAn no disponible para esta competici\xF3n.",
|
|
5747
5993
|
"match.none": "No se encontr\xF3 partido con id {id}.",
|
|
5748
5994
|
"status.live": "EN VIVO",
|
|
5749
5995
|
"status.ht": "DESC",
|
|
@@ -5785,6 +6031,8 @@ var PT2 = {
|
|
|
5785
6031
|
"table.degraded": "Classifica\xE7\xE3o ao vivo indispon\xEDvel \u2014 mostrando os times do grupo.",
|
|
5786
6032
|
"table.unavailable": "Classifica\xE7\xE3o ao vivo indispon\xEDvel.",
|
|
5787
6033
|
"table.empty": "Classifica\xE7\xE3o indispon\xEDvel.",
|
|
6034
|
+
"table.partial": "Tabela parcial \u2014 {n} linhas n\xE3o puderam ser lidas.",
|
|
6035
|
+
"competition.unsupported": "Ainda n\xE3o dispon\xEDvel para esta competi\xE7\xE3o.",
|
|
5788
6036
|
"match.none": "Nenhum jogo encontrado com id {id}.",
|
|
5789
6037
|
"status.live": "AO VIVO",
|
|
5790
6038
|
"status.ht": "INT",
|
|
@@ -5826,6 +6074,8 @@ var FR2 = {
|
|
|
5826
6074
|
"table.degraded": "Classement en direct indisponible \u2014 affichage de la composition du groupe.",
|
|
5827
6075
|
"table.unavailable": "Classement en direct indisponible.",
|
|
5828
6076
|
"table.empty": "Aucun classement disponible.",
|
|
6077
|
+
"table.partial": "Classement partiel \u2014 {n} lignes n'ont pas pu \xEAtre lues.",
|
|
6078
|
+
"competition.unsupported": "Pas encore disponible pour cette comp\xE9tition.",
|
|
5829
6079
|
"match.none": "Aucun match trouv\xE9 avec id {id}.",
|
|
5830
6080
|
"status.live": "DIRECT",
|
|
5831
6081
|
"status.ht": "MT",
|
|
@@ -5935,7 +6185,7 @@ function dataSource(source, lang, c) {
|
|
|
5935
6185
|
}
|
|
5936
6186
|
|
|
5937
6187
|
// src/marketCache.ts
|
|
5938
|
-
import { readFileSync as readFileSync3, statSync as
|
|
6188
|
+
import { readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
5939
6189
|
import { join as join3 } from "path";
|
|
5940
6190
|
|
|
5941
6191
|
// src/cache.ts
|
|
@@ -5944,30 +6194,68 @@ import {
|
|
|
5944
6194
|
mkdirSync as mkdirSync2,
|
|
5945
6195
|
openSync as openSync2,
|
|
5946
6196
|
readFileSync as readFileSync2,
|
|
5947
|
-
rmSync,
|
|
5948
|
-
statSync,
|
|
6197
|
+
rmSync as rmSync2,
|
|
6198
|
+
statSync as statSync2,
|
|
5949
6199
|
writeSync as writeSync2
|
|
5950
6200
|
} from "fs";
|
|
5951
6201
|
import { join as join2 } from "path";
|
|
6202
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
5952
6203
|
|
|
5953
6204
|
// src/paths.ts
|
|
5954
|
-
import {
|
|
6205
|
+
import { randomBytes } from "crypto";
|
|
6206
|
+
import {
|
|
6207
|
+
closeSync,
|
|
6208
|
+
fchmodSync,
|
|
6209
|
+
lstatSync,
|
|
6210
|
+
mkdirSync,
|
|
6211
|
+
openSync,
|
|
6212
|
+
realpathSync,
|
|
6213
|
+
renameSync,
|
|
6214
|
+
rmSync,
|
|
6215
|
+
statSync,
|
|
6216
|
+
writeSync
|
|
6217
|
+
} from "fs";
|
|
5955
6218
|
import { homedir } from "os";
|
|
5956
6219
|
import { dirname, join } from "path";
|
|
5957
6220
|
function cacheDir() {
|
|
5958
6221
|
const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
|
|
5959
6222
|
return join(base, "claudinho");
|
|
5960
6223
|
}
|
|
5961
|
-
function writeFileAtomic(path, data) {
|
|
6224
|
+
function writeFileAtomic(path, data, opts = {}) {
|
|
5962
6225
|
mkdirSync(dirname(path), { recursive: true });
|
|
5963
|
-
|
|
5964
|
-
|
|
6226
|
+
let target = path;
|
|
6227
|
+
let existingMode;
|
|
6228
|
+
let entry;
|
|
6229
|
+
try {
|
|
6230
|
+
entry = lstatSync(path);
|
|
6231
|
+
} catch {
|
|
6232
|
+
entry = void 0;
|
|
6233
|
+
}
|
|
6234
|
+
if (entry?.isSymbolicLink()) {
|
|
6235
|
+
if (opts.followSymlinks) {
|
|
6236
|
+
target = realpathSync(path);
|
|
6237
|
+
existingMode = statSync(target).mode & 511;
|
|
6238
|
+
}
|
|
6239
|
+
} else if (entry) {
|
|
6240
|
+
existingMode = entry.mode & 511;
|
|
6241
|
+
}
|
|
6242
|
+
const tmp = `${target}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
6243
|
+
const fd = openSync(tmp, "wx", existingMode ?? opts.mode ?? 438);
|
|
5965
6244
|
try {
|
|
6245
|
+
if (existingMode !== void 0) fchmodSync(fd, existingMode);
|
|
5966
6246
|
writeSync(fd, data);
|
|
5967
|
-
}
|
|
6247
|
+
} catch (e) {
|
|
5968
6248
|
closeSync(fd);
|
|
6249
|
+
rmSync(tmp, { force: true });
|
|
6250
|
+
throw e;
|
|
6251
|
+
}
|
|
6252
|
+
closeSync(fd);
|
|
6253
|
+
try {
|
|
6254
|
+
renameSync(tmp, target);
|
|
6255
|
+
} catch (e) {
|
|
6256
|
+
rmSync(tmp, { force: true });
|
|
6257
|
+
throw e;
|
|
5969
6258
|
}
|
|
5970
|
-
renameSync(tmp, path);
|
|
5971
6259
|
}
|
|
5972
6260
|
|
|
5973
6261
|
// src/cache.ts
|
|
@@ -6021,7 +6309,7 @@ function isCacheState(value) {
|
|
|
6021
6309
|
function readState(source = "espn", competition = DEFAULT_COMPETITION) {
|
|
6022
6310
|
try {
|
|
6023
6311
|
const path = cachePath(source, competition);
|
|
6024
|
-
const info =
|
|
6312
|
+
const info = statSync2(path);
|
|
6025
6313
|
if (!info.isFile() || info.size > MAX_STATE_BYTES) return void 0;
|
|
6026
6314
|
const bytes = readFileSync2(path);
|
|
6027
6315
|
if (bytes.byteLength > MAX_STATE_BYTES) return void 0;
|
|
@@ -6075,7 +6363,7 @@ function lockAgeMs(now = Date.now()) {
|
|
|
6075
6363
|
return Infinity;
|
|
6076
6364
|
}
|
|
6077
6365
|
try {
|
|
6078
|
-
return stampAgeMs(new Date(
|
|
6366
|
+
return stampAgeMs(new Date(statSync2(lp).mtimeMs).toISOString(), now);
|
|
6079
6367
|
} catch {
|
|
6080
6368
|
return Infinity;
|
|
6081
6369
|
}
|
|
@@ -6083,44 +6371,62 @@ function lockAgeMs(now = Date.now()) {
|
|
|
6083
6371
|
function isLockFresh(now = Date.now()) {
|
|
6084
6372
|
return lockAgeMs(now) < LOCK_STALE_MS;
|
|
6085
6373
|
}
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6374
|
+
var heldToken;
|
|
6375
|
+
function readLockToken() {
|
|
6376
|
+
try {
|
|
6377
|
+
return readFileSync2(lockPath(), "utf8").trim();
|
|
6378
|
+
} catch {
|
|
6379
|
+
return void 0;
|
|
6380
|
+
}
|
|
6381
|
+
}
|
|
6382
|
+
function writeExclusive(lp, token) {
|
|
6089
6383
|
try {
|
|
6090
6384
|
const fd = openSync2(lp, "wx");
|
|
6091
6385
|
try {
|
|
6092
|
-
writeSync2(fd,
|
|
6386
|
+
writeSync2(fd, token);
|
|
6093
6387
|
} finally {
|
|
6094
6388
|
closeSync2(fd);
|
|
6095
6389
|
}
|
|
6096
6390
|
return true;
|
|
6097
6391
|
} catch {
|
|
6098
|
-
if (lockAgeMs(now) > LOCK_STALE_MS) {
|
|
6099
|
-
try {
|
|
6100
|
-
rmSync(lp, { force: true });
|
|
6101
|
-
} catch {
|
|
6102
|
-
return false;
|
|
6103
|
-
}
|
|
6104
|
-
try {
|
|
6105
|
-
const fd = openSync2(lp, "wx");
|
|
6106
|
-
try {
|
|
6107
|
-
writeSync2(fd, `${process.pid} ${now}`);
|
|
6108
|
-
} finally {
|
|
6109
|
-
closeSync2(fd);
|
|
6110
|
-
}
|
|
6111
|
-
return true;
|
|
6112
|
-
} catch {
|
|
6113
|
-
return false;
|
|
6114
|
-
}
|
|
6115
|
-
}
|
|
6116
6392
|
return false;
|
|
6117
6393
|
}
|
|
6118
6394
|
}
|
|
6119
|
-
function
|
|
6395
|
+
function claimLock(now = Date.now()) {
|
|
6396
|
+
mkdirSync2(cacheDir(), { recursive: true });
|
|
6397
|
+
const lp = lockPath();
|
|
6398
|
+
const token = `${process.pid} ${now} ${randomBytes2(6).toString("hex")}`;
|
|
6399
|
+
if (writeExclusive(lp, token)) return token;
|
|
6400
|
+
if (lockAgeMs(now) > LOCK_STALE_MS) {
|
|
6401
|
+
try {
|
|
6402
|
+
rmSync2(lp, { force: true });
|
|
6403
|
+
} catch {
|
|
6404
|
+
return void 0;
|
|
6405
|
+
}
|
|
6406
|
+
return writeExclusive(lp, token) ? token : void 0;
|
|
6407
|
+
}
|
|
6408
|
+
return void 0;
|
|
6409
|
+
}
|
|
6410
|
+
function holdsLock(token = heldToken) {
|
|
6411
|
+
return token !== void 0 && readLockToken() === token;
|
|
6412
|
+
}
|
|
6413
|
+
function acquireLock(now = Date.now()) {
|
|
6414
|
+
const token = claimLock(now);
|
|
6415
|
+
if (token) heldToken = token;
|
|
6416
|
+
return token !== void 0;
|
|
6417
|
+
}
|
|
6418
|
+
function releaseLock(token = heldToken) {
|
|
6419
|
+
if (!holdsLock(token)) return;
|
|
6120
6420
|
try {
|
|
6121
|
-
|
|
6421
|
+
rmSync2(lockPath(), { force: true });
|
|
6122
6422
|
} catch {
|
|
6123
6423
|
}
|
|
6424
|
+
if (token === heldToken) heldToken = void 0;
|
|
6425
|
+
}
|
|
6426
|
+
function publishState(state, token = heldToken) {
|
|
6427
|
+
if (!holdsLock(token)) return false;
|
|
6428
|
+
writeState(state);
|
|
6429
|
+
return true;
|
|
6124
6430
|
}
|
|
6125
6431
|
|
|
6126
6432
|
// src/marketCache.ts
|
|
@@ -6134,7 +6440,7 @@ function cachePath2() {
|
|
|
6134
6440
|
function readFile() {
|
|
6135
6441
|
try {
|
|
6136
6442
|
const path = cachePath2();
|
|
6137
|
-
const info =
|
|
6443
|
+
const info = statSync3(path);
|
|
6138
6444
|
if (!info.isFile() || info.size > MAX_MARKET_CACHE_BYTES) return void 0;
|
|
6139
6445
|
const bytes = readFileSync3(path);
|
|
6140
6446
|
if (bytes.byteLength > MAX_MARKET_CACHE_BYTES) return void 0;
|
|
@@ -6308,7 +6614,7 @@ function isMatchShaped(m) {
|
|
|
6308
6614
|
return !!x && typeof x === "object" && typeof x.id === "string" && typeof x.kickoff === "string" && !!x.home?.code && !!x.away?.code;
|
|
6309
6615
|
}
|
|
6310
6616
|
function nextOverall(now, fixtures = allFixtures()) {
|
|
6311
|
-
return [...fixtures].sort(byKickoff).find((m) =>
|
|
6617
|
+
return [...fixtures].sort(byKickoff).find((m) => isUpcoming(m, new Date(now)) && isResolvedFixture(m));
|
|
6312
6618
|
}
|
|
6313
6619
|
function teamTok(t2, flags) {
|
|
6314
6620
|
return flags ? t2.flag : t2.code;
|
|
@@ -6412,7 +6718,8 @@ function renderPromptLine(state, opts = {}) {
|
|
|
6412
6718
|
const live = liveList.items;
|
|
6413
6719
|
const cachedFixtureList = sealFixtures(state?.fixtures);
|
|
6414
6720
|
const cachedFixtures = [...cachedFixtureList.items];
|
|
6415
|
-
const
|
|
6721
|
+
const bundle = defaultCompetition ? allFixtures() : [];
|
|
6722
|
+
const schedule = cachedFixtures.length ? mergeLive(bundle, cachedFixtures) : defaultCompetition ? void 0 : [];
|
|
6416
6723
|
if (team) {
|
|
6417
6724
|
const mine = live.find((m) => m.home?.code === team || m.away?.code === team);
|
|
6418
6725
|
if (mine) return `\u26BD ${matchSegment(mine, compact, flags)}`;
|
|
@@ -6456,14 +6763,15 @@ function boundContext(text, marker = "") {
|
|
|
6456
6763
|
return `${points.slice(0, room).join("")}
|
|
6457
6764
|
(context truncated)${marker}`;
|
|
6458
6765
|
}
|
|
6459
|
-
function rosterPinned(t2) {
|
|
6766
|
+
function rosterPinned(t2, pin) {
|
|
6767
|
+
if (!pin) return t2;
|
|
6460
6768
|
const { team } = lookupTeam(t2.code);
|
|
6461
6769
|
return team ? { ...t2, name: team.name, flag: team.flag } : t2;
|
|
6462
6770
|
}
|
|
6463
|
-
function line(m, flags) {
|
|
6771
|
+
function line(m, flags, pin) {
|
|
6464
6772
|
const minute = m.status === "HT" ? "half-time" : m.minute ? `${m.minute}'` : "live";
|
|
6465
|
-
const h = rosterPinned(m.home);
|
|
6466
|
-
const a = rosterPinned(m.away);
|
|
6773
|
+
const h = rosterPinned(m.home, pin);
|
|
6774
|
+
const a = rosterPinned(m.away, pin);
|
|
6467
6775
|
const home = flags ? `${h.flag} ${h.name}` : h.name;
|
|
6468
6776
|
const away = flags ? `${a.name} ${a.flag}` : a.name;
|
|
6469
6777
|
return `${home} ${scoreline(m)} ${away} (${minute})`;
|
|
@@ -6472,6 +6780,7 @@ function renderHook(state, opts = {}) {
|
|
|
6472
6780
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
6473
6781
|
const team = opts.team?.toUpperCase();
|
|
6474
6782
|
const flags = opts.flags ?? true;
|
|
6783
|
+
const pin = opts.defaultCompetition ?? true;
|
|
6475
6784
|
const liveList = liveMatchesFromCache(state, now.getTime());
|
|
6476
6785
|
let live = [...liveList.items];
|
|
6477
6786
|
if (live.length === 0) return "";
|
|
@@ -6484,7 +6793,7 @@ function renderHook(state, opts = {}) {
|
|
|
6484
6793
|
}
|
|
6485
6794
|
const shown = live.slice(0, MAX_HOOK_MATCHES);
|
|
6486
6795
|
const overflow = live.length - shown.length;
|
|
6487
|
-
const lines = shown.map((mm) => line(mm, flags)).join("\n");
|
|
6796
|
+
const lines = shown.map((mm) => line(mm, flags, pin)).join("\n");
|
|
6488
6797
|
const more = !liveList.complete ? "\n(more live matches may not be shown)" : overflow > 0 ? `
|
|
6489
6798
|
(+${overflow} more not shown)` : "";
|
|
6490
6799
|
return boundContext(`[Claudinho \u2014 live football scores right now]
|
|
@@ -6504,7 +6813,7 @@ function fixturesStale(state, now) {
|
|
|
6504
6813
|
return fixturesAttemptAgeMs(state, now) > FIXTURES_EMPTY_TTL_MS;
|
|
6505
6814
|
}
|
|
6506
6815
|
function nextStaticUpcoming(nowMs) {
|
|
6507
|
-
return [...allFixtures()].sort(byKickoff).find((m) =>
|
|
6816
|
+
return [...allFixtures()].sort(byKickoff).find((m) => isUpcoming(m, new Date(nowMs)));
|
|
6508
6817
|
}
|
|
6509
6818
|
function inKnockoutPhase(nowMs) {
|
|
6510
6819
|
if (resolveCompetition() !== DEFAULT_COMPETITION) return false;
|
|
@@ -6515,12 +6824,8 @@ function liveWindowActive(nowMs) {
|
|
|
6515
6824
|
if (resolveCompetition() !== DEFAULT_COMPETITION) return true;
|
|
6516
6825
|
return inLiveWindow(nowMs);
|
|
6517
6826
|
}
|
|
6518
|
-
function liveAdapter(source) {
|
|
6519
|
-
|
|
6520
|
-
if (source === "espn" && competition === DEFAULT_COMPETITION) {
|
|
6521
|
-
return new EspnAdapter({ enrichGroups: false });
|
|
6522
|
-
}
|
|
6523
|
-
return makeAdapter(source);
|
|
6827
|
+
function liveAdapter(source, now) {
|
|
6828
|
+
return makeAdapter(source, { enrichGroups: false, now });
|
|
6524
6829
|
}
|
|
6525
6830
|
async function runRefresh(opts = {}) {
|
|
6526
6831
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
@@ -6566,7 +6871,8 @@ async function runRefresh(opts = {}) {
|
|
|
6566
6871
|
}
|
|
6567
6872
|
return;
|
|
6568
6873
|
}
|
|
6569
|
-
|
|
6874
|
+
const token = claimLock();
|
|
6875
|
+
if (!token) return;
|
|
6570
6876
|
try {
|
|
6571
6877
|
let live = base?.live ?? [];
|
|
6572
6878
|
let degraded = base?.degraded ?? false;
|
|
@@ -6575,7 +6881,9 @@ async function runRefresh(opts = {}) {
|
|
|
6575
6881
|
let fixturesUpdatedAt = base?.fixturesUpdatedAt;
|
|
6576
6882
|
let fixturesAttemptedAt = base?.fixturesAttemptedAt;
|
|
6577
6883
|
let backoffUntil = base?.backoffUntil;
|
|
6578
|
-
const
|
|
6884
|
+
const realStart = Date.now();
|
|
6885
|
+
const clock = () => nowMs + (Date.now() - realStart);
|
|
6886
|
+
const adapter = liveAdapter(source, clock);
|
|
6579
6887
|
if (needLive) {
|
|
6580
6888
|
try {
|
|
6581
6889
|
const r = await getLiveMatches(adapter, now);
|
|
@@ -6597,26 +6905,32 @@ async function runRefresh(opts = {}) {
|
|
|
6597
6905
|
} catch {
|
|
6598
6906
|
}
|
|
6599
6907
|
}
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
).toISOString();
|
|
6908
|
+
const armed = adapter.cooldownUntil;
|
|
6909
|
+
if (armed !== void 0 && armed > clock()) {
|
|
6910
|
+
const jitter = opts.jitterMs ?? Math.floor(Math.random() * BACKOFF_JITTER_MS);
|
|
6911
|
+
backoffUntil = new Date(Math.max(armed, nowMs + BACKOFF_MS) + jitter).toISOString();
|
|
6604
6912
|
} else if (backoffUntil && Date.parse(backoffUntil) <= nowMs) {
|
|
6605
6913
|
backoffUntil = void 0;
|
|
6606
6914
|
}
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6915
|
+
const published = publishState(
|
|
6916
|
+
{
|
|
6917
|
+
updatedAt,
|
|
6918
|
+
live,
|
|
6919
|
+
degraded,
|
|
6920
|
+
source,
|
|
6921
|
+
competition,
|
|
6922
|
+
...fixtures ? { fixtures } : {},
|
|
6923
|
+
...fixturesUpdatedAt ? { fixturesUpdatedAt } : {},
|
|
6924
|
+
...fixturesAttemptedAt ? { fixturesAttemptedAt } : {},
|
|
6925
|
+
...backoffUntil ? { backoffUntil } : {}
|
|
6926
|
+
},
|
|
6927
|
+
token
|
|
6928
|
+
);
|
|
6929
|
+
if (!published && process.env.CLAUDINHO_DEBUG) {
|
|
6930
|
+
process.stderr.write("claudinho: refresh lease lost to a successor; snapshot not published\n");
|
|
6931
|
+
}
|
|
6618
6932
|
} finally {
|
|
6619
|
-
releaseLock();
|
|
6933
|
+
releaseLock(token);
|
|
6620
6934
|
}
|
|
6621
6935
|
}
|
|
6622
6936
|
function shouldRefresh(now = Date.now(), state = readState("espn", resolveCompetition())) {
|
|
@@ -6648,7 +6962,7 @@ function spawnRefresh(source) {
|
|
|
6648
6962
|
}
|
|
6649
6963
|
|
|
6650
6964
|
// src/install.ts
|
|
6651
|
-
import { copyFileSync, existsSync, readFileSync as readFileSync5 } from "fs";
|
|
6965
|
+
import { copyFileSync, existsSync, lstatSync as lstatSync2, readFileSync as readFileSync5 } from "fs";
|
|
6652
6966
|
import { homedir as homedir2 } from "os";
|
|
6653
6967
|
import { join as join5 } from "path";
|
|
6654
6968
|
function claudeSettingsPath() {
|
|
@@ -6683,21 +6997,41 @@ function defaultStatusLineConfig(target, command) {
|
|
|
6683
6997
|
function restartMessage(target) {
|
|
6684
6998
|
return target === "cursor" ? "Restart Cursor CLI (or start a new session) to see it." : "Restart Claude Code to see it.";
|
|
6685
6999
|
}
|
|
7000
|
+
function isSettingsObject(v) {
|
|
7001
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
7002
|
+
}
|
|
7003
|
+
function manual(path, snippet, why) {
|
|
7004
|
+
return { action: "manual", path, message: `${why} ${path}. Add this manually:
|
|
7005
|
+
${snippet}` };
|
|
7006
|
+
}
|
|
6686
7007
|
function readSettings(path, snippet) {
|
|
6687
|
-
|
|
7008
|
+
let entry;
|
|
6688
7009
|
try {
|
|
6689
|
-
|
|
7010
|
+
entry = lstatSync2(path);
|
|
6690
7011
|
} catch {
|
|
6691
|
-
return {
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
${snippet}`
|
|
6696
|
-
};
|
|
7012
|
+
return {};
|
|
7013
|
+
}
|
|
7014
|
+
if (entry.isSymbolicLink() && !existsSync(path)) {
|
|
7015
|
+
return manual(path, snippet, "Settings path is a symlink to a missing file:");
|
|
6697
7016
|
}
|
|
7017
|
+
let parsed;
|
|
7018
|
+
try {
|
|
7019
|
+
parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
7020
|
+
} catch {
|
|
7021
|
+
return manual(path, snippet, "Could not parse");
|
|
7022
|
+
}
|
|
7023
|
+
if (!isSettingsObject(parsed)) return manual(path, snippet, "Not a JSON settings object:");
|
|
7024
|
+
return parsed;
|
|
6698
7025
|
}
|
|
6699
7026
|
function isInitResult(v) {
|
|
6700
|
-
return
|
|
7027
|
+
return typeof v.action === "string" && typeof v.path === "string" && typeof v.message === "string";
|
|
7028
|
+
}
|
|
7029
|
+
var SETTINGS_FILE_MODE = 384;
|
|
7030
|
+
var SETTINGS_WRITE = { mode: SETTINGS_FILE_MODE, followSymlinks: true };
|
|
7031
|
+
function validHookMatchers(slot) {
|
|
7032
|
+
return slot.every(
|
|
7033
|
+
(m) => isSettingsObject(m) && (m.hooks === void 0 || Array.isArray(m.hooks) && m.hooks.every(isSettingsObject))
|
|
7034
|
+
);
|
|
6701
7035
|
}
|
|
6702
7036
|
function initStatuslineFor(target, opts = {}) {
|
|
6703
7037
|
const path = configPathFor(target, opts.path);
|
|
@@ -6712,12 +7046,12 @@ function initStatuslineFor(target, opts = {}) {
|
|
|
6712
7046
|
const settings = parsed;
|
|
6713
7047
|
const existing = settings.statusLine;
|
|
6714
7048
|
if (isSameCommand(existing?.command, command)) {
|
|
6715
|
-
const
|
|
6716
|
-
return { action: "already", path, message: `${
|
|
7049
|
+
const label2 = target === "cursor" ? "Cursor CLI statusline" : "Statusline";
|
|
7050
|
+
return { action: "already", path, message: `${label2} already configured (${path}).` };
|
|
6717
7051
|
}
|
|
6718
7052
|
backupOnce(path);
|
|
6719
7053
|
settings.statusLine = sl;
|
|
6720
|
-
writeFileAtomic(path, JSON.stringify(settings, null, 2) + "\n");
|
|
7054
|
+
writeFileAtomic(path, JSON.stringify(settings, null, 2) + "\n", SETTINGS_WRITE);
|
|
6721
7055
|
const surface = target === "cursor" ? "Cursor CLI statusline" : "Statusline";
|
|
6722
7056
|
return {
|
|
6723
7057
|
action: "written",
|
|
@@ -6751,6 +7085,13 @@ function initHook(opts = {}) {
|
|
|
6751
7085
|
const parsed = readSettings(path, snippet);
|
|
6752
7086
|
if (isInitResult(parsed)) return parsed;
|
|
6753
7087
|
const settings = parsed;
|
|
7088
|
+
if (settings.hooks !== void 0 && !isSettingsObject(settings.hooks)) {
|
|
7089
|
+
return manual(path, snippet, 'Unexpected "hooks" shape in');
|
|
7090
|
+
}
|
|
7091
|
+
const eventSlot = settings.hooks?.[CLAUDE_HOOK_EVENT];
|
|
7092
|
+
if (eventSlot !== void 0 && (!Array.isArray(eventSlot) || !validHookMatchers(eventSlot))) {
|
|
7093
|
+
return manual(path, snippet, `Unexpected "hooks.${CLAUDE_HOOK_EVENT}" shape in`);
|
|
7094
|
+
}
|
|
6754
7095
|
if (claudeHookCommands(settings).some((c) => isSameCommand(c, command))) {
|
|
6755
7096
|
return {
|
|
6756
7097
|
action: "already",
|
|
@@ -6763,7 +7104,7 @@ function initHook(opts = {}) {
|
|
|
6763
7104
|
const hooks = settings.hooks;
|
|
6764
7105
|
hooks[CLAUDE_HOOK_EVENT] ??= [];
|
|
6765
7106
|
hooks[CLAUDE_HOOK_EVENT].push({ hooks: [{ type: "command", command }] });
|
|
6766
|
-
writeFileAtomic(path, JSON.stringify(settings, null, 2) + "\n");
|
|
7107
|
+
writeFileAtomic(path, JSON.stringify(settings, null, 2) + "\n", SETTINGS_WRITE);
|
|
6767
7108
|
return {
|
|
6768
7109
|
action: "written",
|
|
6769
7110
|
path,
|
|
@@ -6771,9 +7112,61 @@ function initHook(opts = {}) {
|
|
|
6771
7112
|
};
|
|
6772
7113
|
}
|
|
6773
7114
|
|
|
7115
|
+
// src/providerBackoff.ts
|
|
7116
|
+
function persistBackoff(source, competition, until, nowMs) {
|
|
7117
|
+
const token = claimLock(nowMs);
|
|
7118
|
+
if (!token) return false;
|
|
7119
|
+
try {
|
|
7120
|
+
const prior = readState(source, competition);
|
|
7121
|
+
const base = prior && prior.source === source && prior.competition === competition ? prior : {
|
|
7122
|
+
updatedAt: new Date(nowMs).toISOString(),
|
|
7123
|
+
live: [],
|
|
7124
|
+
degraded: true,
|
|
7125
|
+
// the provider refused us — never claim otherwise
|
|
7126
|
+
source,
|
|
7127
|
+
competition
|
|
7128
|
+
};
|
|
7129
|
+
return publishState({ ...base, backoffUntil: new Date(until).toISOString() }, token);
|
|
7130
|
+
} finally {
|
|
7131
|
+
releaseLock(token);
|
|
7132
|
+
}
|
|
7133
|
+
}
|
|
7134
|
+
function withPersistedBackoff(adapter, source, now = /* @__PURE__ */ new Date()) {
|
|
7135
|
+
const competition = resolveCompetition();
|
|
7136
|
+
const nowMs = now.getTime();
|
|
7137
|
+
const state = readCurrentState(source, competition);
|
|
7138
|
+
const accepted = state?.backoffUntil && backoffActive(state, nowMs) ? Date.parse(state.backoffUntil) : void 0;
|
|
7139
|
+
if (accepted !== void 0) adapter.armCooldown?.(accepted);
|
|
7140
|
+
let persisted = accepted;
|
|
7141
|
+
const persistIfNewer = (until) => {
|
|
7142
|
+
if (!(until > nowMs) || persisted !== void 0 && until <= persisted) return;
|
|
7143
|
+
if (persistBackoff(source, competition, until, nowMs)) persisted = until;
|
|
7144
|
+
};
|
|
7145
|
+
adapter.onCooldown?.(persistIfNewer);
|
|
7146
|
+
const afterCall = () => {
|
|
7147
|
+
const until = adapter.cooldownUntil;
|
|
7148
|
+
if (until !== void 0) persistIfNewer(until);
|
|
7149
|
+
};
|
|
7150
|
+
return new Proxy(adapter, {
|
|
7151
|
+
get(target, prop) {
|
|
7152
|
+
const value = Reflect.get(target, prop, target);
|
|
7153
|
+
if (typeof value === "function" && typeof prop === "string" && prop.startsWith("fetch")) {
|
|
7154
|
+
return async (...args) => {
|
|
7155
|
+
try {
|
|
7156
|
+
return await value.apply(target, args);
|
|
7157
|
+
} finally {
|
|
7158
|
+
afterCall();
|
|
7159
|
+
}
|
|
7160
|
+
};
|
|
7161
|
+
}
|
|
7162
|
+
return value;
|
|
7163
|
+
}
|
|
7164
|
+
});
|
|
7165
|
+
}
|
|
7166
|
+
|
|
6774
7167
|
// src/commands.ts
|
|
6775
|
-
function adapterFor({ cfg, adapter }) {
|
|
6776
|
-
return adapter ?? makeAdapter(cfg.source);
|
|
7168
|
+
function adapterFor({ cfg, adapter, now }) {
|
|
7169
|
+
return withPersistedBackoff(adapter ?? makeAdapter(cfg.source), cfg.source, now);
|
|
6777
7170
|
}
|
|
6778
7171
|
var DEFAULT_ON_MARKET_OPTS = { deadlineMs: 2e3, timeoutMs: 2500 };
|
|
6779
7172
|
var MARKETS_CMD_OPTS = { deadlineMs: 12e3, timeoutMs: 6e3 };
|
|
@@ -6952,20 +7345,30 @@ async function cmdNext(team, ctx) {
|
|
|
6952
7345
|
const { cfg, t: t2, now } = ctx;
|
|
6953
7346
|
precheck(cfg, t2);
|
|
6954
7347
|
const code = resolveTeamArg(team, "Usage: claudinho next <team> (or set CLAUDINHO_TEAM)", t2);
|
|
6955
|
-
const { fixture, degraded, source } = await getNextFixtureForTeam(
|
|
7348
|
+
const { fixture, degraded, source, unsupported } = await getNextFixtureForTeam(
|
|
6956
7349
|
adapterFor(ctx),
|
|
6957
7350
|
code,
|
|
6958
7351
|
now ?? /* @__PURE__ */ new Date()
|
|
6959
7352
|
);
|
|
6960
7353
|
if (cfg.json) {
|
|
6961
|
-
emitJson({
|
|
7354
|
+
emitJson({
|
|
7355
|
+
team: code,
|
|
7356
|
+
fixture: fixture ?? null,
|
|
7357
|
+
degraded,
|
|
7358
|
+
source: source ?? null,
|
|
7359
|
+
...unsupported ? { unsupported: true } : {}
|
|
7360
|
+
});
|
|
6962
7361
|
return;
|
|
6963
7362
|
}
|
|
6964
7363
|
const c = painterFor(cfg);
|
|
6965
7364
|
const flags = flagsEnabled();
|
|
6966
7365
|
out();
|
|
6967
7366
|
if (!fixture) {
|
|
6968
|
-
out(
|
|
7367
|
+
out(
|
|
7368
|
+
c.dim(
|
|
7369
|
+
" " + (unsupported ? t2("competition.unsupported") : degraded ? t2("live.degraded") : t2("next.none", { team: code }))
|
|
7370
|
+
)
|
|
7371
|
+
);
|
|
6969
7372
|
out();
|
|
6970
7373
|
out(disclaimer(t2, c));
|
|
6971
7374
|
endScoreCommand(ctx);
|
|
@@ -6997,7 +7400,7 @@ function cmdTeam(query, ctx) {
|
|
|
6997
7400
|
}
|
|
6998
7401
|
const c = painterFor(cfg);
|
|
6999
7402
|
const flags = flagsEnabled();
|
|
7000
|
-
const
|
|
7403
|
+
const label2 = (tm) => {
|
|
7001
7404
|
const flag2 = flags ? `${tm.flag} ` : "";
|
|
7002
7405
|
const grp = tm.group ? ` \xB7 ${t2("team.group", { group: tm.group })}` : "";
|
|
7003
7406
|
return ` ${flag2}${c.bold(tm.name)} ${c.dim(tm.code + grp)}`;
|
|
@@ -7006,10 +7409,10 @@ function cmdTeam(query, ctx) {
|
|
|
7006
7409
|
if (!q) {
|
|
7007
7410
|
out(" " + c.dim(t2("team.usage")));
|
|
7008
7411
|
} else if (team) {
|
|
7009
|
-
out(
|
|
7412
|
+
out(label2(team));
|
|
7010
7413
|
} else if (matches.length > 0) {
|
|
7011
7414
|
out(" " + c.dim(t2("team.ambiguous", { query: q })));
|
|
7012
|
-
for (const m of matches) out(
|
|
7415
|
+
for (const m of matches) out(label2(m));
|
|
7013
7416
|
} else {
|
|
7014
7417
|
out(" " + c.dim(t2("team.none", { query: q })));
|
|
7015
7418
|
}
|
|
@@ -7022,7 +7425,11 @@ async function cmdTable(group, ctx) {
|
|
|
7022
7425
|
precheck(cfg, t2);
|
|
7023
7426
|
const { tables, degraded, source } = await getStandings(adapterFor(ctx), group);
|
|
7024
7427
|
if (cfg.json) {
|
|
7025
|
-
const json = tables.map((tb) => ({
|
|
7428
|
+
const json = tables.map((tb) => ({
|
|
7429
|
+
group: tb.group,
|
|
7430
|
+
standings: tb.rows,
|
|
7431
|
+
...tb.partial ? { partial: tb.partial } : {}
|
|
7432
|
+
}));
|
|
7026
7433
|
emitJson({
|
|
7027
7434
|
degraded,
|
|
7028
7435
|
source: source ?? null,
|
|
@@ -7045,7 +7452,7 @@ async function cmdTable(group, ctx) {
|
|
|
7045
7452
|
out(disclaimer(t2, c));
|
|
7046
7453
|
return;
|
|
7047
7454
|
}
|
|
7048
|
-
for (const { group: g, rows } of tables) {
|
|
7455
|
+
for (const { group: g, rows, partial } of tables) {
|
|
7049
7456
|
out();
|
|
7050
7457
|
out(header(t2("table.title", { group: g }), c));
|
|
7051
7458
|
const table = new Table({
|
|
@@ -7073,6 +7480,7 @@ async function cmdTable(group, ctx) {
|
|
|
7073
7480
|
]);
|
|
7074
7481
|
}
|
|
7075
7482
|
out(table.toString());
|
|
7483
|
+
if (partial) out(c.dim(" " + t2("table.partial", { n: String(partial.omitted) })));
|
|
7076
7484
|
}
|
|
7077
7485
|
out();
|
|
7078
7486
|
if (degraded) out(c.dim(" " + t2("table.degraded")));
|
|
@@ -7089,10 +7497,22 @@ async function cmdBracket(stage, opts, ctx) {
|
|
|
7089
7497
|
if (filter && !BRACKET_STAGES.has(filter)) {
|
|
7090
7498
|
throw new InputError(t(cfg.lang, "bracket.invalidStage"));
|
|
7091
7499
|
}
|
|
7092
|
-
const { view, degraded, standingsDegraded, source } = await getBracket(
|
|
7500
|
+
const { view, degraded, standingsDegraded, source, unsupported } = await getBracket(
|
|
7093
7501
|
adapterFor(ctx),
|
|
7094
7502
|
filter ? { stage: filter, lang: cfg.lang } : { lang: cfg.lang }
|
|
7095
7503
|
);
|
|
7504
|
+
if (unsupported) {
|
|
7505
|
+
if (cfg.json) {
|
|
7506
|
+
emitJson({ degraded, standingsDegraded, source: null, view, unsupported: true });
|
|
7507
|
+
return;
|
|
7508
|
+
}
|
|
7509
|
+
const c2 = painterFor(cfg);
|
|
7510
|
+
out();
|
|
7511
|
+
out(c2.dim(` ${t(cfg.lang, "competition.unsupported")}`));
|
|
7512
|
+
out();
|
|
7513
|
+
out(disclaimer(t2, c2));
|
|
7514
|
+
return;
|
|
7515
|
+
}
|
|
7096
7516
|
if (cfg.json) {
|
|
7097
7517
|
emitJson({
|
|
7098
7518
|
degraded,
|
|
@@ -7164,7 +7584,13 @@ function cmdHook({ cfg }) {
|
|
|
7164
7584
|
try {
|
|
7165
7585
|
const team = resolveEnvTeam(process.env.CLAUDINHO_TEAM);
|
|
7166
7586
|
const state = readCurrentState(cfg.source, resolveCompetition());
|
|
7167
|
-
const ctx = renderHook(state, {
|
|
7587
|
+
const ctx = renderHook(state, {
|
|
7588
|
+
team,
|
|
7589
|
+
flags: flagsEnabled(),
|
|
7590
|
+
// The bundled roster names World Cup nations only; on another competition
|
|
7591
|
+
// a club sharing a nation's code must not be renamed to that nation.
|
|
7592
|
+
defaultCompetition: resolveCompetition() === DEFAULT_COMPETITION
|
|
7593
|
+
});
|
|
7168
7594
|
if (ctx) out(ctx);
|
|
7169
7595
|
if (!state && !isLockFresh() || shouldRefresh(Date.now(), state) || shouldRefreshFixtures(Date.now(), state)) {
|
|
7170
7596
|
spawnRefresh(cfg.source);
|
|
@@ -7251,7 +7677,7 @@ function cmdInitClaude(opts, { cfg }) {
|
|
|
7251
7677
|
async function cmdMatch(id, ctx) {
|
|
7252
7678
|
const { cfg, t: t2 } = ctx;
|
|
7253
7679
|
precheck(cfg, t2);
|
|
7254
|
-
const { match, degraded, source: liveSource } = await getMatchById(adapterFor(ctx), id);
|
|
7680
|
+
const { match, degraded, source: liveSource, unsupported } = await getMatchById(adapterFor(ctx), id);
|
|
7255
7681
|
const market = match ? await reliableMarketSignalFor(ctx, match) : { signal: void 0, complete: true };
|
|
7256
7682
|
if (cfg.json) {
|
|
7257
7683
|
emitJson({
|
|
@@ -7259,14 +7685,15 @@ async function cmdMatch(id, ctx) {
|
|
|
7259
7685
|
match: match ?? null,
|
|
7260
7686
|
source: liveSource ?? null,
|
|
7261
7687
|
marketComplete: market.complete,
|
|
7262
|
-
marketSignal: market.signal ?? null
|
|
7688
|
+
marketSignal: market.signal ?? null,
|
|
7689
|
+
...unsupported ? { unsupported: true } : {}
|
|
7263
7690
|
});
|
|
7264
7691
|
return;
|
|
7265
7692
|
}
|
|
7266
7693
|
const c = painterFor(cfg);
|
|
7267
7694
|
out();
|
|
7268
7695
|
if (!match) {
|
|
7269
|
-
out(c.dim(" " + t2("match.none", { id })));
|
|
7696
|
+
out(c.dim(" " + (unsupported ? t2("competition.unsupported") : t2("match.none", { id }))));
|
|
7270
7697
|
out();
|
|
7271
7698
|
out(disclaimer(t2, c));
|
|
7272
7699
|
return;
|
|
@@ -7303,6 +7730,7 @@ async function cmdMatch(id, ctx) {
|
|
|
7303
7730
|
maybeStarNudge(ctx);
|
|
7304
7731
|
}
|
|
7305
7732
|
var MARKET_INFO = "Prediction-market data is informational only.";
|
|
7733
|
+
var MARKETS_SCOPE_NOTE = "Market signals cover the World Cup only; none are read for this competition.";
|
|
7306
7734
|
function marketDisplayable(match, sig) {
|
|
7307
7735
|
return marketSignalRendersFor(match, sig) && !sig.ambiguous && sig.favorite != null && hasSaneDistribution(sig.outcomes);
|
|
7308
7736
|
}
|
|
@@ -7311,6 +7739,7 @@ function marketHeaderLine(m, cfg) {
|
|
|
7311
7739
|
return `${m.home.flag} ${m.home.name} vs ${m.away.name} ${m.away.flag} \xB7 ${when}`;
|
|
7312
7740
|
}
|
|
7313
7741
|
function noSignalLine(m, now) {
|
|
7742
|
+
if (!marketsCoverCompetition()) return MARKETS_SCOPE_NOTE;
|
|
7314
7743
|
if (marketRelevant(m, now)) return "No market signal for this match.";
|
|
7315
7744
|
return isFinished(m.status) ? "Match has finished \u2014 market signals are pre-match and in-play reads." : "Match appears to have finished \u2014 market signals are pre-match and in-play reads.";
|
|
7316
7745
|
}
|
|
@@ -7323,7 +7752,7 @@ async function cmdMarkets(target, team, ctx) {
|
|
|
7323
7752
|
precheck(cfg, t2);
|
|
7324
7753
|
const code = resolveTeamArg(team, "Usage: claudinho markets next <team> (or set CLAUDINHO_TEAM)", t2);
|
|
7325
7754
|
const now2 = ctx.now ?? /* @__PURE__ */ new Date();
|
|
7326
|
-
const { match: fixture, degraded } = await marketFixtureForTeam(adapterFor(ctx), code, now2);
|
|
7755
|
+
const { match: fixture, degraded, unsupported } = await marketFixtureForTeam(adapterFor(ctx), code, now2);
|
|
7327
7756
|
const market = fixture && marketRelevant(fixture, now2) ? await marketSignalsFor(ctx, [fixture], MARKETS_CMD_OPTS) : { signals: /* @__PURE__ */ new Map(), complete: true };
|
|
7328
7757
|
const sig = fixture ? market.signals.get(fixture.id) : void 0;
|
|
7329
7758
|
const shown = market.complete && fixture && sig && marketDisplayable(fixture, sig) ? sig : void 0;
|
|
@@ -7334,14 +7763,21 @@ async function cmdMarkets(target, team, ctx) {
|
|
|
7334
7763
|
degraded,
|
|
7335
7764
|
informationalOnly: true,
|
|
7336
7765
|
complete: market.complete,
|
|
7337
|
-
signal: shown ?? null
|
|
7766
|
+
signal: shown ?? null,
|
|
7767
|
+
// Review P2 on #129: a JSON consumer must tell "not available for this
|
|
7768
|
+
// competition" from a successful empty result; the text branch already did.
|
|
7769
|
+
...unsupported ? { unsupported: true } : {}
|
|
7338
7770
|
});
|
|
7339
7771
|
return;
|
|
7340
7772
|
}
|
|
7341
7773
|
const c2 = painterFor(cfg);
|
|
7342
7774
|
out();
|
|
7343
7775
|
if (!fixture) {
|
|
7344
|
-
out(
|
|
7776
|
+
out(
|
|
7777
|
+
c2.dim(
|
|
7778
|
+
" " + (unsupported ? t2("competition.unsupported") : degraded ? t2("live.degraded") : t2("next.none", { team: code }))
|
|
7779
|
+
)
|
|
7780
|
+
);
|
|
7345
7781
|
} else {
|
|
7346
7782
|
out(header(marketHeaderLine(fixture, cfg), c2));
|
|
7347
7783
|
out();
|
|
@@ -7358,7 +7794,7 @@ async function cmdMarkets(target, team, ctx) {
|
|
|
7358
7794
|
if (target && target !== "today" && !isValidDate(target)) {
|
|
7359
7795
|
precheck(cfg, t2);
|
|
7360
7796
|
const now2 = ctx.now ?? /* @__PURE__ */ new Date();
|
|
7361
|
-
const { match } = await getMatchById(adapterFor(ctx), target);
|
|
7797
|
+
const { match, unsupported } = await getMatchById(adapterFor(ctx), target);
|
|
7362
7798
|
const market = match && marketRelevant(match, now2) ? await marketSignalsFor(ctx, [match], MARKETS_CMD_OPTS) : { signals: /* @__PURE__ */ new Map(), complete: true };
|
|
7363
7799
|
const sig = match ? market.signals.get(match.id) : void 0;
|
|
7364
7800
|
const shown = market.complete && match && sig && marketDisplayable(match, sig) ? sig : void 0;
|
|
@@ -7367,14 +7803,15 @@ async function cmdMarkets(target, team, ctx) {
|
|
|
7367
7803
|
matchId: target,
|
|
7368
7804
|
informationalOnly: true,
|
|
7369
7805
|
complete: market.complete,
|
|
7370
|
-
signal: shown ?? null
|
|
7806
|
+
signal: shown ?? null,
|
|
7807
|
+
...unsupported ? { unsupported: true } : {}
|
|
7371
7808
|
});
|
|
7372
7809
|
return;
|
|
7373
7810
|
}
|
|
7374
7811
|
const c2 = painterFor(cfg);
|
|
7375
7812
|
out();
|
|
7376
7813
|
if (!match) {
|
|
7377
|
-
out(c2.dim(" " + t2("match.none", { id: target })));
|
|
7814
|
+
out(c2.dim(" " + (unsupported ? t2("competition.unsupported") : t2("match.none", { id: target }))));
|
|
7378
7815
|
} else {
|
|
7379
7816
|
out(header(marketHeaderLine(match, cfg), c2));
|
|
7380
7817
|
out();
|
|
@@ -7412,7 +7849,7 @@ async function cmdMarkets(target, team, ctx) {
|
|
|
7412
7849
|
if (rows.length === 0) {
|
|
7413
7850
|
out(
|
|
7414
7851
|
c.dim(
|
|
7415
|
-
complete ? ` No market signals available for ${date}.` : ` Market data unavailable or incomplete for ${date} \u2014 not all fixtures could be checked.`
|
|
7852
|
+
!marketsCoverCompetition() ? ` ${MARKETS_SCOPE_NOTE}` : complete ? ` No market signals available for ${date}.` : ` Market data unavailable or incomplete for ${date} \u2014 not all fixtures could be checked.`
|
|
7416
7853
|
)
|
|
7417
7854
|
);
|
|
7418
7855
|
} else {
|
|
@@ -7455,7 +7892,9 @@ function emitShare(ctx, e, copy) {
|
|
|
7455
7892
|
snippet,
|
|
7456
7893
|
matches: e.input.matches,
|
|
7457
7894
|
marketComplete: e.input.marketComplete ?? true,
|
|
7458
|
-
marketSignals: Object.fromEntries(e.input.marketSignals ?? /* @__PURE__ */ new Map())
|
|
7895
|
+
marketSignals: Object.fromEntries(e.input.marketSignals ?? /* @__PURE__ */ new Map()),
|
|
7896
|
+
// The structured card keeps the verdict the snippet's note carries.
|
|
7897
|
+
...e.unsupported ? { unsupported: true } : {}
|
|
7459
7898
|
});
|
|
7460
7899
|
} else {
|
|
7461
7900
|
out(snippet);
|
|
@@ -7487,7 +7926,12 @@ function emitShareTable(ctx, e, copy) {
|
|
|
7487
7926
|
degraded: e.degraded,
|
|
7488
7927
|
informationalOnly: true,
|
|
7489
7928
|
snippet,
|
|
7490
|
-
|
|
7929
|
+
// The structured card keeps the verdict the snippet warns about (A01).
|
|
7930
|
+
tables: e.tables.map((tb) => ({
|
|
7931
|
+
group: tb.group,
|
|
7932
|
+
standings: tb.rows,
|
|
7933
|
+
...tb.partial ? { partial: tb.partial } : {}
|
|
7934
|
+
}))
|
|
7491
7935
|
});
|
|
7492
7936
|
} else {
|
|
7493
7937
|
out(snippet);
|
|
@@ -7518,7 +7962,8 @@ function emitShareBracket(ctx, e, copy) {
|
|
|
7518
7962
|
degraded: e.degraded,
|
|
7519
7963
|
informationalOnly: true,
|
|
7520
7964
|
snippet,
|
|
7521
|
-
view: e.view
|
|
7965
|
+
view: e.view,
|
|
7966
|
+
...e.unsupported ? { unsupported: true } : {}
|
|
7522
7967
|
});
|
|
7523
7968
|
} else {
|
|
7524
7969
|
out(snippet);
|
|
@@ -7592,7 +8037,7 @@ async function cmdShare(target, team, opts, ctx) {
|
|
|
7592
8037
|
if (stageFilter && !BRACKET_STAGES.has(stageFilter)) {
|
|
7593
8038
|
throw new InputError(t(cfg.lang, "bracket.invalidStage"));
|
|
7594
8039
|
}
|
|
7595
|
-
const { view, degraded: degraded2, source: source2 } = await getBracket(
|
|
8040
|
+
const { view, degraded: degraded2, source: source2, unsupported } = await getBracket(
|
|
7596
8041
|
adapterFor(ctx),
|
|
7597
8042
|
stageFilter ? { stage: stageFilter, lang: cfg.lang } : { lang: cfg.lang }
|
|
7598
8043
|
);
|
|
@@ -7604,7 +8049,8 @@ async function cmdShare(target, team, opts, ctx) {
|
|
|
7604
8049
|
source: degraded2 ? void 0 : source2,
|
|
7605
8050
|
degraded: degraded2,
|
|
7606
8051
|
installLine: stageFilter ? `npx @claudinho/cli bracket ${stageFilter}` : "npx @claudinho/cli bracket",
|
|
7607
|
-
emptyNote: t(cfg.lang, "bracket.empty"),
|
|
8052
|
+
emptyNote: unsupported ? t(cfg.lang, "competition.unsupported") : t(cfg.lang, "bracket.empty"),
|
|
8053
|
+
unsupported,
|
|
7608
8054
|
options: {
|
|
7609
8055
|
includeHashtag: baseOptions.includeHashtag,
|
|
7610
8056
|
includeInstallLine: baseOptions.includeInstallLine,
|
|
@@ -7620,7 +8066,7 @@ async function cmdShare(target, team, opts, ctx) {
|
|
|
7620
8066
|
if (target === "next") {
|
|
7621
8067
|
precheck(cfg, t2);
|
|
7622
8068
|
const code = resolveTeamArg(team, "Usage: claudinho share next <team> (or set CLAUDINHO_TEAM)", t2);
|
|
7623
|
-
const { fixture, degraded: degraded2, source: source2 } = await getNextFixtureForTeam(
|
|
8069
|
+
const { fixture, degraded: degraded2, source: source2, unsupported } = await getNextFixtureForTeam(
|
|
7624
8070
|
adapterFor(ctx),
|
|
7625
8071
|
code,
|
|
7626
8072
|
ctx.now ?? /* @__PURE__ */ new Date()
|
|
@@ -7644,12 +8090,13 @@ async function cmdShare(target, team, opts, ctx) {
|
|
|
7644
8090
|
source: source2,
|
|
7645
8091
|
degraded: degraded2,
|
|
7646
8092
|
// Fail-closed: an outage must never paste as "no fixture" (eliminated).
|
|
7647
|
-
emptyNote: degraded2 ? `Couldn't reach the data provider \u2014 no upcoming fixture confirmed for ${code}.` : `No upcoming fixture found for ${code}.`,
|
|
8093
|
+
emptyNote: unsupported ? t(cfg.lang, "competition.unsupported") : degraded2 ? `Couldn't reach the data provider \u2014 no upcoming fixture confirmed for ${code}.` : `No upcoming fixture found for ${code}.`,
|
|
7648
8094
|
installLine: `npx @claudinho/cli next ${code}`,
|
|
7649
8095
|
tz: cfg.tz,
|
|
7650
8096
|
locale: cfg.lang
|
|
7651
8097
|
},
|
|
7652
|
-
options: baseOptions
|
|
8098
|
+
options: baseOptions,
|
|
8099
|
+
unsupported
|
|
7653
8100
|
},
|
|
7654
8101
|
copy
|
|
7655
8102
|
);
|
|
@@ -7657,7 +8104,7 @@ async function cmdShare(target, team, opts, ctx) {
|
|
|
7657
8104
|
}
|
|
7658
8105
|
if (target && target !== "today" && !isValidDate(target)) {
|
|
7659
8106
|
precheck(cfg, t2);
|
|
7660
|
-
const { match, degraded: degraded2, source: source2 } = await getMatchById(adapterFor(ctx), target);
|
|
8107
|
+
const { match, degraded: degraded2, source: source2, unsupported } = await getMatchById(adapterFor(ctx), target);
|
|
7661
8108
|
const matches = match ? [match] : [];
|
|
7662
8109
|
const market2 = await reliableShareSignals(ctx, matches);
|
|
7663
8110
|
emitShare(
|
|
@@ -7672,12 +8119,13 @@ async function cmdShare(target, team, opts, ctx) {
|
|
|
7672
8119
|
marketComplete: market2.complete,
|
|
7673
8120
|
source: source2,
|
|
7674
8121
|
degraded: degraded2,
|
|
7675
|
-
emptyNote: `No match found with id ${target}.`,
|
|
8122
|
+
emptyNote: unsupported ? t(cfg.lang, "competition.unsupported") : `No match found with id ${target}.`,
|
|
7676
8123
|
installLine: `npx @claudinho/cli match ${target}`,
|
|
7677
8124
|
tz: cfg.tz,
|
|
7678
8125
|
locale: cfg.lang
|
|
7679
8126
|
},
|
|
7680
|
-
options: baseOptions
|
|
8127
|
+
options: baseOptions,
|
|
8128
|
+
unsupported
|
|
7681
8129
|
},
|
|
7682
8130
|
copy
|
|
7683
8131
|
);
|
|
@@ -7829,7 +8277,7 @@ function handlePipeError(stream) {
|
|
|
7829
8277
|
}
|
|
7830
8278
|
handlePipeError(process.stdout);
|
|
7831
8279
|
handlePipeError(process.stderr);
|
|
7832
|
-
var VERSION = "0.
|
|
8280
|
+
var VERSION = "0.10.1";
|
|
7833
8281
|
var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
|
|
7834
8282
|
function ctxFrom(cmd) {
|
|
7835
8283
|
let root = cmd;
|