mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-02 04:34:35 +00:00
docs: improve build documentation discovery
This commit is contained in:
parent
6a38cacc1d
commit
38a10547b4
11 changed files with 368 additions and 123 deletions
|
|
@ -11,6 +11,9 @@ truth. Follow links from that page when the question needs more detail. Fetch
|
|||
<https://opencode.ai/v2/docs/> first when you need to discover the relevant
|
||||
documentation page.
|
||||
|
||||
A machine-readable documentation index is available at
|
||||
<https://opencode.ai/v2/llms.txt>.
|
||||
|
||||
## Version policy
|
||||
|
||||
Always answer for OpenCode V2 unless the user explicitly asks about V1,
|
||||
|
|
@ -152,6 +155,8 @@ before answering. Refer to this guide when the user wants to build a plugin. It
|
|||
covers hooks, transforms, tools, plugin context capabilities, and package
|
||||
entrypoints. Plugins can also extend the TUI; for those, fetch the
|
||||
[CLI plugin guide](https://opencode.ai/v2/docs/build/plugins/cli).
|
||||
For custom methods and events shared with other plugins or clients, fetch the
|
||||
[RPC guide](https://opencode.ai/v2/docs/build/plugins/rpc).
|
||||
|
||||
## [Service](https://opencode.ai/v2/docs/troubleshooting#check-the-background-service)
|
||||
|
||||
|
|
@ -220,6 +225,16 @@ exposes typed Effects, Streams, and decoded OpenCode schema values. Its
|
|||
`Service` API can discover, start, stop, and authenticate with the local
|
||||
background service from a Node application.
|
||||
|
||||
## [SDK](https://opencode.ai/v2/docs/build/sdk)
|
||||
|
||||
For questions about embedding OpenCode directly in an application, fetch the
|
||||
full [SDK guide](https://opencode.ai/v2/docs/build/sdk) before answering. The SDK
|
||||
hosts OpenCode in the application without opening an HTTP listener.
|
||||
|
||||
Use the [Effect SDK guide](https://opencode.ai/v2/docs/build/sdk/effect) for
|
||||
Effect applications. For Cloudflare Durable Objects, use the
|
||||
[Cloudflare SDK guide](https://opencode.ai/v2/docs/build/sdk/cloudflare).
|
||||
|
||||
## [Troubleshooting](https://opencode.ai/v2/docs/troubleshooting)
|
||||
|
||||
OpenCode runs a client and a background server. Start by determining whether a
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
- Keep prose sections brief and focused on one idea. Prefer one to three sentences over large paragraphs.
|
||||
- Interleave explanations with concrete code, configuration, command, or output examples so pages do not become walls of text.
|
||||
- Put the relevant example immediately after the text that introduces it, following `content/build/plugins/cli.mdx` as the reference pattern.
|
||||
- Every subsection that explains syntax, fields, or an API concept must include its own minimal example. A larger example earlier on the page does not count.
|
||||
- Split long explanations with meaningful headings and examples rather than accumulating caveats in one paragraph.
|
||||
- Lead with the common task and working example; place edge cases and supporting details afterward.
|
||||
- Do not stack several prose paragraphs without a visual break. After introducing a concept, use an example, list, table, or task-oriented subheading before covering the next concern.
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ RPC error wrapper; reserved `rpc.*` types identify framework failures.
|
|||
RPC events are typed Streams, not callback-style `on` listeners. They receive the
|
||||
RPC's events from all locations, each with required `location` and a normal
|
||||
prefixed type such as `rpc.acme.updated`. This differs from server-plugin handles,
|
||||
which are fixed to their own location. See [plugin RPC](/build/plugins/rpc) for
|
||||
which are fixed to their own location. See [Effect plugin RPC](/build/plugins/effect/rpc) for
|
||||
definitions, schemas, registration, and live subscription semantics.
|
||||
|
||||
## Local background service
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ OpenCode is used by millions every day. Build on top of it to create your own
|
|||
applications, integrations, and agent experiences without starting from
|
||||
scratch.
|
||||
|
||||
We also offer Effect APIs for [plugins](/build/plugins/effect),
|
||||
[clients](/build/client/effect), and [embedded apps](/build/sdk/effect).
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card title="Extend OpenCode" href="/build/plugins">
|
||||
Build plugins that add tools, integrations, commands, agents, and custom behavior while keeping the rest of OpenCode
|
||||
|
|
|
|||
|
|
@ -631,59 +631,6 @@ interface Context {
|
|||
}
|
||||
```
|
||||
|
||||
### RPC
|
||||
|
||||
Use the same execution-neutral [`Rpc.define` builder](/build/plugins/rpc).
|
||||
Effect clients and plugins accept Effect Schema, Standard Schema, or plain JSON
|
||||
Schema. Promise consumers accept only the portable Standard and JSON formats.
|
||||
|
||||
```ts
|
||||
import { Plugin } from "@opencode-ai/plugin/effect"
|
||||
import { Effect } from "effect"
|
||||
import { Acme } from "./rpc.js"
|
||||
|
||||
export default Plugin.define({
|
||||
id: "acme-effect-plugin",
|
||||
effect: (ctx) =>
|
||||
Effect.gen(function* () {
|
||||
const registration = yield* ctx.rpc.register(Acme, {
|
||||
search: ({ query }, context) =>
|
||||
findText(query).pipe(
|
||||
Effect.flatMap((text) =>
|
||||
text
|
||||
? Effect.succeed({ text })
|
||||
: Effect.fail(context.error("not_found", "Result not found", { query })),
|
||||
),
|
||||
),
|
||||
})
|
||||
yield* registration.events.emit("updated", { itemID: "item-1", text: "ready" })
|
||||
}).pipe(Effect.orDie),
|
||||
})
|
||||
```
|
||||
|
||||
Effect handlers use normal interruption. Registrations belong to the plugin
|
||||
scope; `yield* registration.dispose` removes one explicitly. Later registrations
|
||||
override earlier ones at the same location, without changing in-flight handlers.
|
||||
|
||||
`ctx.rpc(Acme)` returns a local typed subclient. Its methods return Effects and
|
||||
`events.subscribe(name)` returns a Stream. Use scoped fibers when listening
|
||||
during plugin lifetime:
|
||||
|
||||
```ts
|
||||
const acme = ctx.rpc(Acme)
|
||||
yield *
|
||||
acme.events.subscribe("updated").pipe(
|
||||
Stream.runForEach((event) => Effect.logInfo(event.data.text)),
|
||||
Effect.forkScoped,
|
||||
)
|
||||
```
|
||||
|
||||
There is no Effect callback-style `on` API. Subscriptions are location-bound,
|
||||
live-only, and close when Stream consumption stops. Events use the normal
|
||||
ephemeral Bus path. Method `errors` maps become typed Effect error channels. Construct one
|
||||
with `context.error(...)` and fail it with `Effect.fail`; unexpected failures and
|
||||
transport errors remain separate from the declared method errors.
|
||||
|
||||
### References
|
||||
|
||||
Read references available at the current location.
|
||||
|
|
|
|||
169
packages/www/src/docs/content/build/plugins/effect/rpc.mdx
Normal file
169
packages/www/src/docs/content/build/plugins/effect/rpc.mdx
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
---
|
||||
title: "RPC"
|
||||
---
|
||||
|
||||
Effect plugins can expose methods and events that return typed Effects and
|
||||
Streams.
|
||||
|
||||
## Define
|
||||
|
||||
Use `Rpc.define` with Effect Schema to define the RPC.
|
||||
|
||||
```ts title="src/rpc.ts"
|
||||
import { Rpc } from "@opencode-ai/plugin/rpc"
|
||||
import { Schema } from "effect"
|
||||
|
||||
export const Acme = Rpc.define({
|
||||
id: "acme",
|
||||
methods: {
|
||||
search: {
|
||||
input: Schema.Struct({ query: Schema.String }),
|
||||
output: Schema.Struct({ text: Schema.String }),
|
||||
errors: {
|
||||
not_found: Schema.Struct({ query: Schema.String }),
|
||||
},
|
||||
},
|
||||
},
|
||||
events: {
|
||||
updated: {
|
||||
schema: Schema.Struct({ itemID: Schema.String, text: Schema.String }),
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Validation
|
||||
|
||||
Effect Schema validates values and infers their TypeScript types.
|
||||
|
||||
```ts
|
||||
input: Schema.Struct({ query: Schema.String })
|
||||
```
|
||||
|
||||
JSON Schema and Standard Schema are also supported.
|
||||
|
||||
### Input and output
|
||||
|
||||
Use `input` for the method argument and `output` for its return value. Leave
|
||||
either one out when there is no value.
|
||||
|
||||
```ts
|
||||
search: {
|
||||
input: Schema.Struct({ query: Schema.String }),
|
||||
output: Schema.Struct({ text: Schema.String }),
|
||||
}
|
||||
```
|
||||
|
||||
### Errors
|
||||
|
||||
Add expected failures to `errors`. Each key becomes the error's `type`.
|
||||
|
||||
```ts
|
||||
errors: {
|
||||
not_found: Schema.Struct({ query: Schema.String }),
|
||||
}
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
Add events to the top-level `events` map. Event data must be an object.
|
||||
|
||||
```ts
|
||||
events: {
|
||||
updated: {
|
||||
schema: Schema.Struct({ itemID: Schema.String, text: Schema.String }),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Implement
|
||||
|
||||
Register the implementation from the plugin Effect:
|
||||
|
||||
```ts title="src/index.ts"
|
||||
import { Plugin } from "@opencode-ai/plugin/effect"
|
||||
import { Effect } from "effect"
|
||||
import { Acme } from "./rpc.js"
|
||||
|
||||
export default Plugin.define({
|
||||
id: "acme-effect-plugin",
|
||||
effect: (ctx) =>
|
||||
Effect.gen(function* () {
|
||||
const registration = yield* ctx.rpc.register(Acme, {
|
||||
search: ({ query }, context) =>
|
||||
findText(query).pipe(
|
||||
Effect.flatMap((text) =>
|
||||
text
|
||||
? Effect.succeed({ text })
|
||||
: Effect.fail(context.error("not_found", "Result not found", { query })),
|
||||
),
|
||||
),
|
||||
})
|
||||
|
||||
const acme = ctx.rpc(Acme)
|
||||
const result = yield* acme.search({ query: "hello" })
|
||||
|
||||
yield* registration.events.emit("updated", { itemID: "item-1", text: result.text })
|
||||
}).pipe(Effect.orDie),
|
||||
})
|
||||
```
|
||||
|
||||
The error map becomes the error type of each method Effect. Use
|
||||
`context.error(...)` to create a declared error.
|
||||
|
||||
## Call
|
||||
|
||||
Once the RPC is registered, it can be called over HTTP or from another plugin.
|
||||
|
||||
### HTTP
|
||||
|
||||
Create the Effect client, then create the RPC subclient:
|
||||
|
||||
```ts
|
||||
import { OpenCode } from "@opencode-ai/client/effect"
|
||||
import { Effect } from "effect"
|
||||
import { FetchHttpClient } from "effect/unstable/http"
|
||||
import { Acme } from "opencode-acme-plugin/rpc"
|
||||
|
||||
const program = Effect.gen(function* () {
|
||||
const client = yield* OpenCode.make({ baseUrl: "http://localhost:4096" })
|
||||
const acme = client.rpc(Acme)
|
||||
return yield* acme.search({ query: "hello" })
|
||||
})
|
||||
|
||||
const result = await Effect.runPromise(program.pipe(Effect.provide(FetchHttpClient.layer)))
|
||||
```
|
||||
|
||||
### Plugin
|
||||
|
||||
Another Effect plugin can create a local subclient from its context:
|
||||
|
||||
```ts
|
||||
import { Effect } from "effect"
|
||||
|
||||
effect: (ctx) =>
|
||||
Effect.gen(function* () {
|
||||
const acme = ctx.rpc(Acme)
|
||||
const result = yield* acme.search({ query: "hello" })
|
||||
yield* Effect.logInfo(result.text)
|
||||
}).pipe(Effect.orDie)
|
||||
```
|
||||
|
||||
### Subscribe
|
||||
|
||||
RPC events are Streams. Subscribe by event name and run the Stream in a scoped
|
||||
fiber:
|
||||
|
||||
```ts
|
||||
import { Effect, Stream } from "effect"
|
||||
|
||||
const acme = ctx.rpc(Acme)
|
||||
|
||||
yield *
|
||||
acme.events.subscribe("updated").pipe(
|
||||
Stream.runForEach((event) => Effect.logInfo(event.data.text)),
|
||||
Effect.forkScoped,
|
||||
)
|
||||
```
|
||||
|
||||
Subscriptions are live only and close when Stream consumption stops.
|
||||
|
|
@ -2,79 +2,129 @@
|
|||
title: "RPC"
|
||||
---
|
||||
|
||||
Expose typed methods and custom events through a shared RPC definition. Start by
|
||||
putting the contract in a browser-safe module, separate from plugin setup and
|
||||
server code.
|
||||
Plugins can expose custom methods and events that run on the server and can be
|
||||
called by other plugins or clients.
|
||||
|
||||
## Define the contract
|
||||
## Define
|
||||
|
||||
Use `Rpc.define` to declare the RPC ID, methods, errors, and events. The builder
|
||||
is synchronous and independent of Promise or Effect execution.
|
||||
Use `Rpc.define` to list the RPC's methods, errors, and events.
|
||||
|
||||
```ts title="src/rpc.ts"
|
||||
import { Rpc } from "@opencode-ai/plugin/rpc"
|
||||
import { z } from "zod"
|
||||
|
||||
export const Acme = Rpc.define({
|
||||
id: "acme",
|
||||
methods: {
|
||||
search: {
|
||||
input: z.object({ query: z.string() }),
|
||||
output: z.object({ text: z.string() }),
|
||||
input: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
output: {
|
||||
type: "object",
|
||||
properties: { text: { type: "string" } },
|
||||
required: ["text"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
errors: {
|
||||
not_found: z.object({ query: z.string() }),
|
||||
not_found: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
events: {
|
||||
updated: {
|
||||
schema: z.object({ itemID: z.string(), text: z.string() }),
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
itemID: { type: "string" },
|
||||
text: { type: "string" },
|
||||
},
|
||||
required: ["itemID", "text"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Choose schemas
|
||||
### Validation
|
||||
|
||||
Use Standard Schema, such as Zod, when Promise and Effect consumers share the
|
||||
contract. It validates at runtime and infers TypeScript types.
|
||||
An RPC definition describes the shapes of input and output of methods, events
|
||||
and errors. It supports two schema formats:
|
||||
|
||||
Other schema formats have narrower tradeoffs:
|
||||
- JSON Schema, simple and requires no dependencies.
|
||||
- Any Standard Schema compliant validator
|
||||
- Zod
|
||||
- Valibot
|
||||
- ArkType
|
||||
|
||||
- Plain JSON Schema validates at runtime, but its inferred TypeScript value is `unknown`.
|
||||
- Plain JSON Schema uses Draft 2020-12 through Effect's JSON Schema importer and decoder.
|
||||
- Use Standard Schema when you need another JSON Schema dialect or parser.
|
||||
- Effect Schema is supported only by Effect plugins and clients.
|
||||
### Input and output
|
||||
|
||||
Every method declares `input` and `output`. Omit either to represent no value.
|
||||
An empty event payload is an object instead:
|
||||
Each method can define an `input` schema for its argument and an `output` schema
|
||||
for its return value. Leave either one out if the method does not accept or
|
||||
return a value.
|
||||
|
||||
```ts
|
||||
events: {
|
||||
refreshed: { schema: z.object({}) },
|
||||
search: {
|
||||
input: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
output: {
|
||||
type: "object",
|
||||
properties: { text: { type: "string" } },
|
||||
required: ["text"],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Event schemas must produce JSON objects. Scalars, arrays, `null`, and `undefined`
|
||||
are invalid event data. Plain JSON Schema events are checked when emitted even
|
||||
though their payload type remains `unknown`.
|
||||
JSON Schema values are `unknown` in TypeScript, so narrow them before use.
|
||||
Standard Schema infers the input and output types.
|
||||
|
||||
## Declare errors
|
||||
### Errors
|
||||
|
||||
Add an `errors` map to a method for expected failures. Each key becomes the
|
||||
literal error `type`, and its schema validates and transforms the error `data`.
|
||||
error's `type`, and its schema defines the error's `data`.
|
||||
|
||||
```ts
|
||||
errors: {
|
||||
not_found: z.object({ query: z.string() }),
|
||||
not_found: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Names beginning with `rpc.` are reserved for framework failures. Contract schemas
|
||||
own parsing, transformations, and Effect encoding; RPC does not perform another
|
||||
generic JSON validation pass.
|
||||
Error names beginning with `rpc.` are reserved by OpenCode.
|
||||
|
||||
## Register the implementation
|
||||
### Events
|
||||
|
||||
Add events to the top-level `events` map. Each event has a schema for the data
|
||||
sent to subscribers.
|
||||
|
||||
Event data must be an object. Use an empty object schema when there is no data:
|
||||
|
||||
```ts
|
||||
events: {
|
||||
refreshed: {
|
||||
schema: { type: "object", additionalProperties: false },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Scalars, arrays, `null`, and `undefined` are not valid event data.
|
||||
|
||||
## Implement
|
||||
|
||||
Register the implementation inside `setup`:
|
||||
|
||||
|
|
@ -86,42 +136,70 @@ export default Plugin.define({
|
|||
id: "acme-plugin",
|
||||
async setup(ctx) {
|
||||
const registration = await ctx.rpc.register(Acme, {
|
||||
search: async ({ query }, context) => {
|
||||
search: async (input, context) => {
|
||||
const { query } = input as { query: string }
|
||||
const text = await findText(query, { signal: context.signal })
|
||||
if (!text) return context.error("not_found", "Result not found", { query })
|
||||
return { text }
|
||||
},
|
||||
})
|
||||
|
||||
const acme = ctx.rpc(Acme)
|
||||
const result = await acme.search({ query: "hello" })
|
||||
|
||||
await registration.events.emit("updated", { itemID: "item-1", text: "ready" })
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Promise handlers receive `signal` and a typed `error(type, message, data)`
|
||||
constructor in their second argument. Return or throw a constructed error to
|
||||
reject callers with `{ type, message, data? }`.
|
||||
After registering the RPC, the same plugin can call it through `ctx.rpc(Acme)`.
|
||||
|
||||
Registration follows these rules:
|
||||
The second argument includes `signal` for cancellation and `context.error(...)`
|
||||
for declared errors. You can return or throw the error.
|
||||
|
||||
- RPC IDs are independent of plugin IDs, and one plugin can implement several RPCs.
|
||||
- A later registration overrides an earlier registration at the same location.
|
||||
- Disposal or plugin unload removes only that registration and reveals the previous one.
|
||||
- In-flight calls retain the handler with which they started.
|
||||
One plugin can register more than one RPC. Disposing the registration removes it.
|
||||
|
||||
## Call from another plugin
|
||||
## Call
|
||||
|
||||
Other server plugins can obtain a handle without implementing the RPC:
|
||||
Once the RPC is registered, it can be called over HTTP or from another plugin.
|
||||
|
||||
### HTTP
|
||||
|
||||
Create an OpenCode client, then use `client.rpc` to create a subclient for the
|
||||
RPC:
|
||||
|
||||
```ts
|
||||
const acme = ctx.rpc(Acme)
|
||||
import { OpenCode } from "@opencode-ai/client"
|
||||
import { Acme } from "opencode-acme-plugin/rpc"
|
||||
|
||||
const client = OpenCode.make({
|
||||
baseUrl: "http://localhost:4096",
|
||||
})
|
||||
|
||||
const acme = client.rpc(Acme)
|
||||
const result = await acme.search({ query: "hello" })
|
||||
```
|
||||
|
||||
The handle is available immediately, and each call finds the current registration.
|
||||
Calls stay within the server plugin's location and cannot override it.
|
||||
### Plugin
|
||||
|
||||
## Subscribe to events
|
||||
Plugins already have an OpenCode client. For example, a TUI plugin can create
|
||||
the same RPC subclient from `context.client`:
|
||||
|
||||
```ts title="src/tui.ts"
|
||||
import { Plugin } from "@opencode-ai/plugin/tui"
|
||||
import { Acme } from "opencode-acme-plugin/rpc"
|
||||
|
||||
export default Plugin.define({
|
||||
id: "acme-tui",
|
||||
async setup(context) {
|
||||
const acme = context.client.rpc(Acme)
|
||||
const result = await acme.search({ query: "hello" })
|
||||
console.log(result)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Subscribe
|
||||
|
||||
Use `events.on` for a callback and unsubscribe when the listener is no longer
|
||||
needed:
|
||||
|
|
@ -142,20 +220,12 @@ for await (const event of acme.events.subscribe("updated")) {
|
|||
}
|
||||
```
|
||||
|
||||
Event subscriptions have these semantics:
|
||||
Event subscriptions are live only, so disconnected subscribers miss events.
|
||||
|
||||
- Event keys use local names such as `updated` when subscribing.
|
||||
- Delivered types are prefixed, such as `rpc.acme.updated`.
|
||||
- Each event includes `id`, `created`, direct `data`, required `location`, and optional `metadata`.
|
||||
- Events use the normal ephemeral Bus path and are live-only; disconnected subscribers miss events.
|
||||
- Plugin unload closes its subscriptions. There is no plugin event log or replay API yet.
|
||||
- The method name `events` is reserved for the subclient event API.
|
||||
- Subscribe with the local name, such as `updated`.
|
||||
- The received type is prefixed, such as `rpc.acme.updated`.
|
||||
- Each event includes `data` and `location`.
|
||||
- Plugin unload closes its subscriptions.
|
||||
|
||||
## Connect external clients
|
||||
|
||||
External [clients](/build/client#plugin-rpc) use `client.rpc(Acme)` and receive
|
||||
that RPC's events across all locations. The native `/api/event` stream and
|
||||
typed subclients observe the same direct `rpc.<rpcID>.<event>` envelope.
|
||||
|
||||
Importing the contract or constructing a handle does not load its server
|
||||
implementation. Configure the plugin on the server separately.
|
||||
External clients receive events from every location, so check `event.location`
|
||||
when needed. The server plugin still needs to be configured and running.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ title: "Overview"
|
|||
calls through its HTTP router in memory. It opens no HTTP listener and adds no
|
||||
network hop between the client and server.
|
||||
|
||||
For Cloudflare Durable Objects, see the [Cloudflare guide](/build/sdk/cloudflare).
|
||||
|
||||
<Callout type="warning">
|
||||
The V2 SDK is beta. Install the current preview with `bun add @opencode-ai/sdk@dev`; its API may change before a
|
||||
stable release.
|
||||
|
|
|
|||
25
packages/www/src/docs/lib/llms.ts
Normal file
25
packages/www/src/docs/lib/llms.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { docsSections } from "./navigation"
|
||||
|
||||
export function renderLlmsTxt(site: URL) {
|
||||
const base = new URL(import.meta.env.BASE_URL, site)
|
||||
const sections = docsSections.flatMap((section) => [
|
||||
`## ${section.title}`,
|
||||
"",
|
||||
...section.groups.flatMap((group) => [
|
||||
...(group.title && group.title !== section.title ? [`### ${group.title}`, ""] : []),
|
||||
...group.items.map((item) => {
|
||||
const path = item.slug === "index" ? "" : `${item.slug.replace(/\/index$/, "")}/`
|
||||
return `- [${item.title}](${new URL(`docs/${path}`, base)})`
|
||||
}),
|
||||
"",
|
||||
]),
|
||||
])
|
||||
|
||||
return [
|
||||
"# OpenCode V2 Documentation",
|
||||
"",
|
||||
"> Official documentation for using, configuring, and building with OpenCode V2.",
|
||||
"",
|
||||
...sections,
|
||||
].join("\n")
|
||||
}
|
||||
|
|
@ -95,25 +95,29 @@ export const docsSections: DocsSection[] = [
|
|||
items: [
|
||||
{ title: "Overview", slug: "build/plugins" },
|
||||
{ title: "RPC", slug: "build/plugins/rpc" },
|
||||
{ title: "Effect", slug: "build/plugins/effect" },
|
||||
{ title: "CLI", slug: "build/plugins/cli" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Client",
|
||||
items: [
|
||||
{ title: "JavaScript", slug: "build/client" },
|
||||
{ title: "Effect", slug: "build/client/effect" },
|
||||
],
|
||||
items: [{ title: "JavaScript", slug: "build/client" }],
|
||||
},
|
||||
{
|
||||
title: "SDK",
|
||||
items: [
|
||||
{ title: "Overview", slug: "build/sdk" },
|
||||
{ title: "Effect", slug: "build/sdk/effect" },
|
||||
{ title: "Cloudflare", slug: "build/sdk/cloudflare" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Effect",
|
||||
items: [
|
||||
{ title: "Plugins", slug: "build/plugins/effect" },
|
||||
{ title: "RPC", slug: "build/plugins/effect/rpc" },
|
||||
{ title: "Client", slug: "build/client/effect" },
|
||||
{ title: "SDK", slug: "build/sdk/effect" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
9
packages/www/src/pages/llms.txt.ts
Normal file
9
packages/www/src/pages/llms.txt.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import type { APIRoute } from "astro"
|
||||
import { renderLlmsTxt } from "../docs/lib/llms"
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export const GET: APIRoute = ({ site }) =>
|
||||
new Response(renderLlmsTxt(site ?? new URL("https://opencode.ai")), {
|
||||
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue