@@ -43,6 +43,7 @@ local utils = require('CopilotChat.utils')
4343--- @field  get_agents  nil | fun ( headers : table ): table<CopilotChat.Provider.agent> 
4444--- @field  get_models  nil | fun ( headers : table ): table<CopilotChat.Provider.model> 
4545--- @field  embed  nil | string | fun ( inputs : table<string> ,  headers : table ): table<CopilotChat.Provider.embed> 
46+ --- @field  search  nil | string | fun ( query : string ,  repository : string ,  headers : table ): table<CopilotChat.Provider.output> 
4647--- @field  prepare_input  nil | fun ( inputs : table<CopilotChat.Provider.input> ,  opts : CopilotChat.Provider.options ): table 
4748--- @field  prepare_output  nil | fun ( output : table ,  opts : CopilotChat.Provider.options ): CopilotChat.Provider.output 
4849--- @field  get_url  nil | fun ( opts : CopilotChat.Provider.options ): string 
@@ -100,11 +101,41 @@ local function get_github_token()
100101  error (' Failed to find GitHub token'  )
101102end 
102103
104+ local  cached_gh_apps_token  =  nil 
105+ 
106+ ---  Get the github apps token (gho_ token)
107+ --- @return  string 
108+ local  function  get_gh_apps_token ()
109+   if  cached_gh_apps_token  then 
110+     return  cached_gh_apps_token 
111+   end 
112+ 
113+   async .util .scheduler ()
114+ 
115+   local  config_path  =  utils .config_path ()
116+   if  not  config_path  then 
117+     error (' Failed to find config path for GitHub token'  )
118+   end 
119+ 
120+   local  file_path  =  config_path  ..  ' /gh/hosts.yml' 
121+   if  vim .fn .filereadable (file_path ) ==  1  then 
122+     local  content  =  table.concat (vim .fn .readfile (file_path ), ' \n '  )
123+     local  token  =  content :match (' oauth_token:%s*([%w_]+)'  )
124+     if  token  then 
125+       cached_gh_apps_token  =  token 
126+       return  token 
127+     end 
128+   end 
129+ 
130+   error (' Failed to find GitHub token'  )
131+ end 
132+ 
103133--- @type  table<string ,  CopilotChat.Provider> 
104134local  M  =  {}
105135
106136M .copilot  =  {
107137  embed  =  ' copilot_embeddings'  ,
138+   search  =  ' copilot_search'  ,
108139
109140  get_headers  =  function ()
110141    local  response , err  =  utils .curl_get (' https://api.github.com/copilot_internal/v2/token'  , {
@@ -271,6 +302,7 @@ M.copilot = {
271302
272303M .github_models  =  {
273304  embed  =  ' copilot_embeddings'  ,
305+   search  =  ' copilot_search'  ,
274306
275307  get_headers  =  function ()
276308    return  {
@@ -350,4 +382,80 @@ M.copilot_embeddings = {
350382  end ,
351383}
352384
385+ M .copilot_search  =  {
386+   get_headers  =  M .copilot .get_headers ,
387+ 
388+   get_token  =  function ()
389+     return  get_gh_apps_token (), nil 
390+   end ,
391+ 
392+   search  =  function (query , repository , headers )
393+     utils .curl_post (
394+       ' https://api.github.com/repos/'  ..  repository  ..  ' /copilot_internal/embeddings_index'  ,
395+       {
396+         headers  =  headers ,
397+       }
398+     )
399+ 
400+     local  response , err  =  utils .curl_get (
401+       ' https://api.github.com/repos/'  ..  repository  ..  ' /copilot_internal/embeddings_index'  ,
402+       {
403+         headers  =  headers ,
404+       }
405+     )
406+ 
407+     if  err  then 
408+       error (err )
409+     end 
410+ 
411+     if  response .status  ~=  200  then 
412+       error (' Failed to check search: '  ..  tostring (response .status ))
413+     end 
414+ 
415+     local  body  =  vim .json .decode (response .body )
416+ 
417+     if 
418+       body .can_index  ~=  ' ok' 
419+       or  not  body .bm25_search_ok 
420+       or  not  body .lexical_search_ok 
421+       or  not  body .semantic_code_search_ok 
422+       or  not  body .semantic_doc_search_ok 
423+       or  not  body .semantic_indexing_enabled 
424+     then 
425+       error (' Failed to search: '  ..  vim .inspect (body ))
426+     end 
427+ 
428+     local  body  =  vim .json .encode ({
429+       query  =  query ,
430+       scopingQuery  =  ' (repo:'  ..  repository  ..  ' )'  ,
431+       similarity  =  0.766 ,
432+       limit  =  100 ,
433+     })
434+ 
435+     local  response , err  =  utils .curl_post (' https://api.individual.githubcopilot.com/search/code'  , {
436+       headers  =  headers ,
437+       body  =  utils .temp_file (body ),
438+     })
439+ 
440+     if  err  then 
441+       error (err )
442+     end 
443+ 
444+     if  response .status  ~=  200  then 
445+       error (' Failed to search: '  ..  tostring (response .body ))
446+     end 
447+ 
448+     local  out  =  {}
449+     for  _ , result  in  ipairs (vim .json .decode (response .body )) do 
450+       table.insert (out , {
451+         filename  =  result .path ,
452+         filetype  =  result .languageName :lower (),
453+         score  =  result .score ,
454+         content  =  result .contents ,
455+       })
456+     end 
457+     return  out 
458+   end ,
459+ }
460+ 
353461return  M 
0 commit comments