1
- import express from "express" ;
1
+ import { createServer , IncomingMessage } from "node:http" ;
2
+
3
+ import { verifyAndParseRequest } from "@copilot-extensions/preview-sdk" ;
2
4
import OpenAI from "openai" ;
3
- import { verifySignatureMiddleware } from "./validate-signature.js" ;
5
+
4
6
import { describeModel } from "./functions/describe-model.js" ;
5
7
import { executeModel } from "./functions/execute-model.js" ;
6
8
import { listModels } from "./functions/list-models.js" ;
7
9
import { RunnerResponse } from "./functions.js" ;
8
10
import { recommendModel } from "./functions/recommend-model.js" ;
9
11
import { ModelsAPI } from "./models-api.js" ;
10
- const app = express ( ) ;
11
12
12
- app . post ( "/" , verifySignatureMiddleware , express . json ( ) , async ( req , res ) => {
13
+ const server = createServer ( async ( request , response ) => {
14
+ if ( request . method === "GET" ) {
15
+ response . statusCode = 200 ;
16
+ response . end ( `OK` ) ;
17
+ return ;
18
+ }
19
+
20
+ const body = await getBody ( request ) ;
21
+
22
+ let verifyAndParseRequestResult : Awaited < ReturnType < typeof verifyAndParseRequest > > ;
23
+ const apiKey = request . headers [ "x-github-token" ] as string ;
24
+ try {
25
+ const signature = request . headers [ "github-public-key-signature" ] as string ;
26
+ const keyID = request . headers [ "github-public-key-identifier" ] as string ;
27
+ verifyAndParseRequestResult = await verifyAndParseRequest ( body , signature , keyID , {
28
+ token : apiKey ,
29
+ } ) ;
30
+ } catch ( err ) {
31
+ console . error ( err ) ;
32
+ response . statusCode = 401
33
+ response . end ( "Unauthorized" ) ;
34
+ return
35
+ }
36
+
37
+ const { isValidRequest, payload } = verifyAndParseRequestResult
38
+
39
+ if ( ! isValidRequest ) {
40
+ console . log ( "Signature verification failed" ) ;
41
+ response . statusCode = 401
42
+ response . end ( "Unauthorized" ) ;
43
+ }
44
+
45
+ console . log ( "Signature verified" ) ;
46
+
13
47
// Use the GitHub API token sent in the request
14
- const apiKey = req . get ( "X-GitHub-Token" ) ;
15
48
if ( ! apiKey ) {
16
- res . status ( 400 ) . end ( ) ;
49
+ response . statusCode = 400
50
+ response . end ( )
17
51
return ;
18
52
}
19
53
@@ -50,8 +84,8 @@ app.post("/", verifySignatureMiddleware, express.json(), async (req, res) => {
50
84
"<-- END OF LIST OF MODELS -->" ,
51
85
] . join ( "\n" ) ,
52
86
} ,
53
- ...req . body . messages ,
54
- ] . concat ( req . body . messages ) ;
87
+ ...payload . messages ,
88
+ ] . concat ( payload . messages ) ;
55
89
56
90
console . time ( "tool-call" ) ;
57
91
const toolCaller = await capiClient . chat . completions . create ( {
@@ -74,15 +108,16 @@ app.post("/", verifySignatureMiddleware, express.json(), async (req, res) => {
74
108
const stream = await capiClient . chat . completions . create ( {
75
109
stream : true ,
76
110
model : "gpt-4" ,
77
- messages : req . body . messages ,
111
+ // @ts -expect-error - TODO @gr2m - type incompatibility between @openai/api and @copilot-extensions/preview-sdk
112
+ messages : payload . messages ,
78
113
} ) ;
79
114
80
115
for await ( const chunk of stream ) {
81
116
const chunkStr = "data: " + JSON . stringify ( chunk ) + "\n\n" ;
82
- res . write ( chunkStr ) ;
117
+ response . write ( chunkStr ) ;
83
118
}
84
- res . write ( "data: [DONE]\n\n" ) ;
85
- res . end ( ) ;
119
+ response . write ( "data: [DONE]\n\n" ) ;
120
+ response . end ( ) ;
86
121
return ;
87
122
}
88
123
@@ -102,10 +137,12 @@ app.post("/", verifySignatureMiddleware, express.json(), async (req, res) => {
102
137
103
138
console . log ( "\t with args" , args ) ;
104
139
const func = new funcClass ( modelsAPI ) ;
105
- functionCallRes = await func . execute ( req . body . messages , args ) ;
140
+ // @ts -expect-error - TODO @gr2m - type incompatibility between @openai/api and @copilot-extensions/preview-sdk
141
+ functionCallRes = await func . execute ( payload . messages , args ) ;
106
142
} catch ( err ) {
107
143
console . error ( err ) ;
108
- res . status ( 500 ) . end ( ) ;
144
+ response . statusCode = 500
145
+ response . end ( ) ;
109
146
return ;
110
147
}
111
148
console . timeEnd ( "function-exec" ) ;
@@ -123,23 +160,33 @@ app.post("/", verifySignatureMiddleware, express.json(), async (req, res) => {
123
160
console . time ( "streaming" ) ;
124
161
for await ( const chunk of stream ) {
125
162
const chunkStr = "data: " + JSON . stringify ( chunk ) + "\n\n" ;
126
- res . write ( chunkStr ) ;
163
+ response . write ( chunkStr ) ;
127
164
}
128
- res . write ( "data: [DONE]\n\n" ) ;
165
+ response . write ( "data: [DONE]\n\n" ) ;
129
166
console . timeEnd ( "streaming" ) ;
130
- res . end ( ) ;
167
+ response . end ( ) ;
131
168
} catch ( err ) {
132
169
console . error ( err ) ;
133
- res . status ( 500 ) . end ( ) ;
170
+ response . statusCode = 500
171
+ response . end ( )
134
172
}
135
173
} ) ;
136
174
137
- // Health check
138
- app . get ( "/" , ( req , res ) => {
139
- res . send ( "OK" ) ;
140
- } ) ;
175
+ const port = process . env . PORT || "3000"
176
+ server . listen ( port ) ;
177
+ console . log ( `Server running at http://localhost:${ port } ` ) ;
141
178
142
- const port = Number ( process . env . PORT || "3000" ) ;
143
- app . listen ( port , ( ) => {
144
- console . log ( `Server is running on http://localhost:${ port } ` ) ;
145
- } ) ;
179
+ function getBody ( request : IncomingMessage ) : Promise < string > {
180
+ return new Promise ( ( resolve ) => {
181
+ const bodyParts : any [ ] = [ ] ;
182
+ let body ;
183
+ request
184
+ . on ( "data" , ( chunk ) => {
185
+ bodyParts . push ( chunk ) ;
186
+ } )
187
+ . on ( "end" , ( ) => {
188
+ body = Buffer . concat ( bodyParts ) . toString ( ) ;
189
+ resolve ( body ) ;
190
+ } ) ;
191
+ } ) ;
192
+ }
0 commit comments