11import path from "path"
22import { mkdir } from "fs/promises"
33import { Log } from "../util/log"
4- import { Global } from "@ /global"
4+ import { Global } from ".. /global"
55
66export namespace Discovery {
77 const log = Log . create ( { service : "skill-discovery" } )
@@ -20,60 +20,77 @@ export namespace Discovery {
2020
2121 async function get ( url : string , dest : string ) : Promise < boolean > {
2222 if ( await Bun . file ( dest ) . exists ( ) ) return true
23- try {
24- const response = await fetch ( url )
25- if ( ! response . ok ) {
26- log . error ( "failed to download" , { url, status : response . status } )
23+ return fetch ( url )
24+ . then ( async ( response ) => {
25+ if ( ! response . ok ) {
26+ log . error ( "failed to download" , { url, status : response . status } )
27+ return false
28+ }
29+ await Bun . write ( dest , await response . text ( ) )
30+ return true
31+ } )
32+ . catch ( ( err ) => {
33+ log . error ( "failed to download" , { url, err } )
2734 return false
28- }
29- const content = await response . text ( )
30- await Bun . write ( dest , content )
31- return true
32- } catch ( err ) {
33- log . error ( "failed to download" , { url, err } )
34- return false
35- }
35+ } )
3636 }
3737
3838 export async function pull ( url : string ) : Promise < string [ ] > {
3939 const result : string [ ] = [ ]
40- const indexUrl = new URL ( "index.json" , url . endsWith ( "/" ) ? url : `${ url } /` ) . href
41- const cacheDir = dir ( )
42-
43- try {
44- log . info ( "fetching index" , { url : indexUrl } )
45- const response = await fetch ( indexUrl )
46- if ( ! response . ok ) {
47- log . error ( "failed to fetch index" , { url : indexUrl , status : response . status } )
48- return result
49- }
50-
51- const index = ( await response . json ( ) ) as Index
52- if ( ! index . skills || ! Array . isArray ( index . skills ) ) {
53- log . warn ( "invalid index format" , { url : indexUrl } )
54- return result
55- }
40+ const base = url . endsWith ( "/" ) ? url : `${ url } /`
41+ const index = new URL ( "index.json" , base ) . href
42+ const cache = dir ( )
43+ const host = base . slice ( 0 , - 1 )
5644
57- for ( const skill of index . skills ) {
58- if ( ! skill . name || ! skill . files || ! Array . isArray ( skill . files ) ) {
59- log . warn ( "invalid skill entry" , { url : indexUrl , skill } )
60- continue
45+ log . info ( "fetching index" , { url : index } )
46+ const data = await fetch ( index )
47+ . then ( async ( response ) => {
48+ if ( ! response . ok ) {
49+ log . error ( "failed to fetch index" , { url : index , status : response . status } )
50+ return undefined
6151 }
52+ return response
53+ . json ( )
54+ . then ( ( json ) => json as Index )
55+ . catch ( ( err ) => {
56+ log . error ( "failed to parse index" , { url : index , err } )
57+ return undefined
58+ } )
59+ } )
60+ . catch ( ( err ) => {
61+ log . error ( "failed to fetch index" , { url : index , err } )
62+ return undefined
63+ } )
6264
63- const skillDir = path . join ( cacheDir , skill . name )
64- for ( const file of skill . files ) {
65- const fileUrl = new URL ( file , `${ url . replace ( / \/ $ / , "" ) } /${ skill . name } /` ) . href
66- const localPath = path . join ( skillDir , file )
67- await mkdir ( path . dirname ( localPath ) , { recursive : true } )
68- await get ( fileUrl , localPath )
69- }
65+ if ( ! data ?. skills || ! Array . isArray ( data . skills ) ) {
66+ log . warn ( "invalid index format" , { url : index } )
67+ return result
68+ }
7069
71- const skillMd = path . join ( skillDir , "SKILL.md" )
72- if ( await Bun . file ( skillMd ) . exists ( ) ) result . push ( skillDir )
70+ const list = data . skills . filter ( ( skill ) => {
71+ if ( ! skill ?. name || ! Array . isArray ( skill . files ) ) {
72+ log . warn ( "invalid skill entry" , { url : index , skill } )
73+ return false
7374 }
74- } catch ( err ) {
75- log . error ( "failed to fetch from URL" , { url, err } )
76- }
75+ return true
76+ } )
77+
78+ await Promise . all (
79+ list . map ( async ( skill ) => {
80+ const root = path . join ( cache , skill . name )
81+ await Promise . all (
82+ skill . files . map ( async ( file ) => {
83+ const link = new URL ( file , `${ host } /${ skill . name } /` ) . href
84+ const dest = path . join ( root , file )
85+ await mkdir ( path . dirname ( dest ) , { recursive : true } )
86+ await get ( link , dest )
87+ } ) ,
88+ )
89+
90+ const md = path . join ( root , "SKILL.md" )
91+ if ( await Bun . file ( md ) . exists ( ) ) result . push ( root )
92+ } ) ,
93+ )
7794
7895 return result
7996 }
0 commit comments