Buckets:
| import fs from "fs/promises" | |
| import path from "path" | |
| import { describe, expect } from "bun:test" | |
| import { Effect, Layer } from "effect" | |
| import { AgentV2 } from "@opencode-ai/core/agent" | |
| import { FSUtil } from "@opencode-ai/core/fs-util" | |
| import { AbsolutePath } from "@opencode-ai/core/schema" | |
| import { SkillV2 } from "@opencode-ai/core/skill" | |
| import { SkillDiscovery } from "@opencode-ai/core/skill/discovery" | |
| import { tmpdir } from "./fixture/tmpdir" | |
| import { testEffect } from "./lib/effect" | |
| const urls = new Map<string, AbsolutePath[]>() | |
| let pulls = 0 | |
| const discovery = Layer.succeed( | |
| SkillDiscovery.Service, | |
| SkillDiscovery.Service.of({ | |
| pull: (url) => { | |
| pulls++ | |
| return Effect.succeed(urls.get(url) ?? []) | |
| }, | |
| }), | |
| ) | |
| const it = testEffect( | |
| SkillV2.layer.pipe( | |
| Layer.provide(discovery), | |
| Layer.provide(FSUtil.defaultLayer), | |
| Layer.provideMerge(AgentV2.locationLayer), | |
| ), | |
| ) | |
| function write(directory: string, name: string, description: string) { | |
| return fs.writeFile( | |
| path.join(directory, name, "SKILL.md"), | |
| `--- | |
| name: ${name} | |
| description: ${description} | |
| --- | |
| # ${name}`, | |
| ) | |
| } | |
| describe("SkillV2", () => { | |
| it.live("registers sources and resolves later source precedence", () => | |
| Effect.acquireRelease( | |
| Effect.promise(() => tmpdir()), | |
| (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), | |
| ).pipe( | |
| Effect.flatMap((tmp) => | |
| Effect.gen(function* () { | |
| const first = path.join(tmp.path, "first") | |
| const second = path.join(tmp.path, "second") | |
| yield* Effect.promise(async () => { | |
| await fs.mkdir(path.join(first, "review"), { recursive: true }) | |
| await fs.mkdir(path.join(second, "review"), { recursive: true }) | |
| await write(first, "review", "First") | |
| await write(second, "review", "Second") | |
| await fs.writeFile(path.join(first, "foo.md"), "---\nslash: true\n---\n# foo") | |
| }) | |
| const skill = yield* SkillV2.Service | |
| const register = yield* skill.transform() | |
| yield* register((editor) => { | |
| editor.source({ type: "directory", path: AbsolutePath.make(first) }) | |
| editor.source({ type: "directory", path: AbsolutePath.make(first) }) | |
| editor.source({ type: "directory", path: AbsolutePath.make(second) }) | |
| expect(editor.list()).toEqual([ | |
| { type: "directory", path: AbsolutePath.make(first) }, | |
| { type: "directory", path: AbsolutePath.make(second) }, | |
| ]) | |
| }) | |
| expect(yield* skill.sources()).toEqual([ | |
| { type: "directory", path: AbsolutePath.make(first) }, | |
| { type: "directory", path: AbsolutePath.make(second) }, | |
| ]) | |
| expect(yield* skill.list()).toEqual([ | |
| new SkillV2.Info({ | |
| name: "foo", | |
| slash: true, | |
| location: AbsolutePath.make(path.join(first, "foo.md")), | |
| content: "# foo", | |
| }), | |
| { | |
| name: "review", | |
| description: "Second", | |
| location: AbsolutePath.make(path.join(second, "review", "SKILL.md")), | |
| content: "# review", | |
| }, | |
| ]) | |
| }), | |
| ), | |
| ), | |
| ) | |
| it.live("loads URL sources and filters skills for agents", () => | |
| Effect.acquireRelease( | |
| Effect.promise(() => tmpdir()), | |
| (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), | |
| ).pipe( | |
| Effect.flatMap((tmp) => | |
| Effect.gen(function* () { | |
| yield* Effect.promise(async () => { | |
| await fs.mkdir(path.join(tmp.path, "deploy"), { recursive: true }) | |
| await write(tmp.path, "deploy", "Deploy production") | |
| }) | |
| pulls = 0 | |
| urls.set("https://example.test/skills/", [AbsolutePath.make(tmp.path)]) | |
| const agents = yield* AgentV2.Service | |
| yield* agents.update((editor) => | |
| editor.update(AgentV2.ID.make("reviewer"), (agent) => { | |
| agent.permissions.push({ action: "skill", resource: "deploy", effect: "deny" }) | |
| }), | |
| ) | |
| const skill = yield* SkillV2.Service | |
| const register = yield* skill.transform() | |
| yield* register((editor) => editor.source({ type: "url", url: "https://example.test/skills/" })) | |
| expect((yield* skill.list()).map((item) => item.name)).toEqual(["deploy"]) | |
| expect((yield* skill.list()).map((item) => item.name)).toEqual(["deploy"]) | |
| expect(pulls).toBe(1) | |
| expect(SkillV2.available(yield* skill.list(), (yield* agents.get(AgentV2.ID.make("reviewer")))!)).toEqual([]) | |
| }), | |
| ), | |
| ), | |
| ) | |
| }) | |
Xet Storage Details
- Size:
- 4.68 kB
- Xet hash:
- 265c0aefaae7e7a5c39beb07ec712299224b3d72c5af9aa40890bfe84a870274
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.