git-slot-machine 2.3.2 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -1
- package/.claude/release.md +0 -74
- package/CHANGELOG.md +0 -250
- package/dist/animation/slotMachine.d.ts.map +0 -1
- package/dist/animation/slotMachine.js.map +0 -1
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js.map +0 -1
- package/dist/balance.d.ts.map +0 -1
- package/dist/balance.js.map +0 -1
- package/dist/commands/auth.d.ts.map +0 -1
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/balance.d.ts.map +0 -1
- package/dist/commands/balance.js.map +0 -1
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/play.d.ts.map +0 -1
- package/dist/commands/play.js.map +0 -1
- package/dist/commands/spin.d.ts.map +0 -1
- package/dist/commands/spin.js.map +0 -1
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/patterns.d.ts.map +0 -1
- package/dist/patterns.js.map +0 -1
- package/dist/secrets.d.ts.map +0 -1
- package/dist/secrets.js.map +0 -1
- package/dist/templates/post-commit.d.ts.map +0 -1
- package/dist/templates/post-commit.js.map +0 -1
- package/dist/utils/amendDetector.d.ts.map +0 -1
- package/dist/utils/amendDetector.js.map +0 -1
- package/dist/utils/fetch-polyfill.d.ts.map +0 -1
- package/dist/utils/fetch-polyfill.js.map +0 -1
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js.map +0 -1
- package/jest.config.js +0 -12
- package/src/animation/slotMachine.ts +0 -164
- package/src/api.ts +0 -207
- package/src/balance.ts +0 -118
- package/src/commands/auth.ts +0 -92
- package/src/commands/balance.ts +0 -28
- package/src/commands/config.ts +0 -59
- package/src/commands/init.ts +0 -259
- package/src/commands/play.ts +0 -196
- package/src/commands/spin.ts +0 -17
- package/src/commands/sync.ts +0 -49
- package/src/commands/test.ts +0 -19
- package/src/config.ts +0 -189
- package/src/index.ts +0 -136
- package/src/patterns.test.ts +0 -44
- package/src/patterns.ts +0 -313
- package/src/secrets.ts +0 -44
- package/src/templates/post-commit.ts +0 -15
- package/src/utils/amendDetector.ts +0 -74
- package/src/utils/fetch-polyfill.ts +0 -13
- package/src/utils/git.ts +0 -88
- package/test.txt +0 -2
- package/tsconfig.json +0 -21
package/jest.config.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
preset: 'ts-jest',
|
|
3
|
-
testEnvironment: 'node',
|
|
4
|
-
roots: ['<rootDir>/src'],
|
|
5
|
-
testMatch: ['**/*.test.ts'],
|
|
6
|
-
moduleFileExtensions: ['ts', 'js'],
|
|
7
|
-
collectCoverageFrom: [
|
|
8
|
-
'src/**/*.ts',
|
|
9
|
-
'!src/**/*.test.ts',
|
|
10
|
-
'!src/**/*.d.ts'
|
|
11
|
-
]
|
|
12
|
-
};
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { PatternResult } from '../patterns.js';
|
|
3
|
-
|
|
4
|
-
const HEX_CHARS = '0123456789abcdef'.split('');
|
|
5
|
-
const ANIMATION_SPEED = 50; // ms per frame
|
|
6
|
-
const SPIN_DURATION = 2000; // total animation time
|
|
7
|
-
const FRAMES = SPIN_DURATION / ANIMATION_SPEED;
|
|
8
|
-
|
|
9
|
-
export interface SlotConfig {
|
|
10
|
-
finalHash: string;
|
|
11
|
-
small: boolean;
|
|
12
|
-
patternResult?: PatternResult;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function getRandomHexChar(): string {
|
|
16
|
-
return HEX_CHARS[Math.floor(Math.random() * HEX_CHARS.length)];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function clearLine(): void {
|
|
20
|
-
process.stdout.write('\r\x1b[K');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function drawSlotMachine(chars: string[], spinning: boolean, highlightIndices: number[] = [], flash: boolean = false): void {
|
|
24
|
-
// Casino color palette: red borders, white title
|
|
25
|
-
const borderColor = chalk.rgb(220, 20, 60); // Crimson red
|
|
26
|
-
const titleColor = chalk.white; // White
|
|
27
|
-
|
|
28
|
-
const borderWidth = 39;
|
|
29
|
-
const border = '═'.repeat(borderWidth);
|
|
30
|
-
const topBorder = borderColor('╔' + border + '╗');
|
|
31
|
-
const middleBorder = borderColor('╠' + border + '╣');
|
|
32
|
-
const bottomBorder = borderColor('╚' + border + '╝');
|
|
33
|
-
|
|
34
|
-
// Title line - emojis count as 2 visual chars each
|
|
35
|
-
const titleText = 'GIT SLOT MACHINE';
|
|
36
|
-
const titleVisualWidth = 2 + 2 + titleText.length + 2 + 2; // emoji + spaces + text + spaces + emoji
|
|
37
|
-
const titlePadding = Math.floor((borderWidth - titleVisualWidth) / 2);
|
|
38
|
-
const titleRightPad = borderWidth - titleVisualWidth - titlePadding;
|
|
39
|
-
const titleLine = borderColor('║') + ' '.repeat(titlePadding) + chalk.rgb(255, 255, 255)('🎰 ' + titleText + ' 🎰') + ' '.repeat(titleRightPad) + borderColor('║');
|
|
40
|
-
|
|
41
|
-
console.log(topBorder);
|
|
42
|
-
console.log(titleLine);
|
|
43
|
-
console.log(middleBorder);
|
|
44
|
-
|
|
45
|
-
// Display the 7 characters as slot reels
|
|
46
|
-
const display = chars.map((char, i) => {
|
|
47
|
-
if (spinning) {
|
|
48
|
-
// Spinning - white blur
|
|
49
|
-
return chalk.rgb(255, 255, 255).bold(char);
|
|
50
|
-
} else {
|
|
51
|
-
// Check if this character should be highlighted
|
|
52
|
-
const isHighlighted = highlightIndices.includes(i);
|
|
53
|
-
|
|
54
|
-
if (isHighlighted && flash) {
|
|
55
|
-
// Flash state - yellow inverse (yellow background, black text)
|
|
56
|
-
return chalk.bgRgb(255, 255, 0).rgb(0, 0, 0).bold(char);
|
|
57
|
-
} else if (isHighlighted) {
|
|
58
|
-
// Normal highlighted state - yellow inverse (yellow background, black text)
|
|
59
|
-
return chalk.bgRgb(255, 255, 0).rgb(0, 0, 0).bold(char);
|
|
60
|
-
} else {
|
|
61
|
-
// Not highlighted - white text
|
|
62
|
-
return chalk.rgb(255, 255, 255)(char);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}).join(' ');
|
|
66
|
-
|
|
67
|
-
// Calculate padding for character display (7 chars + 6 double-space separators = 19 visual chars)
|
|
68
|
-
const displayVisualWidth = 7 + 12; // 7 chars + 6*2 spaces
|
|
69
|
-
const displayPadding = Math.floor((borderWidth - displayVisualWidth) / 2);
|
|
70
|
-
const displayRightPad = borderWidth - displayVisualWidth - displayPadding;
|
|
71
|
-
const displayLine = borderColor('║') + ' '.repeat(displayPadding) + display + ' '.repeat(displayRightPad) + borderColor('║');
|
|
72
|
-
|
|
73
|
-
console.log(displayLine);
|
|
74
|
-
console.log(bottomBorder);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export async function animateSlotMachine(config: SlotConfig): Promise<void> {
|
|
78
|
-
const { finalHash, patternResult } = config;
|
|
79
|
-
const highlightIndices = patternResult?.highlightIndices || [];
|
|
80
|
-
const isCI = !!process.env.CLAUDECODE || !!process.env.CI;
|
|
81
|
-
|
|
82
|
-
const finalChars = finalHash.split('');
|
|
83
|
-
|
|
84
|
-
if (!isCI) {
|
|
85
|
-
// Clear screen
|
|
86
|
-
console.clear();
|
|
87
|
-
|
|
88
|
-
// Initialize with random characters
|
|
89
|
-
let currentChars = Array(7).fill(0).map(() => getRandomHexChar());
|
|
90
|
-
|
|
91
|
-
// Animate spinning
|
|
92
|
-
for (let frame = 0; frame < FRAMES; frame++) {
|
|
93
|
-
// Gradually slow down and settle on final hash
|
|
94
|
-
const progress = frame / FRAMES;
|
|
95
|
-
|
|
96
|
-
if (progress < 0.9) {
|
|
97
|
-
// Still spinning - show random
|
|
98
|
-
currentChars = currentChars.map(() => getRandomHexChar());
|
|
99
|
-
} else {
|
|
100
|
-
// Start settling - reveal characters one by one
|
|
101
|
-
const revealIndex = Math.floor((progress - 0.9) / 0.1 * 7);
|
|
102
|
-
for (let i = 0; i < revealIndex; i++) {
|
|
103
|
-
currentChars[i] = finalHash[i];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Redraw
|
|
108
|
-
console.clear();
|
|
109
|
-
drawSlotMachine(currentChars, progress < 0.9);
|
|
110
|
-
|
|
111
|
-
// Wait for next frame
|
|
112
|
-
await new Promise(resolve => setTimeout(resolve, ANIMATION_SPEED));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Flash 3 times if there are highlighted characters
|
|
116
|
-
if (highlightIndices.length > 0) {
|
|
117
|
-
for (let flashCount = 0; flashCount < 3; flashCount++) {
|
|
118
|
-
console.clear();
|
|
119
|
-
drawSlotMachine(finalChars, false, highlightIndices, true);
|
|
120
|
-
await new Promise(resolve => setTimeout(resolve, 200));
|
|
121
|
-
|
|
122
|
-
console.clear();
|
|
123
|
-
drawSlotMachine(finalChars, false, highlightIndices, false);
|
|
124
|
-
await new Promise(resolve => setTimeout(resolve, 200));
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Steady state
|
|
129
|
-
console.clear();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
drawSlotMachine(finalChars, false, highlightIndices, false);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export async function animateSmallMode(config: SlotConfig): Promise<void> {
|
|
136
|
-
const { finalHash, patternResult } = config;
|
|
137
|
-
const highlightIndices = patternResult?.highlightIndices || [];
|
|
138
|
-
const isCI = !!process.env.CLAUDECODE || !!process.env.CI;
|
|
139
|
-
|
|
140
|
-
if (!isCI) {
|
|
141
|
-
// Single line, rapid character flicker
|
|
142
|
-
process.stdout.write(chalk.cyan('🎰 '));
|
|
143
|
-
|
|
144
|
-
for (let frame = 0; frame < 20; frame++) {
|
|
145
|
-
const chars = Array(7).fill(0).map(() => getRandomHexChar()).join('');
|
|
146
|
-
clearLine();
|
|
147
|
-
process.stdout.write(chalk.cyan('🎰 ') + chalk.rgb(255, 255, 255)(chars));
|
|
148
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
clearLine();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Final with highlighting - don't add newline, let the caller add result info
|
|
155
|
-
const display = finalHash.split('').map((char, i) => {
|
|
156
|
-
if (highlightIndices.includes(i)) {
|
|
157
|
-
return chalk.bgRgb(255, 255, 0).rgb(0, 0, 0).bold(char);
|
|
158
|
-
} else {
|
|
159
|
-
return chalk.rgb(255, 255, 255)(char);
|
|
160
|
-
}
|
|
161
|
-
}).join('');
|
|
162
|
-
process.stdout.write(chalk.cyan('🎰 ') + display);
|
|
163
|
-
// Don't write newline - let caller continue on same line
|
|
164
|
-
}
|
package/src/api.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { getApiUrl, getApiToken, isSyncEnabled } from './config.js';
|
|
2
|
-
import { getFetch } from './utils/fetch-polyfill.js';
|
|
3
|
-
|
|
4
|
-
// Fallback domains to try in order (if primary fails)
|
|
5
|
-
const FALLBACK_DOMAINS = [
|
|
6
|
-
'https://gitslotmachinecom-main-vilmm1.laravel.cloud',
|
|
7
|
-
];
|
|
8
|
-
|
|
9
|
-
export interface PlayData {
|
|
10
|
-
commit_hash: string;
|
|
11
|
-
commit_full_hash: string;
|
|
12
|
-
pattern_type: string;
|
|
13
|
-
pattern_name: string;
|
|
14
|
-
payout: number;
|
|
15
|
-
wager: number;
|
|
16
|
-
balance_before: number;
|
|
17
|
-
balance_after: number;
|
|
18
|
-
repo_url: string;
|
|
19
|
-
github_username: string;
|
|
20
|
-
repo_owner: string;
|
|
21
|
-
repo_name: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface ApiResponse<T = any> {
|
|
25
|
-
success: boolean;
|
|
26
|
-
data?: T;
|
|
27
|
-
message?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface BalanceResponse {
|
|
31
|
-
balance: number;
|
|
32
|
-
total_commits: number;
|
|
33
|
-
total_winnings: number;
|
|
34
|
-
biggest_win: number;
|
|
35
|
-
biggest_win_pattern?: string;
|
|
36
|
-
biggest_win_hash?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Helper to build headers with authentication
|
|
40
|
-
function getHeaders(): Record<string, string> {
|
|
41
|
-
const headers: Record<string, string> = {
|
|
42
|
-
'Content-Type': 'application/json',
|
|
43
|
-
'Accept': 'application/json',
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const token = getApiToken();
|
|
47
|
-
if (token) {
|
|
48
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return headers;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Check if API sync is enabled and available
|
|
55
|
-
export function isApiAvailable(): boolean {
|
|
56
|
-
return isSyncEnabled() && getApiToken() !== null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface PlayResponse {
|
|
60
|
-
balance: number;
|
|
61
|
-
payout: number;
|
|
62
|
-
pattern_name: string;
|
|
63
|
-
share_url?: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Helper to try API call with fallback domains
|
|
67
|
-
async function fetchWithFallback(endpoint: string, options: RequestInit): Promise<Response | null> {
|
|
68
|
-
const configuredUrl = getApiUrl();
|
|
69
|
-
const fetchFn = await getFetch();
|
|
70
|
-
|
|
71
|
-
// Build list of URLs to try: configured URL first, then fallbacks (excluding duplicates)
|
|
72
|
-
const urlsToTry = [
|
|
73
|
-
configuredUrl,
|
|
74
|
-
...FALLBACK_DOMAINS.filter(domain => !configuredUrl.startsWith(domain))
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
for (const baseUrl of urlsToTry) {
|
|
78
|
-
try {
|
|
79
|
-
const url = `${baseUrl.replace(/\/api$/, '')}/api${endpoint}`;
|
|
80
|
-
const response = await fetchFn(url, options);
|
|
81
|
-
|
|
82
|
-
if (response.ok) {
|
|
83
|
-
return response;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// If not ok, try next domain
|
|
87
|
-
continue;
|
|
88
|
-
} catch (error) {
|
|
89
|
-
// Network error, try next domain
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Send play data to API
|
|
98
|
-
export async function sendPlayToAPI(data: PlayData): Promise<PlayResponse | null> {
|
|
99
|
-
if (!isApiAvailable()) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
const response = await fetchWithFallback('/play', {
|
|
105
|
-
method: 'POST',
|
|
106
|
-
headers: getHeaders(),
|
|
107
|
-
body: JSON.stringify(data),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
if (!response) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const result = await response.json() as ApiResponse<PlayResponse>;
|
|
115
|
-
return result.data || null;
|
|
116
|
-
} catch (error) {
|
|
117
|
-
// Silently fail if offline or API unavailable
|
|
118
|
-
// The user still gets local feedback
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Get balance from API
|
|
124
|
-
export async function getBalance(): Promise<BalanceResponse | null> {
|
|
125
|
-
if (!isApiAvailable()) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
const response = await fetchWithFallback('/balance', {
|
|
131
|
-
method: 'GET',
|
|
132
|
-
headers: getHeaders(),
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
if (!response) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const result = await response.json() as ApiResponse<BalanceResponse>;
|
|
140
|
-
return result.data || null;
|
|
141
|
-
} catch (error) {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Create API token (simplified - just pass github username)
|
|
147
|
-
export async function createToken(githubUsername: string): Promise<string | null> {
|
|
148
|
-
try {
|
|
149
|
-
const response = await fetchWithFallback('/auth/token', {
|
|
150
|
-
method: 'POST',
|
|
151
|
-
headers: {
|
|
152
|
-
'Content-Type': 'application/json',
|
|
153
|
-
'Accept': 'application/json',
|
|
154
|
-
},
|
|
155
|
-
body: JSON.stringify({
|
|
156
|
-
github_username: githubUsername,
|
|
157
|
-
}),
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
if (!response) {
|
|
161
|
-
console.error('Failed to reach API server. Please check your network connection.');
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const result = await response.json() as ApiResponse<{ token: string }>;
|
|
166
|
-
return result.data?.token || null;
|
|
167
|
-
} catch (error) {
|
|
168
|
-
console.error('API request failed:', (error as Error).message);
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Verify token is valid
|
|
174
|
-
export async function verifyToken(token: string): Promise<boolean> {
|
|
175
|
-
try {
|
|
176
|
-
const response = await fetchWithFallback('/auth/user', {
|
|
177
|
-
method: 'GET',
|
|
178
|
-
headers: {
|
|
179
|
-
'Content-Type': 'application/json',
|
|
180
|
-
'Accept': 'application/json',
|
|
181
|
-
'Authorization': `Bearer ${token}`,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
return response !== null && response.ok;
|
|
186
|
-
} catch (error) {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Logout (revoke token)
|
|
192
|
-
export async function logout(): Promise<boolean> {
|
|
193
|
-
if (!getApiToken()) {
|
|
194
|
-
return true;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
const response = await fetchWithFallback('/auth/token', {
|
|
199
|
-
method: 'DELETE',
|
|
200
|
-
headers: getHeaders(),
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return response !== null && response.ok;
|
|
204
|
-
} catch (error) {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
}
|
package/src/balance.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as os from 'os';
|
|
4
|
-
|
|
5
|
-
interface BalanceData {
|
|
6
|
-
repos: Record<string, {
|
|
7
|
-
balance: number;
|
|
8
|
-
totalCommits: number;
|
|
9
|
-
totalWinnings: number;
|
|
10
|
-
biggestWin: number;
|
|
11
|
-
lastCommit: string;
|
|
12
|
-
}>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const BALANCE_FILE = path.join(os.homedir(), '.git-slot-machine-balance.json');
|
|
16
|
-
|
|
17
|
-
function getRepoPath(): string | null {
|
|
18
|
-
try {
|
|
19
|
-
const { execSync } = require('child_process');
|
|
20
|
-
return execSync('git rev-parse --show-toplevel', {
|
|
21
|
-
encoding: 'utf-8',
|
|
22
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
23
|
-
}).trim();
|
|
24
|
-
} catch {
|
|
25
|
-
// Not in a git repo - use global balance instead
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function loadBalance(): BalanceData {
|
|
31
|
-
if (!fs.existsSync(BALANCE_FILE)) {
|
|
32
|
-
return { repos: {} };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const data = fs.readFileSync(BALANCE_FILE, 'utf-8');
|
|
36
|
-
return JSON.parse(data);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function saveBalance(data: BalanceData): void {
|
|
40
|
-
fs.writeFileSync(BALANCE_FILE, JSON.stringify(data, null, 2));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function getBalance(): number {
|
|
44
|
-
const repoPath = getRepoPath() || 'global';
|
|
45
|
-
const data = loadBalance();
|
|
46
|
-
|
|
47
|
-
if (!data.repos[repoPath]) {
|
|
48
|
-
// Initialize new repo
|
|
49
|
-
data.repos[repoPath] = {
|
|
50
|
-
balance: 100,
|
|
51
|
-
totalCommits: 0,
|
|
52
|
-
totalWinnings: 0,
|
|
53
|
-
biggestWin: 0,
|
|
54
|
-
lastCommit: ''
|
|
55
|
-
};
|
|
56
|
-
saveBalance(data);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return data.repos[repoPath].balance;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function updateBalance(hash: string, payout: number): number {
|
|
63
|
-
const repoPath = getRepoPath() || 'global';
|
|
64
|
-
const data = loadBalance();
|
|
65
|
-
|
|
66
|
-
if (!data.repos[repoPath]) {
|
|
67
|
-
data.repos[repoPath] = {
|
|
68
|
-
balance: 100,
|
|
69
|
-
totalCommits: 0,
|
|
70
|
-
totalWinnings: 0,
|
|
71
|
-
biggestWin: 0,
|
|
72
|
-
lastCommit: ''
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const repo = data.repos[repoPath];
|
|
77
|
-
|
|
78
|
-
// Deduct cost
|
|
79
|
-
repo.balance -= 10;
|
|
80
|
-
repo.totalCommits += 1;
|
|
81
|
-
|
|
82
|
-
// Add winnings
|
|
83
|
-
if (payout > 0) {
|
|
84
|
-
repo.balance += payout;
|
|
85
|
-
repo.totalWinnings += payout;
|
|
86
|
-
repo.biggestWin = Math.max(repo.biggestWin, payout);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
repo.lastCommit = hash;
|
|
90
|
-
|
|
91
|
-
saveBalance(data);
|
|
92
|
-
return repo.balance;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function setBalance(newBalance: number): void {
|
|
96
|
-
const repoPath = getRepoPath() || 'global';
|
|
97
|
-
const data = loadBalance();
|
|
98
|
-
|
|
99
|
-
if (!data.repos[repoPath]) {
|
|
100
|
-
data.repos[repoPath] = {
|
|
101
|
-
balance: newBalance,
|
|
102
|
-
totalCommits: 0,
|
|
103
|
-
totalWinnings: 0,
|
|
104
|
-
biggestWin: 0,
|
|
105
|
-
lastCommit: ''
|
|
106
|
-
};
|
|
107
|
-
} else {
|
|
108
|
-
data.repos[repoPath].balance = newBalance;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
saveBalance(data);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function getRepoStats() {
|
|
115
|
-
const repoPath = getRepoPath() || 'global';
|
|
116
|
-
const data = loadBalance();
|
|
117
|
-
return data.repos[repoPath] || null;
|
|
118
|
-
}
|
package/src/commands/auth.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { createToken, logout as apiLogout, verifyToken } from '../api.js';
|
|
3
|
-
import { setApiToken, clearApiToken, getApiToken, getApiUrl, setGitHubUsername } from '../config.js';
|
|
4
|
-
|
|
5
|
-
export async function authLoginCommand(githubUsername: string): Promise<void> {
|
|
6
|
-
try {
|
|
7
|
-
console.log(chalk.dim(`Generating token for ${githubUsername}...`));
|
|
8
|
-
|
|
9
|
-
// Generate token from GitHub username
|
|
10
|
-
const token = await createToken(githubUsername);
|
|
11
|
-
|
|
12
|
-
if (!token) {
|
|
13
|
-
console.error(chalk.red('Failed to generate token. Please check your GitHub username.'));
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Save token and username
|
|
18
|
-
setApiToken(token);
|
|
19
|
-
setGitHubUsername(githubUsername);
|
|
20
|
-
|
|
21
|
-
console.log(chalk.green('Successfully authenticated!'));
|
|
22
|
-
console.log(chalk.dim(`Token saved. API URL: ${getApiUrl()}`));
|
|
23
|
-
console.log(chalk.dim(`GitHub Username: ${githubUsername}`));
|
|
24
|
-
console.log();
|
|
25
|
-
console.log(chalk.yellow('Data sent to server on each commit:'));
|
|
26
|
-
console.log(chalk.dim(' • Commit hash (7 and 40 character versions)'));
|
|
27
|
-
console.log(chalk.dim(' • Repository URL, owner, and name'));
|
|
28
|
-
console.log(chalk.dim(' • GitHub username'));
|
|
29
|
-
console.log(chalk.dim(' • Pattern type, payout, and balance'));
|
|
30
|
-
console.log();
|
|
31
|
-
console.log(chalk.dim('To disable sync: git-slot-machine config set sync-enabled false'));
|
|
32
|
-
} catch (error) {
|
|
33
|
-
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function authLogoutCommand(): Promise<void> {
|
|
39
|
-
try {
|
|
40
|
-
const token = getApiToken();
|
|
41
|
-
|
|
42
|
-
if (!token) {
|
|
43
|
-
console.log(chalk.yellow('Not currently authenticated.'));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Try to revoke token on server
|
|
48
|
-
await apiLogout();
|
|
49
|
-
|
|
50
|
-
// Clear local token
|
|
51
|
-
clearApiToken();
|
|
52
|
-
|
|
53
|
-
console.log(chalk.green('Successfully logged out.'));
|
|
54
|
-
} catch (error) {
|
|
55
|
-
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function authStatusCommand(): Promise<void> {
|
|
61
|
-
try {
|
|
62
|
-
const token = getApiToken();
|
|
63
|
-
const apiUrl = getApiUrl();
|
|
64
|
-
|
|
65
|
-
if (!token) {
|
|
66
|
-
console.log(chalk.yellow('Not authenticated.'));
|
|
67
|
-
console.log(chalk.dim(`API URL: ${apiUrl}`));
|
|
68
|
-
console.log();
|
|
69
|
-
console.log('To authenticate, run:');
|
|
70
|
-
console.log(chalk.cyan(' git-slot-machine auth login <your-github-username>'));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Verify token is still valid
|
|
75
|
-
const isValid = await verifyToken(token);
|
|
76
|
-
|
|
77
|
-
if (isValid) {
|
|
78
|
-
console.log(chalk.green('Authenticated'));
|
|
79
|
-
console.log(chalk.dim(`API URL: ${apiUrl}`));
|
|
80
|
-
console.log(chalk.dim(`Token: ${token.substring(0, 10)}...`));
|
|
81
|
-
} else {
|
|
82
|
-
console.log(chalk.red('Authentication expired or invalid.'));
|
|
83
|
-
console.log(chalk.dim(`API URL: ${apiUrl}`));
|
|
84
|
-
console.log();
|
|
85
|
-
console.log('Please login again:');
|
|
86
|
-
console.log(chalk.cyan(' git-slot-machine auth login <your-github-username>'));
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
}
|
package/src/commands/balance.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { getRepoStats } from '../balance.js';
|
|
3
|
-
|
|
4
|
-
export function balanceCommand(): void {
|
|
5
|
-
try {
|
|
6
|
-
const stats = getRepoStats();
|
|
7
|
-
|
|
8
|
-
if (!stats) {
|
|
9
|
-
console.log(chalk.yellow('No balance data for this repository'));
|
|
10
|
-
console.log(chalk.dim('Run a commit or use "git-slot-machine spin" to start playing'));
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
console.log();
|
|
15
|
-
console.log(chalk.cyan.bold('Repository Stats'));
|
|
16
|
-
console.log(chalk.dim('━'.repeat(40)));
|
|
17
|
-
console.log(chalk.white(`Balance: ${stats.balance >= 0 ? chalk.green(stats.balance) : chalk.red(stats.balance)} points`));
|
|
18
|
-
console.log(chalk.white(`Total Commits: ${stats.totalCommits}`));
|
|
19
|
-
console.log(chalk.white(`Total Winnings: ${chalk.yellow(stats.totalWinnings)} points`));
|
|
20
|
-
console.log(chalk.white(`Biggest Win: ${chalk.yellow(stats.biggestWin)} points`));
|
|
21
|
-
console.log(chalk.white(`Last Commit: ${chalk.dim(stats.lastCommit)}`));
|
|
22
|
-
console.log();
|
|
23
|
-
|
|
24
|
-
} catch (error) {
|
|
25
|
-
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
}
|
package/src/commands/config.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import {
|
|
3
|
-
getApiUrl,
|
|
4
|
-
setApiUrl,
|
|
5
|
-
isSyncEnabled,
|
|
6
|
-
setSyncEnabled,
|
|
7
|
-
getConfig
|
|
8
|
-
} from '../config.js';
|
|
9
|
-
|
|
10
|
-
export async function configGetCommand(key: string): Promise<void> {
|
|
11
|
-
try {
|
|
12
|
-
const config = getConfig();
|
|
13
|
-
|
|
14
|
-
switch (key) {
|
|
15
|
-
case 'api-url':
|
|
16
|
-
console.log(getApiUrl());
|
|
17
|
-
break;
|
|
18
|
-
case 'sync-enabled':
|
|
19
|
-
console.log(isSyncEnabled());
|
|
20
|
-
break;
|
|
21
|
-
case 'all':
|
|
22
|
-
console.log(chalk.bold('Configuration:'));
|
|
23
|
-
console.log(` API URL: ${chalk.cyan(getApiUrl())}`);
|
|
24
|
-
console.log(` Sync Enabled: ${chalk.cyan(isSyncEnabled())}`);
|
|
25
|
-
console.log(` Has Token: ${chalk.cyan(config.apiToken ? 'yes' : 'no')}`);
|
|
26
|
-
break;
|
|
27
|
-
default:
|
|
28
|
-
console.error(chalk.red(`Unknown config key: ${key}`));
|
|
29
|
-
console.log('Available keys: api-url, sync-enabled, all');
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
} catch (error) {
|
|
33
|
-
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function configSetCommand(key: string, value: string): Promise<void> {
|
|
39
|
-
try {
|
|
40
|
-
switch (key) {
|
|
41
|
-
case 'api-url':
|
|
42
|
-
setApiUrl(value);
|
|
43
|
-
console.log(chalk.green(`API URL set to: ${value}`));
|
|
44
|
-
break;
|
|
45
|
-
case 'sync-enabled':
|
|
46
|
-
const enabled = value.toLowerCase() === 'true' || value === '1';
|
|
47
|
-
setSyncEnabled(enabled);
|
|
48
|
-
console.log(chalk.green(`Sync ${enabled ? 'enabled' : 'disabled'}`));
|
|
49
|
-
break;
|
|
50
|
-
default:
|
|
51
|
-
console.error(chalk.red(`Unknown config key: ${key}`));
|
|
52
|
-
console.log('Available keys: api-url, sync-enabled');
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
} catch (error) {
|
|
56
|
-
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
}
|