mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-12 03:46:22 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f26ad8787 | ||
|
|
b08ad82518 | ||
|
|
5319f063d6 | ||
|
|
63f7ceecbe | ||
|
|
770420bdad | ||
|
|
4994a7d694 | ||
|
|
c55ee2a815 |
@@ -191,7 +191,7 @@ jobs:
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: opencode-preview-cli
|
||||
name: opencode-preview-cli-macos
|
||||
path: packages/cli/dist/cli-*
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -199,7 +199,7 @@ jobs:
|
||||
needs: version
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
if: github.repository == 'anomalyco/opencode'
|
||||
if: github.repository == 'anomalyco/opencode' && !(github.ref_name == 'v2' && (inputs.bump || inputs.version))
|
||||
steps:
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
needs:
|
||||
- version
|
||||
- build-node-app-archive
|
||||
if: github.repository == 'anomalyco/opencode'
|
||||
if: github.repository == 'anomalyco/opencode' && !(github.ref_name == 'v2' && (inputs.bump || inputs.version))
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -276,10 +276,9 @@ jobs:
|
||||
|
||||
sign-cli-windows:
|
||||
needs:
|
||||
- build-cli
|
||||
- version
|
||||
- sign-cli-macos
|
||||
runs-on: blacksmith-4vcpu-windows-2025
|
||||
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2' && github.ref_name != 'beta'
|
||||
if: github.repository == 'anomalyco/opencode' && (github.ref_name == 'v2' || github.ref_name == 'beta')
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||
@@ -292,15 +291,8 @@ jobs:
|
||||
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: opencode-cli-windows
|
||||
path: packages/opencode/dist
|
||||
|
||||
- name: Setup git committer
|
||||
id: committer
|
||||
uses: ./.github/actions/setup-git-committer
|
||||
with:
|
||||
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
|
||||
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
|
||||
name: opencode-preview-cli-macos
|
||||
path: packages/cli/dist
|
||||
|
||||
- name: Azure login
|
||||
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
|
||||
@@ -315,9 +307,9 @@ jobs:
|
||||
signing-account-name: ${{ env.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
|
||||
certificate-profile-name: ${{ env.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }}
|
||||
files: |
|
||||
${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\cli\dist\cli-windows-arm64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\cli\dist\cli-windows-x64\bin\opencode.exe
|
||||
${{ github.workspace }}\packages\cli\dist\cli-windows-x64-baseline\bin\opencode.exe
|
||||
exclude-environment-credential: true
|
||||
exclude-workload-identity-credential: true
|
||||
exclude-managed-identity-credential: true
|
||||
@@ -333,9 +325,9 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
$files = @(
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline\bin\opencode.exe"
|
||||
"${{ github.workspace }}\packages\cli\dist\cli-windows-arm64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\cli\dist\cli-windows-x64\bin\opencode.exe",
|
||||
"${{ github.workspace }}\packages\cli\dist\cli-windows-x64-baseline\bin\opencode.exe"
|
||||
)
|
||||
|
||||
foreach ($file in $files) {
|
||||
@@ -345,39 +337,16 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
- name: Repack Windows CLI archives
|
||||
working-directory: packages/opencode/dist
|
||||
shell: pwsh
|
||||
run: |
|
||||
Compress-Archive -Path "opencode-windows-arm64\bin\*" -DestinationPath "opencode-windows-arm64.zip" -Force
|
||||
Compress-Archive -Path "opencode-windows-x64\bin\*" -DestinationPath "opencode-windows-x64.zip" -Force
|
||||
Compress-Archive -Path "opencode-windows-x64-baseline\bin\*" -DestinationPath "opencode-windows-x64-baseline.zip" -Force
|
||||
|
||||
- name: Upload signed Windows CLI release assets
|
||||
if: needs.version.outputs.release != ''
|
||||
shell: pwsh
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.committer.outputs.token }}
|
||||
run: |
|
||||
gh release upload "v${{ needs.version.outputs.version }}" `
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64.zip" `
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64.zip" `
|
||||
"${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline.zip" `
|
||||
--clobber `
|
||||
--repo "${{ needs.version.outputs.repo }}"
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: opencode-cli-signed-windows
|
||||
path: |
|
||||
packages/opencode/dist/opencode-windows-arm64
|
||||
packages/opencode/dist/opencode-windows-x64
|
||||
packages/opencode/dist/opencode-windows-x64-baseline
|
||||
name: opencode-preview-cli
|
||||
path: packages/cli/dist/cli-*
|
||||
if-no-files-found: error
|
||||
|
||||
build-electron:
|
||||
needs:
|
||||
- version
|
||||
- sign-cli-macos
|
||||
- sign-cli-windows
|
||||
if: github.repository == 'anomalyco/opencode' && (github.ref_name != 'v2' || needs.version.outputs.release != '')
|
||||
continue-on-error: false
|
||||
env:
|
||||
|
||||
@@ -366,15 +366,14 @@ install_legacy_shim() {
|
||||
if [[ "$shim_os" == "windows" ]]; then
|
||||
cat > "$INSTALL_DIR/opencode2.cmd" <<'EOF'
|
||||
@echo off
|
||||
echo opencode2 is now just opencode. run opencode
|
||||
exit /b 1
|
||||
"%~dp0opencode.exe" %*
|
||||
exit /b %errorlevel%
|
||||
EOF
|
||||
return
|
||||
fi
|
||||
cat > "$INSTALL_DIR/opencode2" <<'EOF'
|
||||
#!/bin/sh
|
||||
echo 'opencode2 is now just opencode. run opencode'
|
||||
exit 1
|
||||
exec "$(dirname "$0")/opencode" "$@"
|
||||
EOF
|
||||
chmod 755 "$INSTALL_DIR/opencode2"
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ ENV BUN_RUNTIME_TRANSPILER_CACHE_PATH=${BUN_RUNTIME_TRANSPILER_CACHE_PATH}
|
||||
RUN apk add --no-cache libgcc libstdc++ ripgrep
|
||||
|
||||
FROM base AS build-amd64
|
||||
COPY dist/opencode-linux-x64-baseline-musl/bin/opencode /usr/local/bin/opencode
|
||||
COPY dist/cli-linux-x64-baseline-musl/bin/opencode /usr/local/bin/opencode
|
||||
|
||||
FROM base AS build-arm64
|
||||
COPY dist/opencode-linux-arm64-musl/bin/opencode /usr/local/bin/opencode
|
||||
COPY dist/cli-linux-arm64-musl/bin/opencode /usr/local/bin/opencode
|
||||
|
||||
ARG TARGETARCH
|
||||
FROM build-${TARGETARCH}
|
||||
RUN opencode --version
|
||||
RUN ln -s opencode /usr/local/bin/opencode2 && opencode --version && opencode2 --version
|
||||
ENTRYPOINT ["opencode"]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
console.log("opencode2 is now just opencode. run opencode")
|
||||
process.exit(1)
|
||||
require("./opencode.cjs")
|
||||
|
||||
@@ -17,7 +17,9 @@ const outdir = path.join(root, `aur-${name}`)
|
||||
const dryRun = process.argv.includes("--dry-run")
|
||||
const pkgver = Script.version.replaceAll("-", ".")
|
||||
const license = Bun.file(path.join(dir, "..", "..", "LICENSE"))
|
||||
const shim = "#!/bin/sh\necho 'opencode2 is now just opencode. run opencode'\nexit 1\n"
|
||||
const shim = `#!/bin/sh
|
||||
exec "$(dirname "$0")/opencode" "$@"
|
||||
`
|
||||
|
||||
await rm(outdir, { recursive: true, force: true })
|
||||
await mkdir(path.dirname(outdir), { recursive: true })
|
||||
|
||||
@@ -60,17 +60,13 @@ async function publishDistribution(input: {
|
||||
"",
|
||||
].join("\n"),
|
||||
)
|
||||
if (input.legacyCommand)
|
||||
await Bun.file(`${input.root}/${input.name}/bin/${input.legacyCommand}.cjs`).write(
|
||||
`#!/usr/bin/env node\n\nconsole.log(${JSON.stringify(`opencode2 is now just opencode. run ${input.command}`)})\nprocess.exit(1)\n`,
|
||||
)
|
||||
await Bun.file(`${input.root}/${input.name}/package.json`).write(
|
||||
JSON.stringify(
|
||||
{
|
||||
name: input.name,
|
||||
bin: {
|
||||
[input.command]: `./bin/${input.command}.exe`,
|
||||
...(input.legacyCommand ? { [input.legacyCommand]: `./bin/${input.legacyCommand}.cjs` } : {}),
|
||||
...(input.legacyCommand ? { [input.legacyCommand]: `./bin/${input.command}.exe` } : {}),
|
||||
},
|
||||
...(input.command !== input.binary ? { opencodeSourceBinary: input.binary } : {}),
|
||||
scripts: { postinstall: "node ./postinstall.mjs" },
|
||||
@@ -136,7 +132,7 @@ await publishDistribution({
|
||||
packagePrefix: "@opencode/cli-",
|
||||
artifact: "cli",
|
||||
})
|
||||
if (existsSync(path.join(root, "node"))) {
|
||||
if (Script.channel !== "latest" && existsSync(path.join(root, "node"))) {
|
||||
await publishDistribution({
|
||||
root: path.join(root, "node"),
|
||||
name: "@opencode/cli-node",
|
||||
|
||||
@@ -2,12 +2,10 @@ import { WebSearchExa } from "./exa.js"
|
||||
import { WebSearchFirecrawl } from "./firecrawl.js"
|
||||
import { WebSearchParallel } from "./parallel.js"
|
||||
import { WebSearchTavily } from "./tavily.js"
|
||||
import { WebSearchTinyFish } from "./tinyfish.js"
|
||||
|
||||
export const WebSearchPlugins = [
|
||||
WebSearchExa.Plugin,
|
||||
WebSearchFirecrawl.Plugin,
|
||||
WebSearchParallel.Plugin,
|
||||
WebSearchTavily.Plugin,
|
||||
WebSearchTinyFish.Plugin,
|
||||
] as const
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
export * as WebSearchTinyFish from "./tinyfish.js"
|
||||
|
||||
import { define } from "@opencode/plugin/effect/plugin"
|
||||
import { Effect, Option, Schema, Scope } from "effect"
|
||||
import { HttpClient } from "effect/unstable/http"
|
||||
import { App } from "../../app.js"
|
||||
import { WebSearchMcp } from "./mcp.js"
|
||||
|
||||
export const endpoint = "https://agent.tinyfish.ai/mcp"
|
||||
|
||||
const McpInput = Schema.Struct({
|
||||
query: Schema.String,
|
||||
})
|
||||
|
||||
const McpOutput = Schema.Struct({
|
||||
content: Schema.Array(Schema.Struct({ type: Schema.Literal("text"), text: Schema.String })),
|
||||
})
|
||||
|
||||
const SearchResponse = Schema.fromJsonString(
|
||||
Schema.Struct({
|
||||
results: Schema.Array(
|
||||
Schema.Struct({
|
||||
url: Schema.String,
|
||||
title: Schema.String,
|
||||
snippet: Schema.String,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
)
|
||||
const decodeSearchResponse = Schema.decodeUnknownOption(SearchResponse)
|
||||
|
||||
export const Plugin = define<HttpClient.HttpClient | Scope.Scope>({
|
||||
id: "opencode.websearch.tinyfish",
|
||||
effect: Effect.fn("WebSearchTinyFish.Plugin")(function* (ctx) {
|
||||
const http = yield* HttpClient.HttpClient
|
||||
yield* ctx.integration.transform((editor) => {
|
||||
editor.update("tinyfish", (integration) => (integration.name = "TinyFish"))
|
||||
editor.method.update({
|
||||
integrationID: "tinyfish",
|
||||
method: { type: "key" },
|
||||
})
|
||||
editor.method.update({
|
||||
integrationID: "tinyfish",
|
||||
method: { type: "env", names: ["TINYFISH_API_KEY"] },
|
||||
})
|
||||
})
|
||||
yield* ctx.websearch.transform((editor) => {
|
||||
editor.add({
|
||||
id: "tinyfish",
|
||||
name: "TinyFish",
|
||||
execute: (input) =>
|
||||
Effect.gen(function* () {
|
||||
const connection = yield* ctx.integration.connection.active("tinyfish")
|
||||
const credential = connection ? yield* ctx.integration.connection.resolve(connection) : undefined
|
||||
const result = yield* WebSearchMcp.call(
|
||||
http,
|
||||
endpoint,
|
||||
"search",
|
||||
{ input: McpInput, output: McpOutput },
|
||||
{ query: input.query },
|
||||
{
|
||||
"User-Agent": App.useragent(ctx.app),
|
||||
...(credential?.type === "key"
|
||||
? { "X-API-Key": credential.key }
|
||||
: { "X-TinyFish-Access-Mode": "keyless" }),
|
||||
},
|
||||
)
|
||||
const content = result?.content.find((item) => item.text)
|
||||
const response = content ? Option.getOrUndefined(decodeSearchResponse(content.text)) : undefined
|
||||
return (
|
||||
response?.results.map((item) => ({
|
||||
url: item.url,
|
||||
title: item.title,
|
||||
...(item.snippet ? { content: item.snippet } : {}),
|
||||
time: {},
|
||||
})) ?? []
|
||||
)
|
||||
}),
|
||||
})
|
||||
})
|
||||
}),
|
||||
})
|
||||
@@ -6,7 +6,6 @@ import { WebSearchExa } from "@opencode/core/plugin/websearch/exa"
|
||||
import { WebSearchFirecrawl } from "@opencode/core/plugin/websearch/firecrawl"
|
||||
import { WebSearchParallel } from "@opencode/core/plugin/websearch/parallel"
|
||||
import { WebSearchTavily } from "@opencode/core/plugin/websearch/tavily"
|
||||
import { WebSearchTinyFish } from "@opencode/core/plugin/websearch/tinyfish"
|
||||
import { host, integrationHost, webSearchHost } from "./host"
|
||||
import { requests, signals, resetWebSearchFixture, webSearchIntegrationTest } from "./websearch-fixture"
|
||||
|
||||
@@ -31,26 +30,24 @@ beforeEach(() => {
|
||||
const it = webSearchIntegrationTest
|
||||
|
||||
describe("built-in web search providers", () => {
|
||||
;[
|
||||
WebSearchExa.Plugin,
|
||||
WebSearchParallel.Plugin,
|
||||
WebSearchFirecrawl.Plugin,
|
||||
WebSearchTavily.Plugin,
|
||||
WebSearchTinyFish.Plugin,
|
||||
].forEach((plugin) => {
|
||||
it.effect(`releases rate-limited HTTP requests for ${plugin.id} before caching their errors`, () =>
|
||||
Effect.gen(function* () {
|
||||
resetWebSearchFixture("Rate limited", 429)
|
||||
const integrations = yield* Integration.Service
|
||||
const websearch = yield* WebSearch.Service
|
||||
yield* plugin.effect(host({ integration: integrationHost(integrations), websearch: webSearchHost(websearch) }))
|
||||
yield* websearch.select("random")
|
||||
expect(yield* websearch.query({ query: "limited" }).pipe(Effect.flip)).toBeInstanceOf(WebSearch.RequestError)
|
||||
expect(signals).toHaveLength(1)
|
||||
expect(signals[0]?.aborted).toBe(true)
|
||||
}),
|
||||
)
|
||||
})
|
||||
;[WebSearchExa.Plugin, WebSearchParallel.Plugin, WebSearchFirecrawl.Plugin, WebSearchTavily.Plugin].forEach(
|
||||
(plugin) => {
|
||||
it.effect(`releases rate-limited HTTP requests for ${plugin.id} before caching their errors`, () =>
|
||||
Effect.gen(function* () {
|
||||
resetWebSearchFixture("Rate limited", 429)
|
||||
const integrations = yield* Integration.Service
|
||||
const websearch = yield* WebSearch.Service
|
||||
yield* plugin.effect(
|
||||
host({ integration: integrationHost(integrations), websearch: webSearchHost(websearch) }),
|
||||
)
|
||||
yield* websearch.select("random")
|
||||
expect(yield* websearch.query({ query: "limited" }).pipe(Effect.flip)).toBeInstanceOf(WebSearch.RequestError)
|
||||
expect(signals).toHaveLength(1)
|
||||
expect(signals[0]?.aborted).toBe(true)
|
||||
}),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
it.effect("registers a provider without an integration", () =>
|
||||
Effect.gen(function* () {
|
||||
@@ -280,84 +277,4 @@ describe("built-in web search providers", () => {
|
||||
expect(requests[1]?.headers["x-tavily-access-mode"]).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("registers TinyFish with keyless and keyed MCP search access", () =>
|
||||
Effect.gen(function* () {
|
||||
resetWebSearchFixture(
|
||||
JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
result: {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
query: "effect typescript",
|
||||
results: [
|
||||
{
|
||||
position: 1,
|
||||
site_name: "effect.website",
|
||||
snippet: "Effect documentation",
|
||||
title: "Effect",
|
||||
url: "https://effect.website",
|
||||
},
|
||||
],
|
||||
total_results: 1,
|
||||
page: 0,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
const integrations = yield* Integration.Service
|
||||
const websearch = yield* WebSearch.Service
|
||||
yield* WebSearchTinyFish.Plugin.effect(
|
||||
host({ integration: integrationHost(integrations), websearch: webSearchHost(websearch) }),
|
||||
)
|
||||
|
||||
expect(yield* integrations.get(Integration.ID.make("tinyfish"))).toMatchObject({
|
||||
id: "tinyfish",
|
||||
name: "TinyFish",
|
||||
methods: [{ type: "key" }, { type: "env", names: ["TINYFISH_API_KEY"] }],
|
||||
})
|
||||
expect(yield* websearch.query({ query: "effect typescript", providerID: WebSearch.ID.make("tinyfish") })).toEqual(
|
||||
new WebSearch.Response({
|
||||
providerID: WebSearch.ID.make("tinyfish"),
|
||||
results: [
|
||||
{
|
||||
url: "https://effect.website",
|
||||
title: "Effect",
|
||||
content: "Effect documentation",
|
||||
time: {},
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
expect(requests[0]).toMatchObject({
|
||||
url: WebSearchTinyFish.endpoint,
|
||||
headers: { "x-tinyfish-access-mode": "keyless" },
|
||||
body: {
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: "tools/call",
|
||||
params: {
|
||||
name: "search",
|
||||
arguments: { query: "effect typescript" },
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(requests[0]?.headers.authorization).toBeUndefined()
|
||||
|
||||
yield* integrations.connection.key({
|
||||
integrationID: Integration.ID.make("tinyfish"),
|
||||
key: "tinyfish-secret",
|
||||
})
|
||||
yield* websearch.query({ query: "effect typescript", providerID: WebSearch.ID.make("tinyfish") })
|
||||
expect(requests[1]).toMatchObject({
|
||||
headers: { "x-api-key": "tinyfish-secret" },
|
||||
})
|
||||
expect(requests[1]?.headers["x-tinyfish-access-mode"]).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ title: "Effect"
|
||||
and decodes responses into OpenCode schema values.
|
||||
|
||||
```sh
|
||||
bun add @opencode/client@beta effect
|
||||
bun add @opencode/client effect
|
||||
```
|
||||
|
||||
## Create a client
|
||||
|
||||
@@ -10,7 +10,7 @@ network. Its native types and methods are generated from the same contract as th
|
||||
## Install
|
||||
|
||||
```sh
|
||||
bun add @opencode/client@beta
|
||||
bun add @opencode/client
|
||||
```
|
||||
|
||||
## Create a client
|
||||
|
||||
@@ -7,7 +7,7 @@ Effects or Streams, callbacks return Effects, and plugin lifetime is represented
|
||||
plugin package.
|
||||
|
||||
```sh
|
||||
bun add @opencode/plugin@beta effect
|
||||
bun add @opencode/plugin effect
|
||||
```
|
||||
|
||||
Export an Effect plugin from `.opencode/plugins/` to load it automatically.
|
||||
|
||||
@@ -6,7 +6,7 @@ Use `@opencode/sdk/workerd` inside a Cloudflare Durable Object. This profile use
|
||||
persists durable events for eviction recovery, and replaces unavailable local filesystem and process services.
|
||||
|
||||
```sh
|
||||
bun add @opencode/sdk@beta
|
||||
bun add @opencode/sdk
|
||||
```
|
||||
|
||||
Hold one host for the lifetime of the Durable Object instance instead of creating one for every request.
|
||||
|
||||
@@ -6,7 +6,7 @@ title: "Effect"
|
||||
the owning Scope releases the router, Location services, fibers, and plugin registrations.
|
||||
|
||||
```sh
|
||||
bun add @opencode/sdk@beta effect
|
||||
bun add @opencode/sdk effect
|
||||
```
|
||||
|
||||
## Create a host
|
||||
|
||||
@@ -12,7 +12,7 @@ For Cloudflare Durable Objects, see the [Cloudflare guide](/build/sdk/cloudflare
|
||||
Install the SDK:
|
||||
|
||||
```sh
|
||||
bun add @opencode/sdk@beta
|
||||
bun add @opencode/sdk
|
||||
```
|
||||
|
||||
## Create a host
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: npm install --global @opencode/cli@beta
|
||||
- run: npm install --global @opencode/cli
|
||||
- run: opencode run --standalone --model anthropic/claude-sonnet-4-5 "Review this repository for correctness and summarize any issues."
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
|
||||
@@ -10,11 +10,10 @@ OpenCode installs and runs as `opencode`.
|
||||
|
||||
<div data-install-code>
|
||||
<CodeBlock
|
||||
code={`$ npm install -g @opencode/cli@beta
|
||||
$ bun install -g --trust @opencode/cli@beta
|
||||
$ pnpm add -g --allow-build=@opencode/cli @opencode/cli@beta
|
||||
$ yarn global add @opencode/cli@beta
|
||||
$ paru -S opencode-beta
|
||||
code={`$ npm install -g @opencode/cli
|
||||
$ bun install -g --trust @opencode/cli
|
||||
$ pnpm add -g --allow-build=@opencode/cli @opencode/cli
|
||||
$ yarn global add @opencode/cli
|
||||
$ curl -fsSL https://opencode.ai/v2/install | bash`}
|
||||
/>
|
||||
</div>
|
||||
@@ -22,10 +21,9 @@ $ curl -fsSL https://opencode.ai/v2/install | bash`}
|
||||
The npm package uses a trusted postinstall script to select the native `opencode` binary for your platform. The Bun and pnpm
|
||||
commands above explicitly allow that script to run.
|
||||
|
||||
On Arch Linux, install [`opencode-beta`](https://aur.archlinux.org/packages/opencode-beta) from the AUR with `paru`.
|
||||
It provides the `opencode` command; manage updates through your AUR helper.
|
||||
Docker images use versioned tags, for example `ghcr.io/anomalyco/opencode:2.0.0`.
|
||||
|
||||
Homebrew, Windows package managers, Docker, and standalone binaries are not supported.
|
||||
Homebrew, AUR, Windows package managers, and standalone binaries are not supported.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user