Skip to content

Commit 0e86466

Browse files
authored
refactor: unwrap Discovery namespace to flat exports + self-reexport (#22878)
1 parent 32548bc commit 0e86466

1 file changed

Lines changed: 103 additions & 103 deletions

File tree

packages/opencode/src/skill/discovery.ts

Lines changed: 103 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -6,111 +6,111 @@ import { AppFileSystem } from "@opencode-ai/shared/filesystem"
66
import { Global } from "../global"
77
import { Log } from "../util"
88

9-
export namespace Discovery {
10-
const skillConcurrency = 4
11-
const fileConcurrency = 8
12-
13-
class IndexSkill extends Schema.Class<IndexSkill>("IndexSkill")({
14-
name: Schema.String,
15-
files: Schema.Array(Schema.String),
16-
}) {}
17-
18-
class Index extends Schema.Class<Index>("Index")({
19-
skills: Schema.Array(IndexSkill),
20-
}) {}
21-
22-
export interface Interface {
23-
readonly pull: (url: string) => Effect.Effect<string[]>
24-
}
25-
26-
export class Service extends Context.Service<Service, Interface>()("@opencode/SkillDiscovery") {}
27-
28-
export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Path.Path | HttpClient.HttpClient> =
29-
Layer.effect(
30-
Service,
31-
Effect.gen(function* () {
32-
const log = Log.create({ service: "skill-discovery" })
33-
const fs = yield* AppFileSystem.Service
34-
const path = yield* Path.Path
35-
const http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient))
36-
const cache = path.join(Global.Path.cache, "skills")
37-
38-
const download = Effect.fn("Discovery.download")(function* (url: string, dest: string) {
39-
if (yield* fs.exists(dest).pipe(Effect.orDie)) return true
40-
41-
return yield* HttpClientRequest.get(url).pipe(
42-
http.execute,
43-
Effect.flatMap((res) => res.arrayBuffer),
44-
Effect.flatMap((body) => fs.writeWithDirs(dest, new Uint8Array(body))),
45-
Effect.as(true),
46-
Effect.catch((err) =>
47-
Effect.sync(() => {
48-
log.error("failed to download", { url, err })
49-
return false
50-
}),
51-
),
52-
)
53-
})
9+
const skillConcurrency = 4
10+
const fileConcurrency = 8
11+
12+
class IndexSkill extends Schema.Class<IndexSkill>("IndexSkill")({
13+
name: Schema.String,
14+
files: Schema.Array(Schema.String),
15+
}) {}
16+
17+
class Index extends Schema.Class<Index>("Index")({
18+
skills: Schema.Array(IndexSkill),
19+
}) {}
20+
21+
export interface Interface {
22+
readonly pull: (url: string) => Effect.Effect<string[]>
23+
}
5424

55-
const pull = Effect.fn("Discovery.pull")(function* (url: string) {
56-
const base = url.endsWith("/") ? url : `${url}/`
57-
const index = new URL("index.json", base).href
58-
const host = base.slice(0, -1)
59-
60-
log.info("fetching index", { url: index })
61-
62-
const data = yield* HttpClientRequest.get(index).pipe(
63-
HttpClientRequest.acceptJson,
64-
http.execute,
65-
Effect.flatMap(HttpClientResponse.schemaBodyJson(Index)),
66-
Effect.catch((err) =>
67-
Effect.sync(() => {
68-
log.error("failed to fetch index", { url: index, err })
69-
return null
70-
}),
71-
),
72-
)
73-
74-
if (!data) return []
75-
76-
const list = data.skills.filter((skill) => {
77-
if (!skill.files.includes("SKILL.md")) {
78-
log.warn("skill entry missing SKILL.md", { url: index, skill: skill.name })
25+
export class Service extends Context.Service<Service, Interface>()("@opencode/SkillDiscovery") {}
26+
27+
export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Path.Path | HttpClient.HttpClient> =
28+
Layer.effect(
29+
Service,
30+
Effect.gen(function* () {
31+
const log = Log.create({ service: "skill-discovery" })
32+
const fs = yield* AppFileSystem.Service
33+
const path = yield* Path.Path
34+
const http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient))
35+
const cache = path.join(Global.Path.cache, "skills")
36+
37+
const download = Effect.fn("Discovery.download")(function* (url: string, dest: string) {
38+
if (yield* fs.exists(dest).pipe(Effect.orDie)) return true
39+
40+
return yield* HttpClientRequest.get(url).pipe(
41+
http.execute,
42+
Effect.flatMap((res) => res.arrayBuffer),
43+
Effect.flatMap((body) => fs.writeWithDirs(dest, new Uint8Array(body))),
44+
Effect.as(true),
45+
Effect.catch((err) =>
46+
Effect.sync(() => {
47+
log.error("failed to download", { url, err })
7948
return false
80-
}
81-
return true
82-
})
83-
84-
const dirs = yield* Effect.forEach(
85-
list,
86-
(skill) =>
87-
Effect.gen(function* () {
88-
const root = path.join(cache, skill.name)
89-
90-
yield* Effect.forEach(
91-
skill.files,
92-
(file) => download(new URL(file, `${host}/${skill.name}/`).href, path.join(root, file)),
93-
{
94-
concurrency: fileConcurrency,
95-
},
96-
)
97-
98-
const md = path.join(root, "SKILL.md")
99-
return (yield* fs.exists(md).pipe(Effect.orDie)) ? root : null
100-
}),
101-
{ concurrency: skillConcurrency },
102-
)
103-
104-
return dirs.filter((dir): dir is string => dir !== null)
49+
}),
50+
),
51+
)
52+
})
53+
54+
const pull = Effect.fn("Discovery.pull")(function* (url: string) {
55+
const base = url.endsWith("/") ? url : `${url}/`
56+
const index = new URL("index.json", base).href
57+
const host = base.slice(0, -1)
58+
59+
log.info("fetching index", { url: index })
60+
61+
const data = yield* HttpClientRequest.get(index).pipe(
62+
HttpClientRequest.acceptJson,
63+
http.execute,
64+
Effect.flatMap(HttpClientResponse.schemaBodyJson(Index)),
65+
Effect.catch((err) =>
66+
Effect.sync(() => {
67+
log.error("failed to fetch index", { url: index, err })
68+
return null
69+
}),
70+
),
71+
)
72+
73+
if (!data) return []
74+
75+
const list = data.skills.filter((skill) => {
76+
if (!skill.files.includes("SKILL.md")) {
77+
log.warn("skill entry missing SKILL.md", { url: index, skill: skill.name })
78+
return false
79+
}
80+
return true
10581
})
10682

107-
return Service.of({ pull })
108-
}),
109-
)
110-
111-
export const defaultLayer: Layer.Layer<Service> = layer.pipe(
112-
Layer.provide(FetchHttpClient.layer),
113-
Layer.provide(AppFileSystem.defaultLayer),
114-
Layer.provide(NodePath.layer),
83+
const dirs = yield* Effect.forEach(
84+
list,
85+
(skill) =>
86+
Effect.gen(function* () {
87+
const root = path.join(cache, skill.name)
88+
89+
yield* Effect.forEach(
90+
skill.files,
91+
(file) => download(new URL(file, `${host}/${skill.name}/`).href, path.join(root, file)),
92+
{
93+
concurrency: fileConcurrency,
94+
},
95+
)
96+
97+
const md = path.join(root, "SKILL.md")
98+
return (yield* fs.exists(md).pipe(Effect.orDie)) ? root : null
99+
}),
100+
{ concurrency: skillConcurrency },
101+
)
102+
103+
return dirs.filter((dir): dir is string => dir !== null)
104+
})
105+
106+
return Service.of({ pull })
107+
}),
115108
)
116-
}
109+
110+
export const defaultLayer: Layer.Layer<Service> = layer.pipe(
111+
Layer.provide(FetchHttpClient.layer),
112+
Layer.provide(AppFileSystem.defaultLayer),
113+
Layer.provide(NodePath.layer),
114+
)
115+
116+
export * as Discovery from "./discovery"

0 commit comments

Comments
 (0)