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,109 @@ 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+ export const cacheVsCodeSessionId = ( ) => {
70+ state . vsCodeSessionId = randomUUID ( ) + Date . now ( ) . toString ( )
71+ consola . debug ( `Generated VSCode session ID: ${ state . vsCodeSessionId } ` )
72+ }
73+
74+ interface PayloadMessage {
75+ role ?: string
76+ content ?: string | Array < { type ?: string ; text ?: string } > | null
77+ type ?: string
78+ }
79+
80+ const findLastUserContent = (
81+ messages : Array < PayloadMessage > ,
82+ ) : string | null => {
83+ for ( let i = messages . length - 1 ; i >= 0 ; i -- ) {
84+ const msg = messages [ i ]
85+ if ( msg . role === "user" && msg . content ) {
86+ if ( typeof msg . content === "string" ) {
87+ return msg . content
88+ } else if ( Array . isArray ( msg . content ) ) {
89+ const array = msg . content . filter ( ( n ) => n . type !== "tool_result" )
90+ if ( array . length > 0 ) {
91+ return JSON . stringify ( array )
92+ }
93+ }
94+ }
95+ }
96+ return null
97+ }
98+
99+ export const generateRequestIdFromPayload = ( payload : {
100+ messages : string | Array < PayloadMessage > | undefined
101+ } ) : string => {
102+ const messages = payload . messages
103+ if ( messages ) {
104+ const lastUserContent =
105+ typeof messages === "string" ? messages : findLastUserContent ( messages )
106+
107+ if ( lastUserContent ) {
108+ return getUUID ( lastUserContent )
109+ }
110+ }
111+
112+ return randomUUID ( )
113+ }
114+
115+ export const getRootSessionId = (
116+ anthropicPayload : AnthropicMessagesPayload ,
117+ c : Context ,
118+ ) : string | undefined => {
119+ let sessionId : string | undefined
120+ if ( anthropicPayload . metadata ?. user_id ) {
121+ const sessionMatch = new RegExp ( / _ s e s s i o n _ ( .+ ) $ / ) . exec (
122+ anthropicPayload . metadata . user_id ,
123+ )
124+ sessionId = sessionMatch ? sessionMatch [ 1 ] : undefined
125+ } else {
126+ sessionId = c . req . header ( "x-session-id" )
127+ }
128+ if ( sessionId ) {
129+ return getUUID ( sessionId )
130+ }
131+ return sessionId
132+ }
133+
134+ const getUUID = ( content : string ) : string => {
135+ const hash = createHash ( "sha256" ) . update ( content ) . digest ( "hex" )
136+ const hash32 = hash . slice ( 0 , 32 )
137+ return `${ hash32 . slice ( 0 , 8 ) } -${ hash32 . slice ( 8 , 12 ) } -${ hash32 . slice ( 12 , 16 ) } -${ hash32 . slice ( 16 , 20 ) } -${ hash32 . slice ( 20 ) } `
138+ }
0 commit comments