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,112 @@ 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 = (
100+ payload : {
101+ messages : string | Array < PayloadMessage > | undefined
102+ } ,
103+ sessionId ?: string ,
104+ ) : string => {
105+ const messages = payload . messages
106+ if ( messages ) {
107+ const lastUserContent =
108+ typeof messages === "string" ? messages : findLastUserContent ( messages )
109+
110+ if ( lastUserContent ) {
111+ return getUUID ( ( sessionId ?? "" ) + lastUserContent )
112+ }
113+ }
114+
115+ return randomUUID ( )
116+ }
117+
118+ export const getRootSessionId = (
119+ anthropicPayload : AnthropicMessagesPayload ,
120+ c : Context ,
121+ ) : string | undefined => {
122+ let sessionId : string | undefined
123+ if ( anthropicPayload . metadata ?. user_id ) {
124+ const sessionMatch = new RegExp ( / _ s e s s i o n _ ( .+ ) $ / ) . exec (
125+ anthropicPayload . metadata . user_id ,
126+ )
127+ sessionId = sessionMatch ? sessionMatch [ 1 ] : undefined
128+ } else {
129+ sessionId = c . req . header ( "x-session-id" )
130+ }
131+ if ( sessionId ) {
132+ return getUUID ( sessionId )
133+ }
134+ return sessionId
135+ }
136+
137+ const getUUID = ( content : string ) : string => {
138+ const hash = createHash ( "sha256" ) . update ( content ) . digest ( "hex" )
139+ const hash32 = hash . slice ( 0 , 32 )
140+ return `${ hash32 . slice ( 0 , 8 ) } -${ hash32 . slice ( 8 , 12 ) } -${ hash32 . slice ( 12 , 16 ) } -${ hash32 . slice ( 16 , 20 ) } -${ hash32 . slice ( 20 ) } `
141+ }
0 commit comments