@gotza02/sequential-thinking 10000.2.1 → 10000.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md.bak +197 -0
- package/dist/.gemini_graph_cache.json.bak +1641 -0
- package/dist/graph.d.ts +7 -0
- package/dist/graph.js +136 -113
- package/dist/intelligent-code.d.ts +8 -0
- package/dist/intelligent-code.js +152 -125
- package/dist/intelligent-code.test.d.ts +1 -0
- package/dist/intelligent-code.test.js +104 -0
- package/dist/lib/formatters.d.ts +9 -0
- package/dist/lib/formatters.js +119 -0
- package/dist/lib/validators.d.ts +45 -0
- package/dist/lib/validators.js +232 -0
- package/dist/lib.js +23 -265
- package/dist/tools/sports/tools/match-calculations.d.ts +51 -0
- package/dist/tools/sports/tools/match-calculations.js +171 -0
- package/dist/tools/sports/tools/match-helpers.d.ts +21 -0
- package/dist/tools/sports/tools/match-helpers.js +57 -0
- package/dist/tools/sports/tools/match.js +19 -122
- package/dist/tools/sports.js +3 -3
- package/dist/utils.d.ts +111 -44
- package/dist/utils.js +510 -305
- package/dist/utils.test.js +3 -3
- package/package.json +1 -1
|
@@ -10,47 +10,10 @@ import { getGlobalCache, CacheService } from '../core/cache.js';
|
|
|
10
10
|
import { formatMatchesTable, formatScore, formatMatchStatus } from '../utils/formatter.js';
|
|
11
11
|
import { CACHE_CONFIG, LEAGUES } from '../core/constants.js';
|
|
12
12
|
import { logger } from '../../../utils.js';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function getDateContext() {
|
|
18
|
-
const now = new Date();
|
|
19
|
-
const month = now.toLocaleDateString('en-US', { month: 'long' });
|
|
20
|
-
const year = now.getFullYear();
|
|
21
|
-
return ` ${month} ${year}`;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Normalize team names for better matching
|
|
25
|
-
*/
|
|
26
|
-
function normalizeTeamName(name) {
|
|
27
|
-
return name
|
|
28
|
-
.toLowerCase()
|
|
29
|
-
.replace(/\b(fc|afc|sc|cf|united|utd|city|town|athletic|albion|rovers|wanderers|olympic|real)\b/g, '')
|
|
30
|
-
.replace(/\s+/g, ' ')
|
|
31
|
-
.trim();
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Check if a team name matches exactly or is contained within another name
|
|
35
|
-
* Uses more strict matching to avoid false positives
|
|
36
|
-
*/
|
|
37
|
-
function isTeamMatch(teamName, matchName) {
|
|
38
|
-
const normalizedSearch = normalizeTeamName(teamName);
|
|
39
|
-
const normalizedMatch = normalizeTeamName(matchName);
|
|
40
|
-
// Exact match
|
|
41
|
-
if (normalizedSearch === normalizedMatch)
|
|
42
|
-
return true;
|
|
43
|
-
// Check if search is contained as a word in match
|
|
44
|
-
const searchWords = normalizedSearch.split(/\s+/).filter(w => w.length > 2);
|
|
45
|
-
const matchWords = normalizedMatch.split(/\s+/).filter(w => w.length > 2);
|
|
46
|
-
// Must have at least one significant word in common
|
|
47
|
-
const commonWords = searchWords.filter(w => matchWords.includes(w));
|
|
48
|
-
// For longer names, require more matching words
|
|
49
|
-
if (searchWords.length >= 2) {
|
|
50
|
-
return commonWords.length >= Math.min(2, searchWords.length);
|
|
51
|
-
}
|
|
52
|
-
return commonWords.length > 0;
|
|
53
|
-
}
|
|
13
|
+
import { getDateContext, isTeamMatch, getAnalysisInstructions } from './match-helpers.js';
|
|
14
|
+
// Note: calculateAdaptiveTTL, calculateProbabilityRange, calculateFormPoints,
|
|
15
|
+
// calculateExpectedGoals, calculateProbabilitiesFromForm, and getLeagueId
|
|
16
|
+
// are defined locally in this file with specific implementations for match analysis
|
|
54
17
|
/**
|
|
55
18
|
* Try to find match ID from API by team names and league
|
|
56
19
|
*/
|
|
@@ -367,65 +330,6 @@ async function scrapeContentWithErrorHandling(url, type) {
|
|
|
367
330
|
return `${type.charAt(0).toUpperCase() + type.slice(1)} scrape failed: ${errorMsg}`;
|
|
368
331
|
}
|
|
369
332
|
}
|
|
370
|
-
/**
|
|
371
|
-
* Get analysis framework instructions
|
|
372
|
-
*/
|
|
373
|
-
function getAnalysisInstructions() {
|
|
374
|
-
return `INSTRUCTIONS: Act as a World-Class Football Analysis Panel. Provide a deep, non-obvious analysis using this framework:\n\n` +
|
|
375
|
-
`1. 📊 THE DATA SCIENTIST (Quantitative):\n - Analyze xG trends & Possession stats.\n - Assess Home/Away variance.\n\n` +
|
|
376
|
-
`2. 🧠 THE TACTICAL SCOUT (Qualitative):\n - Stylistic Matchup (Press vs Block).\n - Key Battles.\n\n` +
|
|
377
|
-
`3. 🚑 THE PHYSIO (Physical Condition):\n - FATIGUE CHECK: Days rest? Travel distance?\n - Squad Depth: Who has the better bench?\n\n` +
|
|
378
|
-
`4. 🎯 SET PIECE ANALYST:\n - Corners/Free Kicks: Strong vs Weak?\n - Who is most likely to score from a header?\n\n` +
|
|
379
|
-
`5. 💎 THE INSIDER (External Factors):\n - Market Movements (Odds dropping?).\n - Referee & Weather impact.\n\n` +
|
|
380
|
-
`6. 🕵️ THE SKEPTIC & SCENARIOS:\n - Why might the favorite LOSE?\n - Game Script: "If Team A scores first..."\n\n` +
|
|
381
|
-
`🏆 FINAL VERDICT:\n - Asian Handicap Leans\n - Goal Line (Over/Under)\n - The "Value Pick"`;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Calculate adaptive TTL based on data type and match timing
|
|
385
|
-
*/
|
|
386
|
-
function calculateAdaptiveTTL(dataType, matchDate) {
|
|
387
|
-
const now = Date.now();
|
|
388
|
-
const hoursUntilMatch = matchDate
|
|
389
|
-
? (matchDate.getTime() - now) / (1000 * 60 * 60)
|
|
390
|
-
: Infinity;
|
|
391
|
-
switch (dataType) {
|
|
392
|
-
case 'odds':
|
|
393
|
-
// Odds change frequently - short TTL
|
|
394
|
-
if (hoursUntilMatch < 1)
|
|
395
|
-
return 60 * 1000; // 1 minute
|
|
396
|
-
if (hoursUntilMatch < 24)
|
|
397
|
-
return 5 * 60 * 1000; // 5 minutes
|
|
398
|
-
return 15 * 60 * 1000; // 15 minutes
|
|
399
|
-
case 'lineups':
|
|
400
|
-
// Lineups announced ~1 hour before match
|
|
401
|
-
if (hoursUntilMatch < 2)
|
|
402
|
-
return 5 * 60 * 1000; // 5 minutes
|
|
403
|
-
return 60 * 60 * 1000; // 1 hour
|
|
404
|
-
case 'news':
|
|
405
|
-
// News changes throughout the day
|
|
406
|
-
if (hoursUntilMatch < 24)
|
|
407
|
-
return 15 * 60 * 1000; // 15 minutes
|
|
408
|
-
return 60 * 60 * 1000; // 1 hour
|
|
409
|
-
case 'stats':
|
|
410
|
-
case 'h2h':
|
|
411
|
-
// Historical stats don't change
|
|
412
|
-
return 24 * 60 * 60 * 1000; // 24 hours
|
|
413
|
-
case 'form':
|
|
414
|
-
// Form updates after each match
|
|
415
|
-
if (hoursUntilMatch < 0)
|
|
416
|
-
return 60 * 1000; // Live match - 1 minute
|
|
417
|
-
return 6 * 60 * 60 * 1000; // 6 hours
|
|
418
|
-
case 'match':
|
|
419
|
-
// Match data changes based on timing
|
|
420
|
-
if (hoursUntilMatch < 0)
|
|
421
|
-
return 60 * 1000; // Live - 1 minute
|
|
422
|
-
if (hoursUntilMatch < 1)
|
|
423
|
-
return 5 * 60 * 1000; // 5 minutes
|
|
424
|
-
return 30 * 60 * 1000; // 30 minutes
|
|
425
|
-
default:
|
|
426
|
-
return 30 * 60 * 1000; // 30 minutes default
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
333
|
/**
|
|
430
334
|
* Perform comprehensive match analysis using web search
|
|
431
335
|
*/
|
|
@@ -487,28 +391,6 @@ async function scrapeDeepDiveSources(candidateUrls) {
|
|
|
487
391
|
}
|
|
488
392
|
return output;
|
|
489
393
|
}
|
|
490
|
-
/**
|
|
491
|
-
* Calculate probability range with uncertainty quantification
|
|
492
|
-
*/
|
|
493
|
-
function calculateProbabilityRange(baseProbability, dataQuality) {
|
|
494
|
-
// Higher data quality = narrower range
|
|
495
|
-
const qualityFactor = dataQuality.score / 100;
|
|
496
|
-
const baseUncertainty = 0.15; // 15% base uncertainty
|
|
497
|
-
// Adjust uncertainty based on data quality (better quality = smaller range)
|
|
498
|
-
const adjustedUncertainty = baseUncertainty * (1 - qualityFactor * 0.7);
|
|
499
|
-
// Calculate range using standard error formula
|
|
500
|
-
// For binomial proportion: SE = sqrt(p*(1-p)/n)
|
|
501
|
-
// We use a simplified version based on data quality
|
|
502
|
-
const mid = baseProbability;
|
|
503
|
-
// Margin decreases with better data quality
|
|
504
|
-
// Using 1.96 for ~95% confidence interval (2 standard deviations)
|
|
505
|
-
const margin = adjustedUncertainty * Math.sqrt(mid * (1 - mid) + 0.1);
|
|
506
|
-
return {
|
|
507
|
-
low: Math.max(0, mid - margin * 1.96),
|
|
508
|
-
mid: Math.min(1, Math.max(0, mid)),
|
|
509
|
-
high: Math.min(1, mid + margin * 1.96),
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
394
|
/**
|
|
513
395
|
* Detect value bets from predictions and odds
|
|
514
396
|
*/
|
|
@@ -1139,6 +1021,21 @@ Requires API provider with match ID.`, {
|
|
|
1139
1021
|
});
|
|
1140
1022
|
}
|
|
1141
1023
|
// ============= Helper Functions =============
|
|
1024
|
+
/**
|
|
1025
|
+
* Calculate probability range with uncertainty quantification
|
|
1026
|
+
*/
|
|
1027
|
+
function calculateProbabilityRange(baseProbability, dataQuality) {
|
|
1028
|
+
const qualityFactor = dataQuality.score / 100;
|
|
1029
|
+
const baseUncertainty = 0.15;
|
|
1030
|
+
const adjustedUncertainty = baseUncertainty * (1 - qualityFactor * 0.7);
|
|
1031
|
+
const mid = baseProbability;
|
|
1032
|
+
const margin = adjustedUncertainty * Math.sqrt(mid * (1 - mid) + 0.1);
|
|
1033
|
+
return {
|
|
1034
|
+
low: Math.max(0, mid - margin * 1.96),
|
|
1035
|
+
mid: Math.min(1, Math.max(0, mid)),
|
|
1036
|
+
high: Math.min(1, mid + margin * 1.96),
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1142
1039
|
/**
|
|
1143
1040
|
* Get league ID from league name
|
|
1144
1041
|
*/
|
package/dist/tools/sports.js
CHANGED
|
@@ -91,7 +91,7 @@ function registerLegacyAnalyzeMatch(server) {
|
|
|
91
91
|
const data = await response.json();
|
|
92
92
|
const items = data.web?.results || [];
|
|
93
93
|
items.forEach((item) => candidateUrls.push(item.url));
|
|
94
|
-
resultsText = items.map((item) => `- [${item.title}](${item.url}): ${item.description}`).join('\n');
|
|
94
|
+
resultsText = items.map((item) => `- [${item.title}](${item.url}): ${item.description ?? ''}`).join('\n');
|
|
95
95
|
}
|
|
96
96
|
else if (searchProvider === 'exa') {
|
|
97
97
|
const response = await fetchWithRetry('https://api.exa.ai/search', {
|
|
@@ -107,7 +107,7 @@ function registerLegacyAnalyzeMatch(server) {
|
|
|
107
107
|
const data = await response.json();
|
|
108
108
|
const items = data.results || [];
|
|
109
109
|
items.forEach((item) => candidateUrls.push(item.url));
|
|
110
|
-
resultsText = items.map((item) => `- [${item.title}](${item.url}): ${item.text
|
|
110
|
+
resultsText = items.map((item) => `- [${item.title}](${item.url}): ${item.text ?? item.title}`).join('\n');
|
|
111
111
|
}
|
|
112
112
|
else if (searchProvider === 'google') {
|
|
113
113
|
const response = await fetchWithRetry(`https://www.googleapis.com/customsearch/v1?key=${process.env.GOOGLE_SEARCH_API_KEY}&cx=${process.env.GOOGLE_SEARCH_CX}&q=${encodeURIComponent(q.query)}&num=4`);
|
|
@@ -116,7 +116,7 @@ function registerLegacyAnalyzeMatch(server) {
|
|
|
116
116
|
const data = await response.json();
|
|
117
117
|
const items = data.items || [];
|
|
118
118
|
items.forEach((item) => candidateUrls.push(item.link));
|
|
119
|
-
resultsText = items.map((item) => `- [${item.title}](${item.link}): ${item.snippet}`).join('\n');
|
|
119
|
+
resultsText = items.map((item) => `- [${item.title}](${item.link}): ${item.snippet ?? ''}`).join('\n');
|
|
120
120
|
}
|
|
121
121
|
return `### ${q.type}\n${resultsText}\n`;
|
|
122
122
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,63 +1,130 @@
|
|
|
1
|
+
declare const CONFIG: {
|
|
2
|
+
readonly COMMAND_TIMEOUT: 60000;
|
|
3
|
+
readonly MAX_BUFFER: number;
|
|
4
|
+
readonly MAX_PATTERN_LENGTH: 500;
|
|
5
|
+
readonly MAX_QUANTIFIERS: 20;
|
|
6
|
+
readonly MAX_LOCKS: 1000;
|
|
7
|
+
readonly DNS_TIMEOUT: 5000;
|
|
8
|
+
readonly MAX_INPUT_LENGTH: 10000;
|
|
9
|
+
readonly RETRY: {
|
|
10
|
+
readonly MAX_RETRIES: 3;
|
|
11
|
+
readonly BASE_DELAY: 1000;
|
|
12
|
+
readonly BACKOFF_MULTIPLIER: 2;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Whitelist of safe commands. Consider loading from config file for flexibility.
|
|
17
|
+
*/
|
|
18
|
+
declare const SAFE_COMMANDS: ReadonlySet<string>;
|
|
19
|
+
export declare class SecurityError extends Error {
|
|
20
|
+
readonly code: string;
|
|
21
|
+
constructor(message: string, code: string);
|
|
22
|
+
}
|
|
23
|
+
export declare class ValidationError extends Error {
|
|
24
|
+
readonly field?: string | undefined;
|
|
25
|
+
constructor(message: string, field?: string | undefined);
|
|
26
|
+
}
|
|
27
|
+
export declare class TimeoutError extends Error {
|
|
28
|
+
readonly timeout: number;
|
|
29
|
+
constructor(message: string, timeout: number);
|
|
30
|
+
}
|
|
31
|
+
interface ParsedCommand {
|
|
32
|
+
command: string;
|
|
33
|
+
args: string[];
|
|
34
|
+
}
|
|
1
35
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* @param command - The command string to execute
|
|
6
|
-
* @param options - Execution options (timeout, maxBuffer)
|
|
7
|
-
* @returns Promise with stdout and stderr
|
|
8
|
-
* @throws Error if command contains shell metacharacters or is not whitelisted
|
|
36
|
+
* Robust command parser supporting quotes and escapes.
|
|
37
|
+
* State machine implementation for correctness.
|
|
9
38
|
*/
|
|
10
|
-
|
|
39
|
+
declare function parseCommand(command: string): ParsedCommand;
|
|
40
|
+
export interface ExecOptions {
|
|
11
41
|
timeout?: number;
|
|
12
42
|
maxBuffer?: number;
|
|
13
|
-
|
|
43
|
+
cwd?: string;
|
|
44
|
+
env?: NodeJS.ProcessEnv;
|
|
45
|
+
}
|
|
46
|
+
export interface ExecResult {
|
|
14
47
|
stdout: string;
|
|
15
48
|
stderr: string;
|
|
16
|
-
|
|
49
|
+
exitCode: number;
|
|
50
|
+
executionTime: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Execute command securely using spawn (no shell interpretation).
|
|
54
|
+
*/
|
|
55
|
+
export declare function execAsync(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
17
56
|
export declare class AsyncMutex {
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
|
|
57
|
+
private queue;
|
|
58
|
+
private locked;
|
|
59
|
+
acquire(): Promise<() => void>;
|
|
60
|
+
private release;
|
|
61
|
+
dispatch<T>(fn: () => T | PromiseLike<T>): Promise<T>;
|
|
62
|
+
}
|
|
63
|
+
export declare class FileLock {
|
|
64
|
+
private locks;
|
|
65
|
+
private queue;
|
|
66
|
+
private readonly maxLocks;
|
|
67
|
+
constructor(maxLocks?: 1000);
|
|
68
|
+
acquire(filePath: string, timeout?: number): Promise<{
|
|
69
|
+
release: () => void;
|
|
70
|
+
}>;
|
|
71
|
+
private waitForLock;
|
|
72
|
+
private release;
|
|
73
|
+
private cleanupStaleLocks;
|
|
74
|
+
getLockCount(): number;
|
|
75
|
+
getQueueCount(): number;
|
|
21
76
|
}
|
|
77
|
+
export declare const fileLock: FileLock;
|
|
22
78
|
export declare function validatePath(requestedPath: string): string;
|
|
79
|
+
declare function isPrivateIPv4(ip: string): boolean;
|
|
80
|
+
declare function isPrivateIPv6(ip: string): boolean;
|
|
23
81
|
export declare function validatePublicUrl(urlString: string): Promise<void>;
|
|
24
82
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
83
|
+
interface LoggerOptions {
|
|
84
|
+
level?: LogLevel;
|
|
85
|
+
useColors?: boolean;
|
|
86
|
+
output?: 'stdout' | 'stderr';
|
|
87
|
+
}
|
|
25
88
|
declare class Logger {
|
|
26
89
|
private level;
|
|
27
|
-
|
|
90
|
+
private useColors;
|
|
91
|
+
private output;
|
|
92
|
+
private static readonly LEVELS;
|
|
93
|
+
private static readonly COLORS;
|
|
94
|
+
constructor(options?: LoggerOptions);
|
|
28
95
|
private shouldLog;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
96
|
+
private formatMessage;
|
|
97
|
+
private print;
|
|
98
|
+
debug(msg: string, ...args: unknown[]): void;
|
|
99
|
+
info(msg: string, ...args: unknown[]): void;
|
|
100
|
+
warn(msg: string, ...args: unknown[]): void;
|
|
101
|
+
error(msg: string, ...args: unknown[]): void;
|
|
102
|
+
setLevel(level: LogLevel): void;
|
|
33
103
|
}
|
|
34
104
|
export declare const logger: Logger;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*/
|
|
105
|
+
export declare class ReDoSError extends SecurityError {
|
|
106
|
+
constructor(message: string);
|
|
107
|
+
}
|
|
39
108
|
export declare function validateRegexPattern(pattern: string): void;
|
|
40
|
-
/**
|
|
41
|
-
* Create a RegExp object with ReDoS protection
|
|
42
|
-
* @param pattern - The regex pattern
|
|
43
|
-
* @param flags - Regex flags (g, i, m, etc.)
|
|
44
|
-
* @returns Safe RegExp object
|
|
45
|
-
* @throws Error if pattern is unsafe
|
|
46
|
-
*/
|
|
47
109
|
export declare function createSafeRegex(pattern: string, flags?: string): RegExp;
|
|
48
|
-
export declare const DEFAULT_HEADERS:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export declare function fetchWithRetry(url: string, options?: any, retries?: number, backoff?: number): Promise<Response>;
|
|
54
|
-
export declare function withRetry(fn: () => Promise<any>, maxRetries?: number, baseDelay?: number): Promise<any>;
|
|
55
|
-
export declare class FileLock {
|
|
56
|
-
locks: Map<any, any>;
|
|
57
|
-
acquire(filePath: string, timeout?: number): Promise<{
|
|
58
|
-
release: () => void;
|
|
59
|
-
}>;
|
|
110
|
+
export declare const DEFAULT_HEADERS: Readonly<Record<string, string>>;
|
|
111
|
+
export interface FetchOptions extends RequestInit {
|
|
112
|
+
retries?: number;
|
|
113
|
+
backoff?: number;
|
|
114
|
+
validateUrl?: boolean;
|
|
60
115
|
}
|
|
61
|
-
export declare
|
|
62
|
-
export declare function
|
|
63
|
-
|
|
116
|
+
export declare function fetchWithRetry(url: string, options?: FetchOptions): Promise<Response>;
|
|
117
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: {
|
|
118
|
+
maxRetries?: number;
|
|
119
|
+
baseDelay?: number;
|
|
120
|
+
onRetry?: (error: Error, attempt: number) => void;
|
|
121
|
+
shouldRetry?: (error: Error) => boolean;
|
|
122
|
+
}): Promise<T>;
|
|
123
|
+
export interface SanitizeOptions {
|
|
124
|
+
maxLength?: number;
|
|
125
|
+
allowedPattern?: RegExp;
|
|
126
|
+
removeNull?: boolean;
|
|
127
|
+
trim?: boolean;
|
|
128
|
+
}
|
|
129
|
+
export declare function sanitizeInput(input: unknown, options?: SanitizeOptions): string;
|
|
130
|
+
export { CONFIG, SAFE_COMMANDS, parseCommand, isPrivateIPv4, isPrivateIPv6, Logger, };
|