openAgent
gitlabThe open source UNIVERSAL agent.
clone
https://gitlab.com/bitzzdev/openAgent.gitAIagentautomationopen-sourceuniversal-agent
import { LayerNode } from "@opencode-ai/core/effect/layer-node"import { httpClient, path } from "@opencode-ai/core/effect/app-node-platform"import { NodePath } from "@effect/platform-node"import { Effect, Layer, Path, Schema, Context } from "effect"import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"import { withTransientReadRetry } from "@/util/effect-http-client"import { FSUtil } from "@opencode-ai/core/fs-util"import { Global } from "@opencode-ai/core/global"const skillConcurrency = 4const fileConcurrency = 8class IndexSkill extends Schema.Class<IndexSkill>("IndexSkill")({name: Schema.String,files: Schema.Array(Schema.String),version: Schema.optional(Schema.String),}) {}class Index extends Schema.Class<Index>("Index")({skills: Schema.Array(IndexSkill),}) {}export interface Interface {readonly pull: (url: string) => Effect.Effect<string[]>}export class Service extends Context.Service<Service, Interface>()("@opencode/SkillDiscovery") {}const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | HttpClient.HttpClient> = Layer.effect(Service,Effect.gen(function* () {const fs = yield* FSUtil.Serviceconst path = yield* Path.Pathconst http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient))const cache = path.join(Global.Path.cache, "skills")const download = Effect.fn("Discovery.download")(function* (url: string, dest: string) {if (yield* fs.exists(dest).pipe(Effect.orDie)) return truereturn yield* HttpClientRequest.get(url).pipe(http.execute,Effect.flatMap((res) => res.arrayBuffer),Effect.flatMap((body) => fs.writeWithDirs(dest, new Uint8Array(body))),Effect.as(true),Effect.catch((err) => Effect.logError("failed to download", { url: url, error: err }).pipe(Effect.as(false))),)})const pull = Effect.fn("Discovery.pull")(function* (url: string) {const base = url.endsWith("/") ? url : `${url}/`const index = new URL("index.json", base).hrefconst host = base.slice(0, -1)yield* Effect.logInfo("fetching index", { url: index })const data = yield* HttpClientRequest.get(index).pipe(HttpClientRequest.acceptJson,http.execute,Effect.flatMap(HttpClientResponse.schemaBodyJson(Index)),Effect.catch((err) =>Effect.logError("failed to fetch index", { url: index, error: err }).pipe(Effect.as(null)),),)if (!data) return []const missing = data.skills.filter((skill) => !skill.files.includes("SKILL.md"))yield* Effect.forEach(missing,(skill) => Effect.logWarning("skill entry missing SKILL.md", { url: index, skill: skill.name }),{ discard: true },)const list = data.skills.filter((skill) => skill.files.includes("SKILL.md"))const dirs = yield* Effect.forEach(list,(skill) =>Effect.gen(function* () {const root = path.join(cache, skill.name)const versionFile = path.join(root, ".opencode-version")const version = skill.versionconst current =version === undefined? undefined: yield* fs.readFileStringSafe(versionFile).pipe(Effect.catch(() => Effect.succeed(undefined)))if (version === undefined || current === version) {yield* Effect.forEach(skill.files,(file) => download(new URL(file, `${host}/${skill.name}/`).href, path.join(root, file)),{ concurrency: fileConcurrency, discard: true },)} else {const token = crypto.randomUUID()const staging = `${root}.tmp-${token}`const backup = `${root}.old-${token}`yield* Effect.gen(function* () {const downloaded = yield* Effect.forEach(skill.files,(file) => download(new URL(file, `${host}/${skill.name}/`).href, path.join(staging, file)),{ concurrency: fileConcurrency },)if (!downloaded.every(Boolean)) returnif (!(yield* fs.exists(path.join(staging, "SKILL.md")).pipe(Effect.orDie))) returnyield* fs.writeFileString(path.join(staging, ".opencode-version"), version)yield* Effect.uninterruptible(Effect.gen(function* () {const cached = yield* fs.exists(root).pipe(Effect.orDie)if (cached) yield* fs.rename(root, backup)yield* fs.rename(staging, root).pipe(Effect.catch((error) =>Effect.gen(function* () {if (cached) yield* fs.rename(backup, root).pipe(Effect.ignore)return yield* Effect.fail(error)}),),)if (cached) yield* fs.remove(backup, { recursive: true, force: true }).pipe(Effect.ignore)}),)}).pipe(Effect.catch((error) => Effect.logError("failed to refresh skill", { skill: skill.name, error })),Effect.ensuring(fs.remove(staging, { recursive: true, force: true }).pipe(Effect.ignore)),)}return (yield* fs.exists(path.join(root, "SKILL.md")).pipe(Effect.orDie)) ? root : null}),{ concurrency: skillConcurrency },)return dirs.filter((dir): dir is string => dir !== null)})return Service.of({ pull })}),)export const node = LayerNode.make({ service: Service, layer: layer, deps: [FSUtil.node, path, httpClient] })export * as Discovery from "./discovery"