feat(opencode): cut plugin system and remote behavior feeds; add SessionContext assembly and Runner
- delete plugin machinery (loader, install, meta, hooks) and all trigger sites - fold first-party provider auth into static registry (provider/hooks.ts) - remove remote instruction URL fetch, skills remote puller, TUI plugin host - add session/context.ts: single provenance-tagged context assembly point - add session/runner.ts: admit/context/stream/tools loop with compaction policy - relocate audit artifacts to audit/ (FORK-AUDIT, instruction docs as evidence)
This commit is contained in:
@@ -1,186 +0,0 @@
|
||||
import { describe, expect, beforeAll, afterAll } from "bun:test"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { Effect } from "effect"
|
||||
import { Discovery } from "../../src/skill/discovery"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { rm } from "fs/promises"
|
||||
import path from "path"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
let CLOUDFLARE_SKILLS_URL: string
|
||||
let server: ReturnType<typeof Bun.serve>
|
||||
let downloadCount = 0
|
||||
let mutableVersion = "1"
|
||||
let mutableContent = "# Old"
|
||||
let mutableDownloadCount = 0
|
||||
let mutableFiles = ["SKILL.md"]
|
||||
|
||||
const fixturePath = path.join(import.meta.dir, "../fixture/skills")
|
||||
const cacheDir = path.join(Global.Path.cache, "skills")
|
||||
const it = testEffect(LayerNode.compile(LayerNode.group([Discovery.node, FSUtil.node])))
|
||||
|
||||
beforeAll(async () => {
|
||||
await rm(cacheDir, { recursive: true, force: true })
|
||||
|
||||
server = Bun.serve({
|
||||
port: 0,
|
||||
async fetch(req) {
|
||||
const url = new URL(req.url)
|
||||
|
||||
if (url.pathname === "/mutable/index.json") {
|
||||
return Response.json({ skills: [{ name: "mutable", version: mutableVersion, files: mutableFiles }] })
|
||||
}
|
||||
if (url.pathname === "/mutable/mutable/SKILL.md") {
|
||||
mutableDownloadCount++
|
||||
return new Response(mutableContent)
|
||||
}
|
||||
if (url.pathname === "/mutable/mutable/old.md") return new Response("old reference")
|
||||
|
||||
// route /.well-known/skills/* to the fixture directory
|
||||
if (url.pathname.startsWith("/.well-known/skills/")) {
|
||||
const filePath = url.pathname.replace("/.well-known/skills/", "")
|
||||
const fullPath = path.join(fixturePath, filePath)
|
||||
|
||||
if (await Filesystem.exists(fullPath)) {
|
||||
if (!fullPath.endsWith("index.json")) {
|
||||
downloadCount++
|
||||
}
|
||||
return new Response(Bun.file(fullPath))
|
||||
}
|
||||
}
|
||||
|
||||
return new Response("Not Found", { status: 404 })
|
||||
},
|
||||
})
|
||||
|
||||
CLOUDFLARE_SKILLS_URL = `http://localhost:${server.port}/.well-known/skills/`
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
void server?.stop()
|
||||
await rm(cacheDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
describe("Discovery.pull", () => {
|
||||
it.live("downloads skills from cloudflare url", () =>
|
||||
Effect.gen(function* () {
|
||||
const fsys = yield* FSUtil.Service
|
||||
const discovery = yield* Discovery.Service
|
||||
const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
|
||||
expect(dirs.length).toBeGreaterThan(0)
|
||||
for (const dir of dirs) {
|
||||
expect(dir).toStartWith(cacheDir)
|
||||
const md = path.join(dir, "SKILL.md")
|
||||
expect(yield* fsys.existsSafe(md)).toBe(true)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("url without trailing slash works", () =>
|
||||
Effect.gen(function* () {
|
||||
const fsys = yield* FSUtil.Service
|
||||
const discovery = yield* Discovery.Service
|
||||
const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL.replace(/\/$/, ""))
|
||||
expect(dirs.length).toBeGreaterThan(0)
|
||||
for (const dir of dirs) {
|
||||
const md = path.join(dir, "SKILL.md")
|
||||
expect(yield* fsys.existsSafe(md)).toBe(true)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("returns empty array for invalid url", () =>
|
||||
Effect.gen(function* () {
|
||||
const discovery = yield* Discovery.Service
|
||||
const dirs = yield* discovery.pull(`http://localhost:${server.port}/invalid-url/`)
|
||||
expect(dirs).toEqual([])
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("returns empty array for non-json response", () =>
|
||||
Effect.gen(function* () {
|
||||
// any url not explicitly handled in server returns 404 text "Not Found"
|
||||
const discovery = yield* Discovery.Service
|
||||
const dirs = yield* discovery.pull(`http://localhost:${server.port}/some-other-path/`)
|
||||
expect(dirs).toEqual([])
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("downloads reference files alongside SKILL.md", () =>
|
||||
Effect.gen(function* () {
|
||||
const fsys = yield* FSUtil.Service
|
||||
const discovery = yield* Discovery.Service
|
||||
const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
|
||||
// find a skill dir that should have reference files (e.g. agents-sdk)
|
||||
const agentsSdk = dirs.find((d) => d.endsWith(path.sep + "agents-sdk"))
|
||||
expect(agentsSdk).toBeDefined()
|
||||
if (agentsSdk) {
|
||||
const refs = path.join(agentsSdk, "references")
|
||||
expect(yield* fsys.existsSafe(path.join(agentsSdk, "SKILL.md"))).toBe(true)
|
||||
// agents-sdk has reference files per the index
|
||||
const refDir = yield* Effect.promise(() =>
|
||||
Array.fromAsync(new Bun.Glob("**/*.md").scan({ cwd: refs, onlyFiles: true })),
|
||||
)
|
||||
expect(refDir.length).toBeGreaterThan(0)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("caches downloaded files on second pull", () =>
|
||||
Effect.gen(function* () {
|
||||
// clear dir and downloadCount
|
||||
yield* Effect.promise(() => rm(cacheDir, { recursive: true, force: true }))
|
||||
downloadCount = 0
|
||||
const discovery = yield* Discovery.Service
|
||||
|
||||
// first pull to populate cache
|
||||
const first = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
|
||||
expect(first.length).toBeGreaterThan(0)
|
||||
const firstCount = downloadCount
|
||||
expect(firstCount).toBeGreaterThan(0)
|
||||
|
||||
// second pull should return same results from cache
|
||||
const second = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
|
||||
expect(second.length).toBe(first.length)
|
||||
expect(second.sort()).toEqual(first.sort())
|
||||
|
||||
// second pull should NOT increment download count
|
||||
expect(downloadCount).toBe(firstCount)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("refreshes a remote skill when its version changes", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* Effect.promise(() => rm(cacheDir, { recursive: true, force: true }))
|
||||
mutableVersion = "1"
|
||||
mutableContent = "# Old"
|
||||
mutableDownloadCount = 0
|
||||
mutableFiles = ["SKILL.md", "old.md"]
|
||||
const discovery = yield* Discovery.Service
|
||||
const url = `http://localhost:${server.port}/mutable/`
|
||||
|
||||
const first = yield* discovery.pull(url)
|
||||
expect(yield* Effect.promise(() => Bun.file(path.join(first[0], "SKILL.md")).text())).toBe("# Old")
|
||||
|
||||
mutableVersion = "2"
|
||||
mutableContent = "# Partial"
|
||||
mutableFiles = ["SKILL.md", "missing.md"]
|
||||
const second = yield* discovery.pull(url)
|
||||
expect(yield* Effect.promise(() => Bun.file(path.join(second[0], "SKILL.md")).text())).toBe("# Old")
|
||||
expect(yield* Effect.promise(() => Bun.file(path.join(second[0], "old.md")).text())).toBe("old reference")
|
||||
|
||||
mutableVersion = "3"
|
||||
mutableContent = "# New"
|
||||
mutableFiles = ["SKILL.md"]
|
||||
yield* discovery.pull(url)
|
||||
expect(yield* Effect.promise(() => Bun.file(path.join(second[0], "SKILL.md")).text())).toBe("# New")
|
||||
expect(yield* Effect.promise(() => Bun.file(path.join(second[0], "old.md")).exists())).toBe(false)
|
||||
expect(mutableDownloadCount).toBe(3)
|
||||
|
||||
yield* discovery.pull(url)
|
||||
expect(mutableDownloadCount).toBe(3)
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -2,7 +2,6 @@ import { describe, expect } from "bun:test"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Skill } from "../../src/skill"
|
||||
import { Discovery } from "../../src/skill/discovery"
|
||||
import { RuntimeFlags } from "../../src/effect/runtime-flags"
|
||||
import { EventV2Bridge } from "../../src/event-v2-bridge"
|
||||
import { Config } from "../../src/config/config"
|
||||
|
||||
Reference in New Issue
Block a user