11import crypto from 'crypto' ;
22import fs from 'fs' ;
33import path from 'path' ;
4+ import { fileURLToPath } from 'url' ;
45
56interface TokenEntry {
67 token : string ;
78 createdAt : number ;
89 lastUsed : number ;
910}
1011
11- const TOKENS_FILE = path . resolve ( './src/tokens.json' ) ;
12+ const __filename = fileURLToPath ( import . meta. url ) ;
13+ const __dirname = path . dirname ( __filename ) ;
14+ const TOKENS_FILE = path . resolve ( __dirname , '../tokens.json' ) ;
1215const EXPIRY_MS = 10 * 24 * 60 * 60 * 1000 ; // 10 days
1316
1417let tokens : TokenEntry [ ] = [ ] ;
18+ let lastSaveTime = 0 ;
19+ const SAVE_THROTTLE_MS = 60 * 1000 ; // 1 minute
1520
1621function load ( ) : void {
1722 try {
@@ -23,9 +28,13 @@ function load(): void {
2328 }
2429}
2530
26- function save ( ) : void {
31+ function save ( force = false ) : void {
32+ const now = Date . now ( ) ;
33+ if ( ! force && ( now - lastSaveTime ) < SAVE_THROTTLE_MS ) return ;
34+
2735 try {
2836 fs . writeFileSync ( TOKENS_FILE , JSON . stringify ( tokens , null , 2 ) ) ;
37+ lastSaveTime = now ;
2938 } catch ( e ) {
3039 console . error ( 'Failed to persist tokens:' , e ) ;
3140 }
@@ -35,7 +44,7 @@ function purgeExpired(): void {
3544 const now = Date . now ( ) ;
3645 const before = tokens . length ;
3746 tokens = tokens . filter ( t => ( now - t . lastUsed ) < EXPIRY_MS ) ;
38- if ( tokens . length !== before ) save ( ) ;
47+ if ( tokens . length !== before ) save ( true ) ;
3948}
4049
4150/** Constant-time string comparison to prevent timing attacks. */
@@ -61,7 +70,7 @@ export function storeToken(token: string): void {
6170 const now = Date . now ( ) ;
6271 tokens . push ( { token, createdAt : now , lastUsed : now } ) ;
6372 }
64- save ( ) ;
73+ save ( true ) ;
6574}
6675
6776/** Check if a token is already known/stored on the server. */
@@ -75,11 +84,19 @@ export function touchToken(token: string): void {
7584 const entry = tokens . find ( t => timingSafeEqual ( t . token , token ) ) ;
7685 if ( entry ) {
7786 entry . lastUsed = Date . now ( ) ;
78- // Persist periodically — save only if >60s since last save
79- // to avoid excessive disk I/O on every message
87+ save ( ) ; // Throttled internally
8088 }
8189}
8290
91+ /** Returns the most recently used active token, if any. */
92+ export function getActiveToken ( ) : string | null {
93+ purgeExpired ( ) ;
94+ if ( tokens . length === 0 ) return null ;
95+ // Return the one used most recently
96+ const sorted = [ ...tokens ] . sort ( ( a , b ) => b . lastUsed - a . lastUsed ) ;
97+ return sorted [ 0 ] . token ;
98+ }
99+
83100/** Check if any tokens exist yet (first-run detection). */
84101export function hasTokens ( ) : boolean {
85102 purgeExpired ( ) ;
0 commit comments