@@ -44,6 +44,7 @@ local utils = require('CopilotChat.utils')
44
44
--- @field get_agents nil | fun ( headers : table ): table<CopilotChat.Provider.agent>
45
45
--- @field get_models nil | fun ( headers : table ): table<CopilotChat.Provider.model>
46
46
--- @field embed nil | string | fun ( inputs : table<string> , headers : table ): table<CopilotChat.Provider.embed>
47
+ --- @field search nil | string | fun ( query : string , repository : string , headers : table ): table<CopilotChat.Provider.output>
47
48
--- @field prepare_input nil | fun ( inputs : table<CopilotChat.Provider.input> , opts : CopilotChat.Provider.options ): table
48
49
--- @field prepare_output nil | fun ( output : table , opts : CopilotChat.Provider.options ): CopilotChat.Provider.output
49
50
--- @field get_url nil | fun ( opts : CopilotChat.Provider.options ): string
@@ -101,11 +102,41 @@ local function get_github_token()
101
102
error (' Failed to find GitHub token' )
102
103
end
103
104
105
+ local cached_gh_apps_token = nil
106
+
107
+ --- Get the github apps token (gho_ token)
108
+ --- @return string
109
+ local function get_gh_apps_token ()
110
+ if cached_gh_apps_token then
111
+ return cached_gh_apps_token
112
+ end
113
+
114
+ async .util .scheduler ()
115
+
116
+ local config_path = utils .config_path ()
117
+ if not config_path then
118
+ error (' Failed to find config path for GitHub token' )
119
+ end
120
+
121
+ local file_path = config_path .. ' /gh/hosts.yml'
122
+ if vim .fn .filereadable (file_path ) == 1 then
123
+ local content = table.concat (vim .fn .readfile (file_path ), ' \n ' )
124
+ local token = content :match (' oauth_token:%s*([%w_]+)' )
125
+ if token then
126
+ cached_gh_apps_token = token
127
+ return token
128
+ end
129
+ end
130
+
131
+ error (' Failed to find GitHub token' )
132
+ end
133
+
104
134
--- @type table<string , CopilotChat.Provider>
105
135
local M = {}
106
136
107
137
M .copilot = {
108
138
embed = ' copilot_embeddings' ,
139
+ search = ' copilot_search' ,
109
140
110
141
get_headers = function (token )
111
142
return {
@@ -279,6 +310,7 @@ M.copilot = {
279
310
280
311
M .github_models = {
281
312
embed = ' copilot_embeddings' ,
313
+ search = ' copilot_search' ,
282
314
283
315
get_headers = function (token )
284
316
return {
@@ -360,4 +392,80 @@ M.copilot_embeddings = {
360
392
end ,
361
393
}
362
394
395
+ M .copilot_search = {
396
+ get_headers = M .copilot .get_headers ,
397
+
398
+ get_token = function ()
399
+ return get_gh_apps_token (), nil
400
+ end ,
401
+
402
+ search = function (query , repository , headers )
403
+ utils .curl_post (
404
+ ' https://api.github.com/repos/' .. repository .. ' /copilot_internal/embeddings_index' ,
405
+ {
406
+ headers = headers ,
407
+ }
408
+ )
409
+
410
+ local response , err = utils .curl_get (
411
+ ' https://api.github.com/repos/' .. repository .. ' /copilot_internal/embeddings_index' ,
412
+ {
413
+ headers = headers ,
414
+ }
415
+ )
416
+
417
+ if err then
418
+ error (err )
419
+ end
420
+
421
+ if response .status ~= 200 then
422
+ error (' Failed to check search: ' .. tostring (response .status ))
423
+ end
424
+
425
+ local body = vim .json .decode (response .body )
426
+
427
+ if
428
+ body .can_index ~= ' ok'
429
+ or not body .bm25_search_ok
430
+ or not body .lexical_search_ok
431
+ or not body .semantic_code_search_ok
432
+ or not body .semantic_doc_search_ok
433
+ or not body .semantic_indexing_enabled
434
+ then
435
+ error (' Failed to search: ' .. vim .inspect (body ))
436
+ end
437
+
438
+ local body = vim .json .encode ({
439
+ query = query ,
440
+ scopingQuery = ' (repo:' .. repository .. ' )' ,
441
+ similarity = 0.766 ,
442
+ limit = 100 ,
443
+ })
444
+
445
+ local response , err = utils .curl_post (' https://api.individual.githubcopilot.com/search/code' , {
446
+ headers = headers ,
447
+ body = utils .temp_file (body ),
448
+ })
449
+
450
+ if err then
451
+ error (err )
452
+ end
453
+
454
+ if response .status ~= 200 then
455
+ error (' Failed to search: ' .. tostring (response .body ))
456
+ end
457
+
458
+ local out = {}
459
+ for _ , result in ipairs (vim .json .decode (response .body )) do
460
+ table.insert (out , {
461
+ filename = result .path ,
462
+ filetype = result .languageName :lower (),
463
+ score = result .score ,
464
+ content = result .contents ,
465
+ })
466
+ end
467
+ return out
468
+ end ,
469
+ }
470
+
363
471
return M
0 commit comments