1+ import type { Context } from "hono"
2+
13import consola from "consola"
4+ import { createHash , randomUUID } from "node:crypto"
5+ import { networkInterfaces } from "node:os"
6+
7+ import type { AnthropicMessagesPayload } from "~/routes/messages/anthropic-types"
28
39import { getModels } from "~/services/copilot/get-models"
410import { getVSCodeVersion } from "~/services/get-vscode-version"
@@ -24,3 +30,140 @@ export const cacheVSCodeVersion = async () => {
2430
2531 consola . info ( `Using VSCode version: ${ response } ` )
2632}
33+
34+ const invalidMacAddresses = new Set ( [
35+ "00:00:00:00:00:00" ,
36+ "ff:ff:ff:ff:ff:ff" ,
37+ "ac:de:48:00:11:22" ,
38+ ] )
39+
40+ function validateMacAddress ( candidate : string ) : boolean {
41+ const tempCandidate = candidate . replaceAll ( "-" , ":" ) . toLowerCase ( )
42+ return ! invalidMacAddresses . has ( tempCandidate )
43+ }
44+
45+ export function getMac ( ) : string | null {
46+ const ifaces = networkInterfaces ( )
47+ // eslint-disable-next-line guard-for-in
48+ for ( const name in ifaces ) {
49+ const networkInterface = ifaces [ name ]
50+ if ( networkInterface ) {
51+ for ( const { mac } of networkInterface ) {
52+ if ( validateMacAddress ( mac ) ) {
53+ return mac
54+ }
55+ }
56+ }
57+ }
58+ return null
59+ }
60+
61+ export const cacheMacMachineId = ( ) => {
62+ const macAddress = getMac ( ) ?? randomUUID ( )
63+ state . macMachineId = createHash ( "sha256" )
64+ . update ( macAddress , "utf8" )
65+ . digest ( "hex" )
66+ consola . debug ( `Using machine ID: ${ state . macMachineId } ` )
67+ }
68+
69+ const SESSION_REFRESH_BASE_MS = 60 * 60 * 1000
70+ const SESSION_REFRESH_JITTER_MS = 20 * 60 * 1000
71+
72+ const generateSessionId = ( ) => {
73+ state . vsCodeSessionId = randomUUID ( ) + Date . now ( ) . toString ( )
74+ consola . debug ( `Generated VSCode session ID: ${ state . vsCodeSessionId } ` )
75+ }
76+
77+ const scheduleSessionIdRefresh = async ( ) => {
78+ try {
79+ const randomDelay = Math . floor ( Math . random ( ) * SESSION_REFRESH_JITTER_MS )
80+ const delay = SESSION_REFRESH_BASE_MS + randomDelay
81+ consola . debug (
82+ `Scheduling next VSCode session ID refresh in ${ Math . round (
83+ delay / 1000 ,
84+ ) } seconds`,
85+ )
86+ await sleep ( delay )
87+ generateSessionId ( )
88+ void scheduleSessionIdRefresh ( )
89+ } catch ( error ) {
90+ consola . error ( "Failed to refresh session ID, rescheduling..." , error )
91+ void scheduleSessionIdRefresh ( )
92+ }
93+ }
94+
95+ export const cacheVsCodeSessionId = ( ) => {
96+ generateSessionId ( )
97+ void scheduleSessionIdRefresh ( )
98+ }
99+
100+ interface PayloadMessage {
101+ role ?: string
102+ content ?: string | Array < { type ?: string ; text ?: string } > | null
103+ type ?: string
104+ }
105+
106+ const findLastUserContent = (
107+ messages : Array < PayloadMessage > ,
108+ ) : string | null => {
109+ for ( let i = messages . length - 1 ; i >= 0 ; i -- ) {
110+ const msg = messages [ i ]
111+ if ( msg . role === "user" && msg . content ) {
112+ if ( typeof msg . content === "string" ) {
113+ return msg . content
114+ } else if ( Array . isArray ( msg . content ) ) {
115+ const array = msg . content . filter ( ( n ) => n . type !== "tool_result" )
116+ if ( array . length > 0 ) {
117+ return JSON . stringify ( array )
118+ }
119+ }
120+ }
121+ }
122+ return null
123+ }
124+
125+ export const generateRequestIdFromPayload = (
126+ payload : {
127+ messages : string | Array < PayloadMessage > | undefined
128+ } ,
129+ sessionId ?: string ,
130+ ) : string => {
131+ const messages = payload . messages
132+ if ( messages ) {
133+ const lastUserContent =
134+ typeof messages === "string" ? messages : findLastUserContent ( messages )
135+
136+ if ( lastUserContent ) {
137+ return getUUID (
138+ ( sessionId ?? "" ) + ( state . macMachineId ?? "" ) + lastUserContent ,
139+ )
140+ }
141+ }
142+
143+ return randomUUID ( )
144+ }
145+
146+ export const getRootSessionId = (
147+ anthropicPayload : AnthropicMessagesPayload ,
148+ c : Context ,
149+ ) : string | undefined => {
150+ let sessionId : string | undefined
151+ if ( anthropicPayload . metadata ?. user_id ) {
152+ const sessionMatch = new RegExp ( / _ s e s s i o n _ ( .+ ) $ / ) . exec (
153+ anthropicPayload . metadata . user_id ,
154+ )
155+ sessionId = sessionMatch ? sessionMatch [ 1 ] : undefined
156+ } else {
157+ sessionId = c . req . header ( "x-session-id" )
158+ }
159+ if ( sessionId ) {
160+ return getUUID ( sessionId )
161+ }
162+ return sessionId
163+ }
164+
165+ export const getUUID = ( content : string ) : string => {
166+ const hash = createHash ( "sha256" ) . update ( content ) . digest ( "hex" )
167+ const hash32 = hash . slice ( 0 , 32 )
168+ return `${ hash32 . slice ( 0 , 8 ) } -${ hash32 . slice ( 8 , 12 ) } -${ hash32 . slice ( 12 , 16 ) } -${ hash32 . slice ( 16 , 20 ) } -${ hash32 . slice ( 20 ) } `
169+ }
0 commit comments