Merge remote-tracking branch 'upstream/dev' into refactor-shells
This commit is contained in:
commit
5a7e69b325
88 changed files with 2959 additions and 1699 deletions
|
|
@ -1,899 +0,0 @@
|
|||
---
|
||||
description: Translate content for a specified locale while preserving technical terms
|
||||
mode: subagent
|
||||
model: opencode/gpt-5.4
|
||||
---
|
||||
|
||||
You are a professional translator and localization specialist.
|
||||
|
||||
Translate the user's content into the requested target locale (language + region, e.g. fr-FR, de-DE).
|
||||
|
||||
Requirements:
|
||||
|
||||
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
|
||||
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
|
||||
- Also preserve every term listed in the Do-Not-Translate glossary below.
|
||||
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
|
||||
- Do not modify fenced code blocks.
|
||||
- Output ONLY the translation (no commentary).
|
||||
|
||||
If the target locale is missing, ask the user to provide it.
|
||||
If no locale-specific glossary exists, use the global glossary only.
|
||||
|
||||
---
|
||||
|
||||
# Locale-Specific Glossaries
|
||||
|
||||
When a locale glossary exists, use it to:
|
||||
|
||||
- Apply preferred wording for recurring UI/docs terms in that locale
|
||||
- Preserve locale-specific do-not-translate terms and casing decisions
|
||||
- Prefer natural phrasing over literal translation when the locale file calls it out
|
||||
- If the repo uses a locale alias slug, apply that file too (for example, `pt-BR` maps to `br.md` in this repo)
|
||||
|
||||
Locale guidance does not override code/command preservation rules or the global Do-Not-Translate glossary below.
|
||||
|
||||
---
|
||||
|
||||
# Do-Not-Translate Terms (OpenCode Docs)
|
||||
|
||||
Generated from: `packages/web/src/content/docs/*.mdx` (default English docs)
|
||||
Generated on: 2026-02-10
|
||||
|
||||
Use this as a translation QA checklist / glossary. Preserve listed terms exactly (spelling, casing, punctuation).
|
||||
|
||||
General rules (verbatim, even if not listed below):
|
||||
|
||||
- Anything inside inline code (single backticks) or fenced code blocks (triple backticks)
|
||||
- MDX/JS code in docs: `import ... from "..."`, component tags, identifiers
|
||||
- CLI commands, flags, config keys/values, file paths, URLs/domains, and env vars
|
||||
|
||||
## Proper nouns and product names
|
||||
|
||||
Additional (not reliably captured via link text):
|
||||
|
||||
```text
|
||||
Astro
|
||||
Bun
|
||||
Chocolatey
|
||||
Cursor
|
||||
Docker
|
||||
Git
|
||||
GitHub Actions
|
||||
GitLab CI
|
||||
GNOME Terminal
|
||||
Homebrew
|
||||
Mise
|
||||
Neovim
|
||||
Node.js
|
||||
npm
|
||||
Obsidian
|
||||
opencode
|
||||
opencode-ai
|
||||
Paru
|
||||
pnpm
|
||||
ripgrep
|
||||
Scoop
|
||||
SST
|
||||
Starlight
|
||||
Visual Studio Code
|
||||
VS Code
|
||||
VSCodium
|
||||
Windsurf
|
||||
Windows Terminal
|
||||
Yarn
|
||||
Zellij
|
||||
Zed
|
||||
anomalyco
|
||||
```
|
||||
|
||||
Extracted from link labels in the English docs (review and prune as desired):
|
||||
|
||||
```text
|
||||
@openspoon/subtask2
|
||||
302.AI console
|
||||
ACP progress report
|
||||
Agent Client Protocol
|
||||
Agent Skills
|
||||
Agentic
|
||||
AGENTS.md
|
||||
AI SDK
|
||||
Alacritty
|
||||
Anthropic
|
||||
Anthropic's Data Policies
|
||||
Atom One
|
||||
Avante.nvim
|
||||
Ayu
|
||||
Azure AI Foundry
|
||||
Azure portal
|
||||
Baseten
|
||||
built-in GITHUB_TOKEN
|
||||
Bun.$
|
||||
Catppuccin
|
||||
Cerebras console
|
||||
ChatGPT Plus or Pro
|
||||
Cloudflare dashboard
|
||||
CodeCompanion.nvim
|
||||
CodeNomad
|
||||
Configuring Adapters: Environment Variables
|
||||
Context7 MCP server
|
||||
Cortecs console
|
||||
Deep Infra dashboard
|
||||
DeepSeek console
|
||||
Duo Agent Platform
|
||||
Everforest
|
||||
Fireworks AI console
|
||||
Firmware dashboard
|
||||
Ghostty
|
||||
GitLab CLI agents docs
|
||||
GitLab docs
|
||||
GitLab User Settings > Access Tokens
|
||||
Granular Rules (Object Syntax)
|
||||
Grep by Vercel
|
||||
Groq console
|
||||
Gruvbox
|
||||
Helicone
|
||||
Helicone documentation
|
||||
Helicone Header Directory
|
||||
Helicone's Model Directory
|
||||
Hugging Face Inference Providers
|
||||
Hugging Face settings
|
||||
install WSL
|
||||
IO.NET console
|
||||
JetBrains IDE
|
||||
Kanagawa
|
||||
Kitty
|
||||
MiniMax API Console
|
||||
Models.dev
|
||||
Moonshot AI console
|
||||
Nebius Token Factory console
|
||||
Nord
|
||||
OAuth
|
||||
Ollama integration docs
|
||||
OpenAI's Data Policies
|
||||
OpenChamber
|
||||
OpenCode
|
||||
OpenCode config
|
||||
OpenCode Config
|
||||
OpenCode TUI with the opencode theme
|
||||
OpenCode Web - Active Session
|
||||
OpenCode Web - New Session
|
||||
OpenCode Web - See Servers
|
||||
OpenCode Zen
|
||||
OpenCode-Obsidian
|
||||
OpenRouter dashboard
|
||||
OpenWork
|
||||
OVHcloud panel
|
||||
Pro+ subscription
|
||||
SAP BTP Cockpit
|
||||
Scaleway Console IAM settings
|
||||
Scaleway Generative APIs
|
||||
SDK documentation
|
||||
Sentry MCP server
|
||||
shell API
|
||||
Together AI console
|
||||
Tokyonight
|
||||
Unified Billing
|
||||
Venice AI console
|
||||
Vercel dashboard
|
||||
WezTerm
|
||||
Windows Subsystem for Linux (WSL)
|
||||
WSL
|
||||
WSL (Windows Subsystem for Linux)
|
||||
WSL extension
|
||||
xAI console
|
||||
Z.AI API console
|
||||
Zed
|
||||
ZenMux dashboard
|
||||
Zod
|
||||
```
|
||||
|
||||
## Acronyms and initialisms
|
||||
|
||||
```text
|
||||
ACP
|
||||
AGENTS
|
||||
AI
|
||||
AI21
|
||||
ANSI
|
||||
API
|
||||
AST
|
||||
AWS
|
||||
BTP
|
||||
CD
|
||||
CDN
|
||||
CI
|
||||
CLI
|
||||
CMD
|
||||
CORS
|
||||
DEBUG
|
||||
EKS
|
||||
ERROR
|
||||
FAQ
|
||||
GLM
|
||||
GNOME
|
||||
GPT
|
||||
HTML
|
||||
HTTP
|
||||
HTTPS
|
||||
IAM
|
||||
ID
|
||||
IDE
|
||||
INFO
|
||||
IO
|
||||
IP
|
||||
IRSA
|
||||
JS
|
||||
JSON
|
||||
JSONC
|
||||
K2
|
||||
LLM
|
||||
LM
|
||||
LSP
|
||||
M2
|
||||
MCP
|
||||
MR
|
||||
NET
|
||||
NPM
|
||||
NTLM
|
||||
OIDC
|
||||
OS
|
||||
PAT
|
||||
PATH
|
||||
PHP
|
||||
PR
|
||||
PTY
|
||||
README
|
||||
RFC
|
||||
RPC
|
||||
SAP
|
||||
SDK
|
||||
SKILL
|
||||
SSE
|
||||
SSO
|
||||
TS
|
||||
TTY
|
||||
TUI
|
||||
UI
|
||||
URL
|
||||
US
|
||||
UX
|
||||
VCS
|
||||
VPC
|
||||
VPN
|
||||
VS
|
||||
WARN
|
||||
WSL
|
||||
X11
|
||||
YAML
|
||||
```
|
||||
|
||||
## Code identifiers used in prose (CamelCase, mixedCase)
|
||||
|
||||
```text
|
||||
apiKey
|
||||
AppleScript
|
||||
AssistantMessage
|
||||
baseURL
|
||||
BurntSushi
|
||||
ChatGPT
|
||||
ClangFormat
|
||||
CodeCompanion
|
||||
CodeNomad
|
||||
DeepSeek
|
||||
DefaultV2
|
||||
FileContent
|
||||
FileDiff
|
||||
FileNode
|
||||
fineGrained
|
||||
FormatterStatus
|
||||
GitHub
|
||||
GitLab
|
||||
iTerm2
|
||||
JavaScript
|
||||
JetBrains
|
||||
macOS
|
||||
mDNS
|
||||
MiniMax
|
||||
NeuralNomadsAI
|
||||
NickvanDyke
|
||||
NoeFabris
|
||||
OpenAI
|
||||
OpenAPI
|
||||
OpenChamber
|
||||
OpenCode
|
||||
OpenRouter
|
||||
OpenTUI
|
||||
OpenWork
|
||||
ownUserPermissions
|
||||
PowerShell
|
||||
ProviderAuthAuthorization
|
||||
ProviderAuthMethod
|
||||
ProviderInitError
|
||||
SessionStatus
|
||||
TabItem
|
||||
tokenType
|
||||
ToolIDs
|
||||
ToolList
|
||||
TypeScript
|
||||
typesUrl
|
||||
UserMessage
|
||||
VcsInfo
|
||||
WebView2
|
||||
WezTerm
|
||||
xAI
|
||||
ZenMux
|
||||
```
|
||||
|
||||
## OpenCode CLI commands (as shown in docs)
|
||||
|
||||
```text
|
||||
opencode
|
||||
opencode [project]
|
||||
opencode /path/to/project
|
||||
opencode acp
|
||||
opencode agent [command]
|
||||
opencode agent create
|
||||
opencode agent list
|
||||
opencode attach [url]
|
||||
opencode attach http://10.20.30.40:4096
|
||||
opencode attach http://localhost:4096
|
||||
opencode auth [command]
|
||||
opencode auth list
|
||||
opencode auth login
|
||||
opencode auth logout
|
||||
opencode auth ls
|
||||
opencode export [sessionID]
|
||||
opencode github [command]
|
||||
opencode github install
|
||||
opencode github run
|
||||
opencode import <file>
|
||||
opencode import https://opncd.ai/s/abc123
|
||||
opencode import session.json
|
||||
opencode mcp [command]
|
||||
opencode mcp add
|
||||
opencode mcp auth [name]
|
||||
opencode mcp auth list
|
||||
opencode mcp auth ls
|
||||
opencode mcp auth my-oauth-server
|
||||
opencode mcp auth sentry
|
||||
opencode mcp debug <name>
|
||||
opencode mcp debug my-oauth-server
|
||||
opencode mcp list
|
||||
opencode mcp logout [name]
|
||||
opencode mcp logout my-oauth-server
|
||||
opencode mcp ls
|
||||
opencode models --refresh
|
||||
opencode models [provider]
|
||||
opencode models anthropic
|
||||
opencode run [message..]
|
||||
opencode run Explain the use of context in Go
|
||||
opencode serve
|
||||
opencode serve --cors http://localhost:5173 --cors https://app.example.com
|
||||
opencode serve --hostname 0.0.0.0 --port 4096
|
||||
opencode serve [--port <number>] [--hostname <string>] [--cors <origin>]
|
||||
opencode session [command]
|
||||
opencode session list
|
||||
opencode session delete <sessionID>
|
||||
opencode stats
|
||||
opencode uninstall
|
||||
opencode upgrade
|
||||
opencode upgrade [target]
|
||||
opencode upgrade v0.1.48
|
||||
opencode web
|
||||
opencode web --cors https://example.com
|
||||
opencode web --hostname 0.0.0.0
|
||||
opencode web --mdns
|
||||
opencode web --mdns --mdns-domain myproject.local
|
||||
opencode web --port 4096
|
||||
opencode web --port 4096 --hostname 0.0.0.0
|
||||
opencode.server.close()
|
||||
```
|
||||
|
||||
## Slash commands and routes
|
||||
|
||||
```text
|
||||
/agent
|
||||
/auth/:id
|
||||
/clear
|
||||
/command
|
||||
/config
|
||||
/config/providers
|
||||
/connect
|
||||
/continue
|
||||
/doc
|
||||
/editor
|
||||
/event
|
||||
/experimental/tool?provider=<p>&model=<m>
|
||||
/experimental/tool/ids
|
||||
/export
|
||||
/file?path=<path>
|
||||
/file/content?path=<p>
|
||||
/file/status
|
||||
/find?pattern=<pat>
|
||||
/find/file
|
||||
/find/file?query=<q>
|
||||
/find/symbol?query=<q>
|
||||
/formatter
|
||||
/global/event
|
||||
/global/health
|
||||
/help
|
||||
/init
|
||||
/instance/dispose
|
||||
/log
|
||||
/lsp
|
||||
/mcp
|
||||
/mnt/
|
||||
/mnt/c/
|
||||
/mnt/d/
|
||||
/models
|
||||
/oc
|
||||
/opencode
|
||||
/path
|
||||
/project
|
||||
/project/current
|
||||
/provider
|
||||
/provider/{id}/oauth/authorize
|
||||
/provider/{id}/oauth/callback
|
||||
/provider/auth
|
||||
/q
|
||||
/quit
|
||||
/redo
|
||||
/resume
|
||||
/session
|
||||
/session/:id
|
||||
/session/:id/abort
|
||||
/session/:id/children
|
||||
/session/:id/command
|
||||
/session/:id/diff
|
||||
/session/:id/fork
|
||||
/session/:id/init
|
||||
/session/:id/message
|
||||
/session/:id/message/:messageID
|
||||
/session/:id/permissions/:permissionID
|
||||
/session/:id/prompt_async
|
||||
/session/:id/revert
|
||||
/session/:id/share
|
||||
/session/:id/shell
|
||||
/session/:id/summarize
|
||||
/session/:id/todo
|
||||
/session/:id/unrevert
|
||||
/session/status
|
||||
/share
|
||||
/summarize
|
||||
/theme
|
||||
/tui
|
||||
/tui/append-prompt
|
||||
/tui/clear-prompt
|
||||
/tui/control/next
|
||||
/tui/control/response
|
||||
/tui/execute-command
|
||||
/tui/open-help
|
||||
/tui/open-models
|
||||
/tui/open-sessions
|
||||
/tui/open-themes
|
||||
/tui/show-toast
|
||||
/tui/submit-prompt
|
||||
/undo
|
||||
/Users/username
|
||||
/Users/username/projects/*
|
||||
/vcs
|
||||
```
|
||||
|
||||
## CLI flags and short options
|
||||
|
||||
```text
|
||||
--agent
|
||||
--attach
|
||||
--command
|
||||
--continue
|
||||
--cors
|
||||
--cwd
|
||||
--days
|
||||
--dir
|
||||
--dry-run
|
||||
--event
|
||||
--file
|
||||
--force
|
||||
--fork
|
||||
--format
|
||||
--help
|
||||
--hostname
|
||||
--hostname 0.0.0.0
|
||||
--keep-config
|
||||
--keep-data
|
||||
--log-level
|
||||
--max-count
|
||||
--mdns
|
||||
--mdns-domain
|
||||
--method
|
||||
--model
|
||||
--models
|
||||
--port
|
||||
--print-logs
|
||||
--project
|
||||
--prompt
|
||||
--refresh
|
||||
--session
|
||||
--share
|
||||
--title
|
||||
--token
|
||||
--tools
|
||||
--verbose
|
||||
--version
|
||||
--wait
|
||||
|
||||
-c
|
||||
-d
|
||||
-f
|
||||
-h
|
||||
-m
|
||||
-n
|
||||
-s
|
||||
-v
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
```text
|
||||
AI_API_URL
|
||||
AI_FLOW_CONTEXT
|
||||
AI_FLOW_EVENT
|
||||
AI_FLOW_INPUT
|
||||
AICORE_DEPLOYMENT_ID
|
||||
AICORE_RESOURCE_GROUP
|
||||
AICORE_SERVICE_KEY
|
||||
ANTHROPIC_API_KEY
|
||||
AWS_ACCESS_KEY_ID
|
||||
AWS_BEARER_TOKEN_BEDROCK
|
||||
AWS_PROFILE
|
||||
AWS_REGION
|
||||
AWS_ROLE_ARN
|
||||
AWS_SECRET_ACCESS_KEY
|
||||
AWS_WEB_IDENTITY_TOKEN_FILE
|
||||
AZURE_COGNITIVE_SERVICES_RESOURCE_NAME
|
||||
AZURE_RESOURCE_NAME
|
||||
CI_PROJECT_DIR
|
||||
CI_SERVER_FQDN
|
||||
CI_WORKLOAD_REF
|
||||
CLOUDFLARE_ACCOUNT_ID
|
||||
CLOUDFLARE_API_TOKEN
|
||||
CLOUDFLARE_GATEWAY_ID
|
||||
CONTEXT7_API_KEY
|
||||
GITHUB_TOKEN
|
||||
GITLAB_AI_GATEWAY_URL
|
||||
GITLAB_HOST
|
||||
GITLAB_INSTANCE_URL
|
||||
GITLAB_OAUTH_CLIENT_ID
|
||||
GITLAB_TOKEN
|
||||
GITLAB_TOKEN_OPENCODE
|
||||
GOOGLE_APPLICATION_CREDENTIALS
|
||||
GOOGLE_CLOUD_PROJECT
|
||||
HTTP_PROXY
|
||||
HTTPS_PROXY
|
||||
K2_
|
||||
MY_API_KEY
|
||||
MY_ENV_VAR
|
||||
MY_MCP_CLIENT_ID
|
||||
MY_MCP_CLIENT_SECRET
|
||||
NO_PROXY
|
||||
NODE_ENV
|
||||
NODE_EXTRA_CA_CERTS
|
||||
NPM_AUTH_TOKEN
|
||||
OC_ALLOW_WAYLAND
|
||||
OPENCODE_API_KEY
|
||||
OPENCODE_AUTH_JSON
|
||||
OPENCODE_AUTO_SHARE
|
||||
OPENCODE_CLIENT
|
||||
OPENCODE_CONFIG
|
||||
OPENCODE_CONFIG_CONTENT
|
||||
OPENCODE_CONFIG_DIR
|
||||
OPENCODE_DISABLE_AUTOCOMPACT
|
||||
OPENCODE_DISABLE_AUTOUPDATE
|
||||
OPENCODE_DISABLE_CLAUDE_CODE
|
||||
OPENCODE_DISABLE_CLAUDE_CODE_PROMPT
|
||||
OPENCODE_DISABLE_CLAUDE_CODE_SKILLS
|
||||
OPENCODE_DISABLE_DEFAULT_PLUGINS
|
||||
OPENCODE_DISABLE_LSP_DOWNLOAD
|
||||
OPENCODE_DISABLE_MODELS_FETCH
|
||||
OPENCODE_DISABLE_PRUNE
|
||||
OPENCODE_DISABLE_TERMINAL_TITLE
|
||||
OPENCODE_ENABLE_EXA
|
||||
OPENCODE_ENABLE_EXPERIMENTAL_MODELS
|
||||
OPENCODE_EXPERIMENTAL
|
||||
OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS
|
||||
OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT
|
||||
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER
|
||||
OPENCODE_EXPERIMENTAL_EXA
|
||||
OPENCODE_EXPERIMENTAL_FILEWATCHER
|
||||
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY
|
||||
OPENCODE_EXPERIMENTAL_LSP_TOOL
|
||||
OPENCODE_EXPERIMENTAL_LSP_TY
|
||||
OPENCODE_EXPERIMENTAL_MARKDOWN
|
||||
OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX
|
||||
OPENCODE_EXPERIMENTAL_OXFMT
|
||||
OPENCODE_EXPERIMENTAL_PLAN_MODE
|
||||
OPENCODE_ENABLE_QUESTION_TOOL
|
||||
OPENCODE_FAKE_VCS
|
||||
OPENCODE_GIT_BASH_PATH
|
||||
OPENCODE_MODEL
|
||||
OPENCODE_MODELS_URL
|
||||
OPENCODE_PERMISSION
|
||||
OPENCODE_PORT
|
||||
OPENCODE_SERVER_PASSWORD
|
||||
OPENCODE_SERVER_USERNAME
|
||||
PROJECT_ROOT
|
||||
RESOURCE_NAME
|
||||
RUST_LOG
|
||||
VARIABLE_NAME
|
||||
VERTEX_LOCATION
|
||||
XDG_CONFIG_HOME
|
||||
```
|
||||
|
||||
## Package/module identifiers
|
||||
|
||||
```text
|
||||
../../../config.mjs
|
||||
@astrojs/starlight/components
|
||||
@opencode-ai/plugin
|
||||
@opencode-ai/sdk
|
||||
path
|
||||
shescape
|
||||
zod
|
||||
|
||||
@
|
||||
@ai-sdk/anthropic
|
||||
@ai-sdk/cerebras
|
||||
@ai-sdk/google
|
||||
@ai-sdk/openai
|
||||
@ai-sdk/openai-compatible
|
||||
@File#L37-42
|
||||
@modelcontextprotocol/server-everything
|
||||
@opencode
|
||||
```
|
||||
|
||||
## GitHub owner/repo slugs referenced in docs
|
||||
|
||||
```text
|
||||
24601/opencode-zellij-namer
|
||||
angristan/opencode-wakatime
|
||||
anomalyco/opencode
|
||||
apps/opencode-agent
|
||||
athal7/opencode-devcontainers
|
||||
awesome-opencode/awesome-opencode
|
||||
backnotprop/plannotator
|
||||
ben-vargas/ai-sdk-provider-opencode-sdk
|
||||
btriapitsyn/openchamber
|
||||
BurntSushi/ripgrep
|
||||
Cluster444/agentic
|
||||
code-yeongyu/oh-my-opencode
|
||||
darrenhinde/opencode-agents
|
||||
different-ai/opencode-scheduler
|
||||
different-ai/openwork
|
||||
features/copilot
|
||||
folke/tokyonight.nvim
|
||||
franlol/opencode-md-table-formatter
|
||||
ggml-org/llama.cpp
|
||||
ghoulr/opencode-websearch-cited.git
|
||||
H2Shami/opencode-helicone-session
|
||||
hosenur/portal
|
||||
jamesmurdza/daytona
|
||||
jenslys/opencode-gemini-auth
|
||||
JRedeker/opencode-morph-fast-apply
|
||||
JRedeker/opencode-shell-strategy
|
||||
kdcokenny/ocx
|
||||
kdcokenny/opencode-background-agents
|
||||
kdcokenny/opencode-notify
|
||||
kdcokenny/opencode-workspace
|
||||
kdcokenny/opencode-worktree
|
||||
login/device
|
||||
mohak34/opencode-notifier
|
||||
morhetz/gruvbox
|
||||
mtymek/opencode-obsidian
|
||||
NeuralNomadsAI/CodeNomad
|
||||
nick-vi/opencode-type-inject
|
||||
NickvanDyke/opencode.nvim
|
||||
NoeFabris/opencode-antigravity-auth
|
||||
nordtheme/nord
|
||||
numman-ali/opencode-openai-codex-auth
|
||||
olimorris/codecompanion.nvim
|
||||
panta82/opencode-notificator
|
||||
rebelot/kanagawa.nvim
|
||||
remorses/kimaki
|
||||
sainnhe/everforest
|
||||
shekohex/opencode-google-antigravity-auth
|
||||
shekohex/opencode-pty.git
|
||||
spoons-and-mirrors/subtask2
|
||||
sudo-tee/opencode.nvim
|
||||
supermemoryai/opencode-supermemory
|
||||
Tarquinen/opencode-dynamic-context-pruning
|
||||
Th3Whit3Wolf/one-nvim
|
||||
upstash/context7
|
||||
vtemian/micode
|
||||
vtemian/octto
|
||||
yetone/avante.nvim
|
||||
zenobi-us/opencode-plugin-template
|
||||
zenobi-us/opencode-skillful
|
||||
```
|
||||
|
||||
## Paths, filenames, globs, and URLs
|
||||
|
||||
```text
|
||||
./.opencode/themes/*.json
|
||||
./<project-slug>/storage/
|
||||
./config/#custom-directory
|
||||
./global/storage/
|
||||
.agents/skills/*/SKILL.md
|
||||
.agents/skills/<name>/SKILL.md
|
||||
.clang-format
|
||||
.claude
|
||||
.claude/skills
|
||||
.claude/skills/*/SKILL.md
|
||||
.claude/skills/<name>/SKILL.md
|
||||
.env
|
||||
.github/workflows/opencode.yml
|
||||
.gitignore
|
||||
.gitlab-ci.yml
|
||||
.ignore
|
||||
.NET SDK
|
||||
.npmrc
|
||||
.ocamlformat
|
||||
.opencode
|
||||
.opencode/
|
||||
.opencode/agents/
|
||||
.opencode/commands/
|
||||
.opencode/commands/test.md
|
||||
.opencode/modes/
|
||||
.opencode/plans/*.md
|
||||
.opencode/plugins/
|
||||
.opencode/skills/<name>/SKILL.md
|
||||
.opencode/skills/git-release/SKILL.md
|
||||
.opencode/tools/
|
||||
.well-known/opencode
|
||||
{ type: "raw" \| "patch", content: string }
|
||||
{file:path/to/file}
|
||||
**/*.js
|
||||
%USERPROFILE%/intelephense/license.txt
|
||||
%USERPROFILE%\.cache\opencode
|
||||
%USERPROFILE%\.config\opencode\opencode.jsonc
|
||||
%USERPROFILE%\.config\opencode\plugins
|
||||
%USERPROFILE%\.local\share\opencode
|
||||
%USERPROFILE%\.local\share\opencode\log
|
||||
<project-root>/.opencode/themes/*.json
|
||||
<providerId>/<modelId>
|
||||
<your-project>/.opencode/plugins/
|
||||
~
|
||||
~/...
|
||||
~/.agents/skills/*/SKILL.md
|
||||
~/.agents/skills/<name>/SKILL.md
|
||||
~/.aws/credentials
|
||||
~/.bashrc
|
||||
~/.cache/opencode
|
||||
~/.cache/opencode/node_modules/
|
||||
~/.claude/CLAUDE.md
|
||||
~/.claude/skills/
|
||||
~/.claude/skills/*/SKILL.md
|
||||
~/.claude/skills/<name>/SKILL.md
|
||||
~/.config/opencode
|
||||
~/.config/opencode/AGENTS.md
|
||||
~/.config/opencode/agents/
|
||||
~/.config/opencode/commands/
|
||||
~/.config/opencode/modes/
|
||||
~/.config/opencode/opencode.json
|
||||
~/.config/opencode/opencode.jsonc
|
||||
~/.config/opencode/plugins/
|
||||
~/.config/opencode/skills/*/SKILL.md
|
||||
~/.config/opencode/skills/<name>/SKILL.md
|
||||
~/.config/opencode/themes/*.json
|
||||
~/.config/opencode/tools/
|
||||
~/.config/zed/settings.json
|
||||
~/.local/share
|
||||
~/.local/share/opencode/
|
||||
~/.local/share/opencode/auth.json
|
||||
~/.local/share/opencode/log/
|
||||
~/.local/share/opencode/mcp-auth.json
|
||||
~/.local/share/opencode/opencode.jsonc
|
||||
~/.npmrc
|
||||
~/.zshrc
|
||||
~/code/
|
||||
~/Library/Application Support
|
||||
~/projects/*
|
||||
~/projects/personal/
|
||||
${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts
|
||||
$HOME/intelephense/license.txt
|
||||
$HOME/projects/*
|
||||
$XDG_CONFIG_HOME/opencode/themes/*.json
|
||||
agent/
|
||||
agents/
|
||||
build/
|
||||
commands/
|
||||
dist/
|
||||
http://<wsl-ip>:4096
|
||||
http://127.0.0.1:8080/callback
|
||||
http://localhost:<port>
|
||||
http://localhost:4096
|
||||
http://localhost:4096/doc
|
||||
https://app.example.com
|
||||
https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/
|
||||
https://opencode.ai/zen/v1/chat/completions
|
||||
https://opencode.ai/zen/v1/messages
|
||||
https://opencode.ai/zen/v1/models/gemini-3-flash
|
||||
https://opencode.ai/zen/v1/models/gemini-3-pro
|
||||
https://opencode.ai/zen/v1/responses
|
||||
https://RESOURCE_NAME.openai.azure.com/
|
||||
laravel/pint
|
||||
log/
|
||||
model: "anthropic/claude-sonnet-4-5"
|
||||
modes/
|
||||
node_modules/
|
||||
openai/gpt-4.1
|
||||
opencode.ai/config.json
|
||||
opencode/<model-id>
|
||||
opencode/gpt-5.1-codex
|
||||
opencode/gpt-5.2-codex
|
||||
opencode/kimi-k2
|
||||
openrouter/google/gemini-2.5-flash
|
||||
opncd.ai/s/<share-id>
|
||||
packages/*/AGENTS.md
|
||||
plugins/
|
||||
project/
|
||||
provider_id/model_id
|
||||
provider/model
|
||||
provider/model-id
|
||||
rm -rf ~/.cache/opencode
|
||||
skills/
|
||||
skills/*/SKILL.md
|
||||
src/**/*.ts
|
||||
themes/
|
||||
tools/
|
||||
```
|
||||
|
||||
## Keybind strings
|
||||
|
||||
```text
|
||||
alt+b
|
||||
Alt+Ctrl+K
|
||||
alt+d
|
||||
alt+f
|
||||
Cmd+Esc
|
||||
Cmd+Option+K
|
||||
Cmd+Shift+Esc
|
||||
Cmd+Shift+G
|
||||
Cmd+Shift+P
|
||||
ctrl+a
|
||||
ctrl+b
|
||||
ctrl+d
|
||||
ctrl+e
|
||||
Ctrl+Esc
|
||||
ctrl+f
|
||||
ctrl+g
|
||||
ctrl+k
|
||||
Ctrl+Shift+Esc
|
||||
Ctrl+Shift+P
|
||||
ctrl+t
|
||||
ctrl+u
|
||||
ctrl+w
|
||||
ctrl+x
|
||||
DELETE
|
||||
Shift+Enter
|
||||
WIN+R
|
||||
```
|
||||
|
||||
## Model ID strings referenced
|
||||
|
||||
```text
|
||||
{env:OPENCODE_MODEL}
|
||||
anthropic/claude-3-5-sonnet-20241022
|
||||
anthropic/claude-haiku-4-20250514
|
||||
anthropic/claude-haiku-4-5
|
||||
anthropic/claude-sonnet-4-20250514
|
||||
anthropic/claude-sonnet-4-5
|
||||
gitlab/duo-chat-haiku-4-5
|
||||
lmstudio/google/gemma-3n-e4b
|
||||
openai/gpt-4.1
|
||||
openai/gpt-5
|
||||
opencode/gpt-5.1-codex
|
||||
opencode/gpt-5.2-codex
|
||||
opencode/kimi-k2
|
||||
openrouter/google/gemini-2.5-flash
|
||||
```
|
||||
14
.opencode/command/translate.md
Normal file
14
.opencode/command/translate.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
description: translate English to other languages
|
||||
model: opencode/claude-opus-4-7
|
||||
---
|
||||
|
||||
run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
|
||||
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
|
||||
- Also preserve every term listed in the Do-Not-Translate glossary below.
|
||||
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
|
||||
- Do not modify fenced code blocks.
|
||||
|
|
@ -3,8 +3,8 @@ import { tool } from "@opencode-ai/plugin"
|
|||
const TEAM = {
|
||||
desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
|
||||
zen: ["fwang", "MrMushrooooom"],
|
||||
tui: ["thdxr", "kommander", "rekram1-node"],
|
||||
core: ["thdxr", "rekram1-node", "jlongster"],
|
||||
tui: ["kommander", "rekram1-node", "simonklee"],
|
||||
core: ["kitlangton", "rekram1-node", "jlongster"],
|
||||
docs: ["R44VC0RP"],
|
||||
windows: ["Hona"],
|
||||
} as const
|
||||
|
|
|
|||
62
bun.lock
62
bun.lock
|
|
@ -29,7 +29,7 @@
|
|||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
|
|
@ -83,7 +83,7 @@
|
|||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
|
|
@ -144,7 +144,7 @@
|
|||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.64",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
|
|
@ -168,7 +168,7 @@
|
|||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
@ -192,7 +192,7 @@
|
|||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode-ai/core",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
|
|
@ -226,7 +226,7 @@
|
|||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
|
|
@ -259,7 +259,7 @@
|
|||
},
|
||||
"packages/desktop-electron": {
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
|
|
@ -303,7 +303,7 @@
|
|||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
|
|
@ -332,7 +332,7 @@
|
|||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
|
|
@ -348,7 +348,7 @@
|
|||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
|
|
@ -393,7 +393,7 @@
|
|||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "2.5.1",
|
||||
"@openrouter/ai-sdk-provider": "2.8.1",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@opentelemetry/context-async-hooks": "2.6.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
|
||||
|
|
@ -491,7 +491,7 @@
|
|||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"effect": "catalog:",
|
||||
|
|
@ -506,8 +506,8 @@
|
|||
"typescript": "catalog:",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentui/core": ">=0.1.103",
|
||||
"@opentui/solid": ">=0.1.103",
|
||||
"@opentui/core": ">=0.1.104",
|
||||
"@opentui/solid": ">=0.1.104",
|
||||
},
|
||||
"optionalPeers": [
|
||||
"@opentui/core",
|
||||
|
|
@ -526,7 +526,7 @@
|
|||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
|
|
@ -541,7 +541,7 @@
|
|||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
|
|
@ -576,7 +576,7 @@
|
|||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
|
|
@ -625,7 +625,7 @@
|
|||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
|
@ -685,8 +685,8 @@
|
|||
"@npmcli/arborist": "9.4.0",
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opentui/core": "0.1.103",
|
||||
"@opentui/solid": "0.1.103",
|
||||
"@opentui/core": "0.1.104",
|
||||
"@opentui/solid": "0.1.104",
|
||||
"@pierre/diffs": "1.1.0-beta.18",
|
||||
"@playwright/test": "1.59.1",
|
||||
"@solid-primitives/storage": "4.3.3",
|
||||
|
|
@ -1585,7 +1585,7 @@
|
|||
|
||||
"@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"],
|
||||
|
||||
"@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="],
|
||||
"@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.8.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Y6j3yivgoEUf/kutD/k5GX/mzZfioRFoSx0gbQ+mIOzMaH/vJv1rCkztiuvlLw5xRYQil7oxHUZvmSfXqOx1NQ=="],
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||
|
||||
|
|
@ -1613,21 +1613,21 @@
|
|||
|
||||
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
|
||||
|
||||
"@opentui/core": ["@opentui/core@0.1.103", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.103", "@opentui/core-darwin-x64": "0.1.103", "@opentui/core-linux-arm64": "0.1.103", "@opentui/core-linux-x64": "0.1.103", "@opentui/core-win32-arm64": "0.1.103", "@opentui/core-win32-x64": "0.1.103", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-PWVv/bDmlk1i6X1f0zXs+jSaTrQ/ByX8wFbP2WinOObTGf//UbcRP4dbWxPXvOyka9QlmRBG/7GbloQSIStyVw=="],
|
||||
"@opentui/core": ["@opentui/core@0.1.104", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.104", "@opentui/core-darwin-x64": "0.1.104", "@opentui/core-linux-arm64": "0.1.104", "@opentui/core-linux-x64": "0.1.104", "@opentui/core-win32-arm64": "0.1.104", "@opentui/core-win32-x64": "0.1.104", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-29RZ+f7sw5B6ebDFRuhrDs2UOV7vR76npr0vVRAJAmIMtdLb4Dse35Y0Hk6WerFKNuX4ajlQK4eO3oAY/29KLQ=="],
|
||||
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.103", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lxCyedDkcen12IgBtXjkJ7iY66xa7VC4nxRNKCUeLY2ZP9hUE1AsDtbyQzqY+BQadsI/ZME9STzaHDCUFg0TpA=="],
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.104", "", { "os": "darwin", "cpu": "arm64" }, "sha512-83Bf+LOLYCgWccAuPzftchs8LIoTzYU8f8CM7GeUFqH0VLKs2Ey+TtCTsBWpeDklOal0XFQr5begotqc/ldPVg=="],
|
||||
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.103", "", { "os": "darwin", "cpu": "x64" }, "sha512-QMYD+zUDGQliJ6m5nuNvA72jtluFeyVMoHkuA5m/Xmed/u8eLfahAKmDj3kY66ntUroPHWevcpbpvd7NCFEoFQ=="],
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.104", "", { "os": "darwin", "cpu": "x64" }, "sha512-TAHRtEi7Gz2O3TXolPCiWpXsoapKllGCl74bOuJsyipMwfk3wuUu6SeqBPC/cmVJlp143dC8kcGLwLsd3dxsgA=="],
|
||||
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.103", "", { "os": "linux", "cpu": "arm64" }, "sha512-GzOvNr9dN6JaQ9qs7m8E75wLAHwT5CyxqkE6rEr1BO23/d2Ix7e3GYw/JRY5VnTge+eXrfDVbqNtPcQamUNiEA=="],
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.104", "", { "os": "linux", "cpu": "arm64" }, "sha512-WJ8ssxcRpoHWgGLDYlVjlYZGkmA2G9melgXTrzk6CbwhW5tVAE6AmW5Kq9DUHclYssCpNWTvZFMDsVsf5kn2Xw=="],
|
||||
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.103", "", { "os": "linux", "cpu": "x64" }, "sha512-odywllco5zUKNc60uD3JKaCybK64u6BfmpScs4a8Qn89yH/yk23bzWXDRWaGgQdY65L2/VCbcGs1ezA1S/2YTw=="],
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.104", "", { "os": "linux", "cpu": "x64" }, "sha512-I4DurCkJXJxxGwAq1wGZ1vzXh57GxdzljWeCJ5sIPy7coICn+/cWRw9gE9VxiWTa3T85l4YN0UFSQx7IFCEbvg=="],
|
||||
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.103", "", { "os": "win32", "cpu": "arm64" }, "sha512-tZL5w3Y0JnO7RkIvfuNDAzJn0j6+JIYl6M8DgPM5p8AQt+162S8LmbumzmqQLZl4cEev2eN7/tw72WIk6b+/CQ=="],
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.104", "", { "os": "win32", "cpu": "arm64" }, "sha512-PDwqGHlVUiAiZa8GXiPPJVWIxucQ5+2FLcK4C76VT8HJzUXiUEGJFtuvi9oY7k29p8olcFHW75Gj1WMUrw+JKw=="],
|
||||
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.103", "", { "os": "win32", "cpu": "x64" }, "sha512-wqnibt/OE5ldSzVPxEbriA0TjI2B11CJl4uJoLxTZ47KDx7tAFIMdhBf9IRAGNCSbcDuZ8ZGEFhV+SLaftkrlw=="],
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.104", "", { "os": "win32", "cpu": "x64" }, "sha512-fQGMxFshk/i7KskqaK5lB8u2K8Lx/LO9+PdfKu/+GBIMKmElpJXQ8Bq2sMf+7COcoXcjAjUJn8cpTyIqbFxAyQ=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/solid@0.1.103", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.103", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-L28WFBs17Z5JXkJhPagLy8tUamkttDaaQDdb2KEO01IQ9r81yLBRpYqD/lHp6UoSISXgwQVDQ9yNtxwR1BQZvQ=="],
|
||||
"@opentui/solid": ["@opentui/solid@0.1.104", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.104", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-Mma1OR3s0QZayRGdbdNv1m3H6IExRrF9QRUNWEZzDOqo3oNxTxaY9K7Qk6eGrk0unMjwwE113TSOdnG80RwYnQ=="],
|
||||
|
||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||
|
||||
|
|
@ -5723,6 +5723,8 @@
|
|||
|
||||
"ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.75", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-V8UKK4fNpI9cnrtsZBvUp9O9J6Y9fTKBRoSLyEaNGPirACewixmLDbXsSgAeownPVWiWpK34bFysd+XouI5Ywg=="],
|
||||
|
||||
"ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="],
|
||||
|
||||
"ajv-keywords/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
||||
|
||||
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-LpzWEZzURUEj7fcHGvh33gM7D9GNPE+XIvU0/hmdcQM=",
|
||||
"aarch64-linux": "sha256-0zdO3zuj6g9cMZFEOsvQJcKKcPjGVZJ2DkJdDcb2VCM=",
|
||||
"aarch64-darwin": "sha256-dmT8R9Pmzh5tjO8NCCCtENiQpJQeifQpVdhaty1MXOs=",
|
||||
"x86_64-darwin": "sha256-Q6rAQRoC6WaMAQl++YHAZmbNuO303cWgGaYzXaRlzy4="
|
||||
"x86_64-linux": "sha256-zQOuZkuOLm9xV5YzfcQiWSzvussXVw/dRPw4HhuYsdc=",
|
||||
"aarch64-linux": "sha256-J8Ak9rCCkKlBj0KhdUOklglpR3ntRP2VjtdmEJox6Ro=",
|
||||
"aarch64-darwin": "sha256-r4F6S9CfGR6qxSaSqu9AIjWCEKd/PuT1Wprcbzk0IwY=",
|
||||
"x86_64-darwin": "sha256-I/ztb7VnLbJYzAxf2egN0+YsJcP5yix3tIoHtiuRUXE="
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
|
|||
[
|
||||
ripgrep
|
||||
]
|
||||
# bun runs sysctl to detect if dunning on rosetta2
|
||||
# bun runs sysctl to detect if running on rosetta2
|
||||
++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@
|
|||
"@types/cross-spawn": "6.0.6",
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"@opentui/core": "0.1.103",
|
||||
"@opentui/solid": "0.1.103",
|
||||
"@opentui/core": "0.1.104",
|
||||
"@opentui/solid": "0.1.104",
|
||||
"ulid": "3.0.1",
|
||||
"@kobalte/core": "0.13.11",
|
||||
"@types/luxon": "3.7.1",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
10
packages/console/app/src/routes/zen/go/v1/models.ts
Normal file
10
packages/console/app/src/routes/zen/go/v1/models.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { getHandler, optionsHandler } from "../../util/modelsHandler"
|
||||
|
||||
export async function OPTIONS(_input: APIEvent) {
|
||||
return optionsHandler()
|
||||
}
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
return getHandler({ modelList: "lite" })
|
||||
}
|
||||
|
|
@ -101,12 +101,13 @@ export async function handler(
|
|||
const requestId = input.request.headers.get("x-opencode-request") ?? ""
|
||||
const projectId = input.request.headers.get("x-opencode-project") ?? ""
|
||||
const ocClient = input.request.headers.get("x-opencode-client") ?? ""
|
||||
const userAgent = input.request.headers.get("user-agent") ?? ""
|
||||
logger.metric({
|
||||
is_stream: isStream,
|
||||
session: sessionId,
|
||||
request: requestId,
|
||||
client: ocClient,
|
||||
...(model === "mimo-v2-pro-free" && JSON.stringify(body).length < 1000 ? { payload: JSON.stringify(body) } : {}),
|
||||
user_agent: userAgent,
|
||||
})
|
||||
const zenData = ZenData.list(opts.modelList)
|
||||
const modelInfo = validateModel(zenData, model)
|
||||
|
|
|
|||
36
packages/console/app/src/routes/zen/util/modelsHandler.ts
Normal file
36
packages/console/app/src/routes/zen/util/modelsHandler.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { ZenData } from "@opencode-ai/console-core/model.js"
|
||||
|
||||
export async function optionsHandler() {
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function getHandler(opts: { modelList: "lite" | "full"; disabledModels?: string[] }) {
|
||||
const zenData = ZenData.list(opts.modelList)
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
object: "list",
|
||||
data: Object.entries(zenData.models)
|
||||
.filter(([id]) => !opts.disabledModels?.includes(id))
|
||||
.filter(([id]) => !id.startsWith("alpha-"))
|
||||
.map(([id, _model]) => ({
|
||||
id,
|
||||
object: "model",
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
owned_by: "opencode",
|
||||
})),
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -3,59 +3,29 @@ import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/ind
|
|||
import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
|
||||
import { ZenData } from "@opencode-ai/console-core/model.js"
|
||||
import { optionsHandler, getHandler } from "~/routes/zen/util/modelsHandler"
|
||||
|
||||
export async function OPTIONS(_input: APIEvent) {
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
},
|
||||
})
|
||||
return optionsHandler()
|
||||
}
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
const zenData = ZenData.list("full")
|
||||
const disabledModels = await authenticate()
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
object: "list",
|
||||
data: Object.entries(zenData.models)
|
||||
.filter(([id]) => !disabledModels.includes(id))
|
||||
.filter(([id]) => !id.startsWith("alpha-"))
|
||||
.map(([id, _model]) => ({
|
||||
id,
|
||||
object: "model",
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
owned_by: "opencode",
|
||||
})),
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
async function authenticate() {
|
||||
const disabledModels = await (() => {
|
||||
const apiKey = input.request.headers.get("authorization")?.split(" ")[1]
|
||||
if (!apiKey) return []
|
||||
|
||||
const disabledModels = await Database.use((tx) =>
|
||||
return Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
model: ModelTable.model,
|
||||
})
|
||||
.from(KeyTable)
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
|
||||
.leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted)))
|
||||
.innerJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted)))
|
||||
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
|
||||
.then((rows) => rows.map((row) => row.model)),
|
||||
)
|
||||
})()
|
||||
|
||||
return disabledModels
|
||||
}
|
||||
return getHandler({ modelList: "full", disabledModels })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"name": "@opencode-ai/core",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"private": true,
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.14.25"
|
||||
version = "1.14.26"
|
||||
schema_version = 1
|
||||
authors = ["Anomaly"]
|
||||
repository = "https://github.com/anomalyco/opencode"
|
||||
|
|
@ -11,26 +11,26 @@ name = "OpenCode"
|
|||
icon = "./icons/opencode.svg"
|
||||
|
||||
[agent_servers.opencode.targets.darwin-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-linux-arm64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-linux-x64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "2.5.1",
|
||||
"@openrouter/ai-sdk-provider": "2.8.1",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@opentelemetry/context-async-hooks": "2.6.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
|
||||
|
|
|
|||
|
|
@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho
|
|||
|
||||
## Current Route Status
|
||||
|
||||
| Area | Status | Notes |
|
||||
| ------------------------- | ----------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| `question` | `bridged` | `GET /question`, reply, reject |
|
||||
| `permission` | `bridged` | list and reply |
|
||||
| `provider` | `bridged` | list, auth, OAuth authorize/callback |
|
||||
| `config` | `bridged` | read, providers, update |
|
||||
| `project` | `bridged` | list, current, git init, update |
|
||||
| `file` | `bridged` partial | find text/file/symbol, list/content/status |
|
||||
| `mcp` | `bridged` | status, add, OAuth, connect/disconnect |
|
||||
| `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain |
|
||||
| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose |
|
||||
| experimental JSON routes | `bridged` partial | console reads, tool ids, worktree list/mutations, resource list; global session list remains later |
|
||||
| `session` | `later/special` | large stateful surface plus streaming |
|
||||
| `sync` | `later` | process/control side effects |
|
||||
| `event` | `special` | SSE |
|
||||
| `pty` | `special` | websocket |
|
||||
| `tui` | `special` | UI bridge |
|
||||
| Area | Status | Notes |
|
||||
| ------------------------- | ----------------- | -------------------------------------------------------------------------- |
|
||||
| `question` | `bridged` | `GET /question`, reply, reject |
|
||||
| `permission` | `bridged` | list and reply |
|
||||
| `provider` | `bridged` | list, auth, OAuth authorize/callback |
|
||||
| `config` | `bridged` | read, providers, update |
|
||||
| `project` | `bridged` | list, current, git init, update |
|
||||
| `file` | `bridged` partial | find text/file/symbol, list/content/status |
|
||||
| `mcp` | `bridged` | status, add, OAuth, connect/disconnect |
|
||||
| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore |
|
||||
| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose |
|
||||
| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list |
|
||||
| `session` | `bridged` | read, lifecycle, prompt, message/part mutations, revert, permission reply |
|
||||
| `sync` | `bridged` | start/replay/history |
|
||||
| `event` | `special` | SSE |
|
||||
| `pty` | `special` | websocket |
|
||||
| `tui` | `special` | UI bridge |
|
||||
|
||||
## Full Route Checklist
|
||||
|
||||
|
|
@ -259,60 +259,60 @@ This checklist tracks bridge parity only. Checked routes are available through t
|
|||
|
||||
- [x] `GET /experimental/console` - active Console provider metadata.
|
||||
- [x] `GET /experimental/console/orgs` - switchable Console orgs.
|
||||
- [ ] `POST /experimental/console/switch` - switch active Console org.
|
||||
- [x] `POST /experimental/console/switch` - switch active Console org.
|
||||
- [x] `GET /experimental/tool/ids` - tool IDs.
|
||||
- [ ] `GET /experimental/tool` - tools for provider/model.
|
||||
- [x] `GET /experimental/tool` - tools for provider/model.
|
||||
- [x] `GET /experimental/worktree` - list worktrees.
|
||||
- [x] `POST /experimental/worktree` - create worktree.
|
||||
- [x] `DELETE /experimental/worktree` - remove worktree.
|
||||
- [x] `POST /experimental/worktree/reset` - reset worktree.
|
||||
- [ ] `GET /experimental/session` - global session list.
|
||||
- [x] `GET /experimental/session` - global session list.
|
||||
- [x] `GET /experimental/resource` - MCP resources.
|
||||
|
||||
### Workspace Routes
|
||||
|
||||
- [x] `GET /experimental/workspace/adaptor` - list workspace adaptors.
|
||||
- [ ] `POST /experimental/workspace` - create workspace.
|
||||
- [x] `POST /experimental/workspace` - create workspace.
|
||||
- [x] `GET /experimental/workspace` - list workspaces.
|
||||
- [x] `GET /experimental/workspace/status` - workspace status.
|
||||
- [ ] `DELETE /experimental/workspace/:id` - remove workspace.
|
||||
- [ ] `POST /experimental/workspace/:id/session-restore` - restore session into workspace.
|
||||
- [x] `DELETE /experimental/workspace/:id` - remove workspace.
|
||||
- [x] `POST /experimental/workspace/:id/session-restore` - restore session into workspace.
|
||||
|
||||
### Sync Routes
|
||||
|
||||
- [ ] `POST /sync/start` - start workspace sync.
|
||||
- [ ] `POST /sync/replay` - replay sync events.
|
||||
- [ ] `POST /sync/history` - list sync event history.
|
||||
- [x] `POST /sync/start` - start workspace sync.
|
||||
- [x] `POST /sync/replay` - replay sync events.
|
||||
- [x] `POST /sync/history` - list sync event history.
|
||||
|
||||
### Session Routes
|
||||
|
||||
- [ ] `GET /session` - list sessions.
|
||||
- [ ] `GET /session/status` - session status map.
|
||||
- [ ] `GET /session/:sessionID` - get session.
|
||||
- [ ] `GET /session/:sessionID/children` - get child sessions.
|
||||
- [ ] `GET /session/:sessionID/todo` - get session todos.
|
||||
- [ ] `POST /session` - create session.
|
||||
- [ ] `DELETE /session/:sessionID` - delete session.
|
||||
- [ ] `PATCH /session/:sessionID` - update session metadata.
|
||||
- [ ] `POST /session/:sessionID/init` - run project init command.
|
||||
- [ ] `POST /session/:sessionID/fork` - fork session.
|
||||
- [ ] `POST /session/:sessionID/abort` - abort session.
|
||||
- [ ] `POST /session/:sessionID/share` - share session.
|
||||
- [ ] `GET /session/:sessionID/diff` - session diff.
|
||||
- [ ] `DELETE /session/:sessionID/share` - unshare session.
|
||||
- [ ] `POST /session/:sessionID/summarize` - summarize session.
|
||||
- [ ] `GET /session/:sessionID/message` - list session messages.
|
||||
- [ ] `GET /session/:sessionID/message/:messageID` - get message.
|
||||
- [ ] `DELETE /session/:sessionID/message/:messageID` - delete message.
|
||||
- [ ] `DELETE /session/:sessionID/message/:messageID/part/:partID` - delete part.
|
||||
- [ ] `PATCH /session/:sessionID/message/:messageID/part/:partID` - update part.
|
||||
- [ ] `POST /session/:sessionID/message` - prompt with streaming response.
|
||||
- [ ] `POST /session/:sessionID/prompt_async` - async prompt.
|
||||
- [ ] `POST /session/:sessionID/command` - run command.
|
||||
- [ ] `POST /session/:sessionID/shell` - run shell command.
|
||||
- [ ] `POST /session/:sessionID/revert` - revert message.
|
||||
- [ ] `POST /session/:sessionID/unrevert` - restore reverted messages.
|
||||
- [ ] `POST /session/:sessionID/permissions/:permissionID` - deprecated permission response route.
|
||||
- [x] `GET /session` - list sessions.
|
||||
- [x] `GET /session/status` - session status map.
|
||||
- [x] `GET /session/:sessionID` - get session.
|
||||
- [x] `GET /session/:sessionID/children` - get child sessions.
|
||||
- [x] `GET /session/:sessionID/todo` - get session todos.
|
||||
- [x] `POST /session` - create session.
|
||||
- [x] `DELETE /session/:sessionID` - delete session.
|
||||
- [x] `PATCH /session/:sessionID` - update session metadata.
|
||||
- [x] `POST /session/:sessionID/init` - run project init command.
|
||||
- [x] `POST /session/:sessionID/fork` - fork session.
|
||||
- [x] `POST /session/:sessionID/abort` - abort session.
|
||||
- [x] `POST /session/:sessionID/share` - share session.
|
||||
- [x] `GET /session/:sessionID/diff` - session diff.
|
||||
- [x] `DELETE /session/:sessionID/share` - unshare session.
|
||||
- [x] `POST /session/:sessionID/summarize` - summarize session.
|
||||
- [x] `GET /session/:sessionID/message` - list session messages.
|
||||
- [x] `GET /session/:sessionID/message/:messageID` - get message.
|
||||
- [x] `DELETE /session/:sessionID/message/:messageID` - delete message.
|
||||
- [x] `DELETE /session/:sessionID/message/:messageID/part/:partID` - delete part.
|
||||
- [x] `PATCH /session/:sessionID/message/:messageID/part/:partID` - update part.
|
||||
- [x] `POST /session/:sessionID/message` - prompt with streaming response.
|
||||
- [x] `POST /session/:sessionID/prompt_async` - async prompt.
|
||||
- [x] `POST /session/:sessionID/command` - run command.
|
||||
- [x] `POST /session/:sessionID/shell` - run shell command.
|
||||
- [x] `POST /session/:sessionID/revert` - revert message.
|
||||
- [x] `POST /session/:sessionID/unrevert` - restore reverted messages.
|
||||
- [x] `POST /session/:sessionID/permissions/:permissionID` - deprecated permission response route.
|
||||
|
||||
### Event Routes
|
||||
|
||||
|
|
@ -350,13 +350,13 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev
|
|||
1. [x] Bridge `PATCH /project/:projectID`.
|
||||
2. [x] Bridge MCP add/connect/disconnect routes.
|
||||
3. [x] Bridge MCP OAuth routes: start, callback, authenticate, remove.
|
||||
4. [ ] Bridge experimental console switch and tool list routes.
|
||||
5. [ ] Bridge experimental global session list.
|
||||
6. [ ] Bridge workspace create/remove/session-restore routes.
|
||||
7. [ ] Bridge sync start/replay/history routes.
|
||||
8. [ ] Bridge session read routes: list, status, get, children, todo, diff, messages.
|
||||
9. [ ] Bridge session lifecycle mutation routes: create, delete, update, fork, abort.
|
||||
10. [ ] Bridge session share/summary/message/part mutation routes.
|
||||
4. [x] Bridge experimental console switch and tool list routes.
|
||||
5. [x] Bridge experimental global session list.
|
||||
6. [x] Bridge workspace create/remove/session-restore routes.
|
||||
7. [x] Bridge sync start/replay/history routes.
|
||||
8. [x] Bridge session read routes: list, status, get, children, todo, diff, messages.
|
||||
9. [x] Bridge session lifecycle mutation routes: create, delete, update, fork, abort.
|
||||
10. [x] Bridge remaining session mutation and prompt routes.
|
||||
11. [ ] Replace event SSE with non-Hono Effect HTTP.
|
||||
12. [ ] Replace pty websocket/control routes with non-Hono Effect HTTP.
|
||||
13. [ ] Replace tui bridge routes or explicitly isolate them behind a non-Hono compatibility layer.
|
||||
|
|
|
|||
|
|
@ -278,12 +278,16 @@ function resolveEditorLockFile() {
|
|||
}
|
||||
|
||||
const cwd = process.cwd()
|
||||
// longest workspace folder that contains cwd; 0 if none match
|
||||
const bestMatchLength = (lock: EditorLockFile) =>
|
||||
Math.max(0, ...lock.workspaceFolders.map((folder) => pathContainsLength(folder, cwd)))
|
||||
const locks = entries
|
||||
.filter((entry) => entry.endsWith(".lock"))
|
||||
.map((entry) => readEditorLockFile(path.join(directory, entry)))
|
||||
.filter((entry): entry is EditorLockFile => Boolean(entry))
|
||||
.sort((left, right) => scoreEditorLock(right, cwd) - scoreEditorLock(left, cwd))
|
||||
|
||||
.filter((entry) => bestMatchLength(entry) > 0)
|
||||
// prefer locks with longer matching workspace folders, then more recent ones
|
||||
.sort((left, right) => bestMatchLength(right) - bestMatchLength(left) || right.mtimeMs - left.mtimeMs)
|
||||
return locks[0]
|
||||
}
|
||||
|
||||
|
|
@ -310,11 +314,6 @@ function readEditorLockFile(filePath: string): EditorLockFile | undefined {
|
|||
}
|
||||
}
|
||||
|
||||
function scoreEditorLock(lock: EditorLockFile, cwd: string) {
|
||||
const workspaceMatch = lock.workspaceFolders.some((folder) => pathContains(folder, cwd)) ? 1 : 0
|
||||
return workspaceMatch * 1_000_000_000_000 + lock.mtimeMs
|
||||
}
|
||||
|
||||
function editorSelectionKey(selection: EditorSelection | undefined) {
|
||||
if (!selection) return ""
|
||||
return [
|
||||
|
|
@ -327,9 +326,10 @@ function editorSelectionKey(selection: EditorSelection | undefined) {
|
|||
].join("\0")
|
||||
}
|
||||
|
||||
function pathContains(parent: string, child: string) {
|
||||
const relative = path.relative(path.resolve(parent), path.resolve(child))
|
||||
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))
|
||||
function pathContainsLength(parent: string, child: string) {
|
||||
const resolved = path.resolve(parent)
|
||||
const relative = path.relative(resolved, path.resolve(child))
|
||||
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)) ? resolved.length : 0
|
||||
}
|
||||
|
||||
function openEditorSocket(connection: EditorConnection) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { BusEvent } from "@/bus/bus-event"
|
||||
import { SessionID } from "@/session/schema"
|
||||
import { Schema } from "effect"
|
||||
import { Effect, Schema } from "effect"
|
||||
|
||||
const DEFAULT_TOAST_DURATION = 5000
|
||||
|
||||
export const TuiEvent = {
|
||||
PromptAppend: BusEvent.define("tui.prompt.append", Schema.Struct({ text: Schema.String })),
|
||||
|
|
@ -36,7 +38,9 @@ export const TuiEvent = {
|
|||
title: Schema.optional(Schema.String),
|
||||
message: Schema.String,
|
||||
variant: Schema.Literals(["info", "success", "warning", "error"]),
|
||||
duration: Schema.optional(Schema.Number).annotate({ description: "Duration in milliseconds" }),
|
||||
duration: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_TOAST_DURATION))).annotate({
|
||||
description: "Duration in milliseconds",
|
||||
}),
|
||||
}),
|
||||
),
|
||||
SessionSelect: BusEvent.define(
|
||||
|
|
|
|||
|
|
@ -5,10 +5,13 @@ import { useTerminalDimensions } from "@opentui/solid"
|
|||
import { SplitBorder } from "../component/border"
|
||||
import { TextAttributes } from "@opentui/core"
|
||||
import { Schema } from "effect"
|
||||
import { type TuiEvent } from "../event"
|
||||
import { TuiEvent } from "../event"
|
||||
|
||||
type ToastInput = Schema.Codec.Encoded<typeof TuiEvent.ToastShow.properties>
|
||||
export type ToastOptions = Schema.Schema.Type<typeof TuiEvent.ToastShow.properties>
|
||||
|
||||
const decodeToastOptions = Schema.decodeUnknownSync(TuiEvent.ToastShow.properties)
|
||||
|
||||
export function Toast() {
|
||||
const toast = useToast()
|
||||
const { theme } = useTheme()
|
||||
|
|
@ -55,13 +58,13 @@ function init() {
|
|||
let timeoutHandle: NodeJS.Timeout | null = null
|
||||
|
||||
const toast = {
|
||||
show(options: ToastOptions) {
|
||||
const { duration, ...currentToast } = options
|
||||
setStore("currentToast", currentToast)
|
||||
show(options: ToastInput) {
|
||||
const toastOptions = decodeToastOptions(options)
|
||||
setStore("currentToast", toastOptions)
|
||||
if (timeoutHandle) clearTimeout(timeoutHandle)
|
||||
timeoutHandle = setTimeout(() => {
|
||||
setStore("currentToast", null)
|
||||
}, duration).unref()
|
||||
}, toastOptions.duration).unref()
|
||||
},
|
||||
error: (err: any) => {
|
||||
if (err instanceof Error)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
export * as Editor from "./editor"
|
||||
export * as Selection from "./selection"
|
||||
export * as Sound from "./sound"
|
||||
export * as Terminal from "./terminal"
|
||||
export * as Clipboard from "./clipboard"
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
import { RGBA } from "@opentui/core"
|
||||
|
||||
export type Colors = Awaited<ReturnType<typeof colors>>
|
||||
|
||||
function parse(color: string): RGBA | null {
|
||||
if (color.startsWith("rgb:")) {
|
||||
const parts = color.substring(4).split("/")
|
||||
return RGBA.fromInts(parseInt(parts[0], 16) >> 8, parseInt(parts[1], 16) >> 8, parseInt(parts[2], 16) >> 8, 255)
|
||||
}
|
||||
if (color.startsWith("#")) {
|
||||
return RGBA.fromHex(color)
|
||||
}
|
||||
if (color.startsWith("rgb(")) {
|
||||
const parts = color.substring(4, color.length - 1).split(",")
|
||||
return RGBA.fromInts(parseInt(parts[0]), parseInt(parts[1]), parseInt(parts[2]), 255)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Query terminal colors including background, foreground, and palette (0-15).
|
||||
* Uses OSC escape sequences to retrieve actual terminal color values.
|
||||
*
|
||||
* Note: OSC 4 (palette) queries may not work through tmux as responses are filtered.
|
||||
* OSC 10/11 (foreground/background) typically work in most environments.
|
||||
*
|
||||
* Returns an object with background, foreground, and colors array.
|
||||
* Any query that fails will be null/empty.
|
||||
*/
|
||||
export async function colors(): Promise<{
|
||||
background: RGBA | null
|
||||
foreground: RGBA | null
|
||||
colors: RGBA[]
|
||||
}> {
|
||||
if (!process.stdin.isTTY) return { background: null, foreground: null, colors: [] }
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let background: RGBA | null = null
|
||||
let foreground: RGBA | null = null
|
||||
const paletteColors: RGBA[] = []
|
||||
let timeout: NodeJS.Timeout
|
||||
|
||||
const cleanup = () => {
|
||||
process.stdin.setRawMode(false)
|
||||
process.stdin.removeListener("data", handler)
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
const handler = (data: Buffer) => {
|
||||
const str = data.toString()
|
||||
|
||||
// Match OSC 11 (background color)
|
||||
const bgMatch = str.match(/\x1b]11;([^\x07\x1b]+)/)
|
||||
if (bgMatch) {
|
||||
background = parse(bgMatch[1])
|
||||
}
|
||||
|
||||
// Match OSC 10 (foreground color)
|
||||
const fgMatch = str.match(/\x1b]10;([^\x07\x1b]+)/)
|
||||
if (fgMatch) {
|
||||
foreground = parse(fgMatch[1])
|
||||
}
|
||||
|
||||
// Match OSC 4 (palette colors)
|
||||
const paletteMatches = str.matchAll(/\x1b]4;(\d+);([^\x07\x1b]+)/g)
|
||||
for (const match of paletteMatches) {
|
||||
const index = parseInt(match[1])
|
||||
const color = parse(match[2])
|
||||
if (color) paletteColors[index] = color
|
||||
}
|
||||
|
||||
// Return immediately if we have all 16 palette colors
|
||||
if (paletteColors.filter((c) => c !== undefined).length === 16) {
|
||||
cleanup()
|
||||
resolve({ background, foreground, colors: paletteColors })
|
||||
}
|
||||
}
|
||||
|
||||
process.stdin.setRawMode(true)
|
||||
process.stdin.on("data", handler)
|
||||
|
||||
// Query background (OSC 11)
|
||||
process.stdout.write("\x1b]11;?\x07")
|
||||
// Query foreground (OSC 10)
|
||||
process.stdout.write("\x1b]10;?\x07")
|
||||
// Query palette colors 0-15 (OSC 4)
|
||||
for (let i = 0; i < 16; i++) {
|
||||
process.stdout.write(`\x1b]4;${i};?\x07`)
|
||||
}
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
cleanup()
|
||||
resolve({ background, foreground, colors: paletteColors })
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
|
@ -193,7 +193,11 @@ function normalizeMessages(
|
|||
})
|
||||
}
|
||||
|
||||
if (typeof model.capabilities.interleaved === "object" && model.capabilities.interleaved.field) {
|
||||
if (
|
||||
typeof model.capabilities.interleaved === "object" &&
|
||||
model.capabilities.interleaved.field &&
|
||||
model.api.npm !== "@openrouter/ai-sdk-provider"
|
||||
) {
|
||||
const field = model.capabilities.interleaved.field
|
||||
return msgs.map((msg) => {
|
||||
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
import { Account } from "@/account/account"
|
||||
import { AccountID, OrgID } from "@/account/schema"
|
||||
import { Agent } from "@/agent/agent"
|
||||
import { Config } from "@/config"
|
||||
import { InstanceState } from "@/effect"
|
||||
import { MCP } from "@/mcp"
|
||||
import { Project } from "@/project"
|
||||
import { ProviderID, ModelID } from "@/provider/schema"
|
||||
import { Session } from "@/session"
|
||||
import { ToolRegistry } from "@/tool"
|
||||
import * as EffectZod from "@/util/effect-zod"
|
||||
import { Worktree } from "@/worktree"
|
||||
import { Effect, Layer, Option, Schema } from "effect"
|
||||
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"
|
||||
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { Authorization } from "./auth"
|
||||
|
||||
const ConsoleStateResponse = Schema.Struct({
|
||||
|
|
@ -28,16 +34,43 @@ const ConsoleOrgList = Schema.Struct({
|
|||
orgs: Schema.Array(ConsoleOrgOption),
|
||||
}).annotate({ identifier: "ConsoleOrgList" })
|
||||
|
||||
const ConsoleSwitchPayload = Schema.Struct({
|
||||
accountID: AccountID,
|
||||
orgID: OrgID,
|
||||
}).annotate({ identifier: "ConsoleSwitchInput" })
|
||||
|
||||
const ToolIDs = Schema.Array(Schema.String).annotate({ identifier: "ToolIDs" })
|
||||
const ToolListItem = Schema.Struct({
|
||||
id: Schema.String,
|
||||
description: Schema.String,
|
||||
parameters: Schema.Record(Schema.String, Schema.Any),
|
||||
}).annotate({ identifier: "ToolListItem" })
|
||||
const ToolList = Schema.Array(ToolListItem).annotate({ identifier: "ToolList" })
|
||||
const ToolListQuery = Schema.Struct({
|
||||
provider: ProviderID,
|
||||
model: ModelID,
|
||||
})
|
||||
|
||||
const WorktreeList = Schema.Array(Schema.String).annotate({ identifier: "WorktreeList" })
|
||||
const SessionListQuery = Schema.Struct({
|
||||
directory: Schema.optional(Schema.String),
|
||||
roots: Schema.optional(Schema.Literals(["true", "false"])),
|
||||
start: Schema.optional(Schema.NumberFromString),
|
||||
cursor: Schema.optional(Schema.NumberFromString),
|
||||
search: Schema.optional(Schema.String),
|
||||
limit: Schema.optional(Schema.NumberFromString),
|
||||
archived: Schema.optional(Schema.Literals(["true", "false"])),
|
||||
})
|
||||
|
||||
export const ExperimentalPaths = {
|
||||
console: "/experimental/console",
|
||||
consoleOrgs: "/experimental/console/orgs",
|
||||
consoleSwitch: "/experimental/console/switch",
|
||||
tool: "/experimental/tool",
|
||||
toolIDs: "/experimental/tool/ids",
|
||||
worktree: "/experimental/worktree",
|
||||
worktreeReset: "/experimental/worktree/reset",
|
||||
session: "/experimental/session",
|
||||
resource: "/experimental/resource",
|
||||
} as const
|
||||
|
||||
|
|
@ -63,6 +96,27 @@ export const ExperimentalApi = HttpApi.make("experimental")
|
|||
description: "Get the available Console orgs across logged-in accounts, including the current active org.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("consoleSwitch", ExperimentalPaths.consoleSwitch, {
|
||||
payload: ConsoleSwitchPayload,
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "experimental.console.switchOrg",
|
||||
summary: "Switch active Console org",
|
||||
description: "Persist a new active Console account/org selection for the current local OpenCode state.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("tool", ExperimentalPaths.tool, {
|
||||
query: ToolListQuery,
|
||||
success: ToolList,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "tool.list",
|
||||
summary: "List tools",
|
||||
description:
|
||||
"Get a list of available tools with their JSON schema parameters for a specific provider and model combination.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("toolIDs", ExperimentalPaths.toolIDs, {
|
||||
success: ToolIDs,
|
||||
}).annotateMerge(
|
||||
|
|
@ -112,6 +166,17 @@ export const ExperimentalApi = HttpApi.make("experimental")
|
|||
description: "Reset a worktree branch to the primary default branch.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("session", ExperimentalPaths.session, {
|
||||
query: SessionListQuery,
|
||||
success: Schema.Array(Session.GlobalInfo),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "experimental.session.list",
|
||||
summary: "List sessions",
|
||||
description:
|
||||
"Get a list of all OpenCode sessions across projects, sorted by most recently updated. Archived sessions are excluded by default.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("resource", ExperimentalPaths.resource, {
|
||||
success: Schema.Record(Schema.String, MCP.Resource),
|
||||
}).annotateMerge(
|
||||
|
|
@ -141,6 +206,7 @@ export const ExperimentalApi = HttpApi.make("experimental")
|
|||
export const experimentalHandlers = Layer.unwrap(
|
||||
Effect.gen(function* () {
|
||||
const account = yield* Account.Service
|
||||
const agents = yield* Agent.Service
|
||||
const config = yield* Config.Service
|
||||
const mcp = yield* MCP.Service
|
||||
const project = yield* Project.Service
|
||||
|
|
@ -183,6 +249,28 @@ export const experimentalHandlers = Layer.unwrap(
|
|||
}
|
||||
})
|
||||
|
||||
const switchConsole = Effect.fn("ExperimentalHttpApi.consoleSwitch")(function* (ctx: {
|
||||
payload: typeof ConsoleSwitchPayload.Type
|
||||
}) {
|
||||
yield* account
|
||||
.use(ctx.payload.accountID, Option.some(ctx.payload.orgID))
|
||||
.pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
|
||||
return true
|
||||
})
|
||||
|
||||
const tool = Effect.fn("ExperimentalHttpApi.tool")(function* (ctx: { query: typeof ToolListQuery.Type }) {
|
||||
const list = yield* registry.tools({
|
||||
providerID: ctx.query.provider,
|
||||
modelID: ctx.query.model,
|
||||
agent: yield* agents.get(yield* agents.defaultAgent()),
|
||||
})
|
||||
return list.map((item) => ({
|
||||
id: item.id,
|
||||
description: item.description,
|
||||
parameters: EffectZod.toJsonSchema(item.parameters),
|
||||
}))
|
||||
})
|
||||
|
||||
const toolIDs = Effect.fn("ExperimentalHttpApi.toolIDs")(function* () {
|
||||
return yield* registry.ids()
|
||||
})
|
||||
|
|
@ -214,6 +302,28 @@ export const experimentalHandlers = Layer.unwrap(
|
|||
return true
|
||||
})
|
||||
|
||||
const session = Effect.fn("ExperimentalHttpApi.session")(function* (ctx: { query: typeof SessionListQuery.Type }) {
|
||||
const limit = ctx.query.limit ?? 100
|
||||
const sessions = Array.from(
|
||||
Session.listGlobal({
|
||||
directory: ctx.query.directory,
|
||||
roots: ctx.query.roots === "true" ? true : undefined,
|
||||
start: ctx.query.start,
|
||||
cursor: ctx.query.cursor,
|
||||
search: ctx.query.search,
|
||||
limit: limit + 1,
|
||||
archived: ctx.query.archived === "true" ? true : undefined,
|
||||
}),
|
||||
)
|
||||
const list = sessions.length > limit ? sessions.slice(0, limit) : sessions
|
||||
return HttpServerResponse.jsonUnsafe(list, {
|
||||
headers:
|
||||
sessions.length > limit && list.length > 0
|
||||
? { "x-next-cursor": String(list[list.length - 1].time.updated) }
|
||||
: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
const resource = Effect.fn("ExperimentalHttpApi.resource")(function* () {
|
||||
return yield* mcp.resources()
|
||||
})
|
||||
|
|
@ -222,16 +332,20 @@ export const experimentalHandlers = Layer.unwrap(
|
|||
handlers
|
||||
.handle("console", getConsole)
|
||||
.handle("consoleOrgs", listConsoleOrgs)
|
||||
.handle("consoleSwitch", switchConsole)
|
||||
.handle("tool", tool)
|
||||
.handle("toolIDs", toolIDs)
|
||||
.handle("worktree", worktree)
|
||||
.handle("worktreeCreate", worktreeCreate)
|
||||
.handle("worktreeRemove", worktreeRemove)
|
||||
.handle("worktreeReset", worktreeReset)
|
||||
.handle("session", session)
|
||||
.handle("resource", resource),
|
||||
)
|
||||
}),
|
||||
).pipe(
|
||||
Layer.provide(Account.defaultLayer),
|
||||
Layer.provide(Agent.defaultLayer),
|
||||
Layer.provide(Config.defaultLayer),
|
||||
Layer.provide(MCP.defaultLayer),
|
||||
Layer.provide(Project.defaultLayer),
|
||||
|
|
|
|||
|
|
@ -139,9 +139,10 @@ export const mcpHandlers = Layer.unwrap(
|
|||
})
|
||||
|
||||
const add = Effect.fn("McpHttpApi.add")(function* (ctx: { payload: typeof AddPayload.Type }) {
|
||||
const payload = Schema.decodeUnknownSync(AddPayload)(ctx.payload)
|
||||
const result = (yield* mcp.add(payload.name, payload.config)).status
|
||||
return Schema.decodeUnknownSync(StatusMap)("status" in result ? { [payload.name]: result } : result)
|
||||
const result = (yield* mcp.add(ctx.payload.name, ctx.payload.config)).status
|
||||
return yield* Schema.decodeUnknownEffect(StatusMap)(
|
||||
"status" in result ? { [ctx.payload.name]: result } : result,
|
||||
).pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
|
||||
})
|
||||
|
||||
const authStart = Effect.fn("McpHttpApi.authStart")(function* (ctx: { params: { name: string } }) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import { PermissionApi, permissionHandlers } from "./permission"
|
|||
import { ProjectApi, projectHandlers } from "./project"
|
||||
import { ProviderApi, providerHandlers } from "./provider"
|
||||
import { QuestionApi, questionHandlers } from "./question"
|
||||
import { SessionApi, sessionHandlers } from "./session"
|
||||
import { SyncApi, syncHandlers } from "./sync"
|
||||
import { WorkspaceApi, workspaceHandlers } from "./workspace"
|
||||
import { disposeMiddleware } from "./lifecycle"
|
||||
import { memoMap } from "@opencode-ai/core/effect/memo-map"
|
||||
|
|
@ -73,6 +75,8 @@ export const routes = Layer.mergeAll(
|
|||
HttpApiBuilder.layer(QuestionApi).pipe(Layer.provide(questionHandlers)),
|
||||
HttpApiBuilder.layer(PermissionApi).pipe(Layer.provide(permissionHandlers)),
|
||||
HttpApiBuilder.layer(ProviderApi).pipe(Layer.provide(providerHandlers)),
|
||||
HttpApiBuilder.layer(SessionApi).pipe(Layer.provide(sessionHandlers)),
|
||||
HttpApiBuilder.layer(SyncApi).pipe(Layer.provide(syncHandlers)),
|
||||
HttpApiBuilder.layer(WorkspaceApi).pipe(Layer.provide(workspaceHandlers)),
|
||||
).pipe(
|
||||
Layer.provide(authorizationLayer),
|
||||
|
|
|
|||
924
packages/opencode/src/server/routes/instance/httpapi/session.ts
Normal file
924
packages/opencode/src/server/routes/instance/httpapi/session.ts
Normal file
|
|
@ -0,0 +1,924 @@
|
|||
import * as InstanceState from "@/effect/instance-state"
|
||||
import { AppRuntime } from "@/effect/app-runtime"
|
||||
import { Agent } from "@/agent/agent"
|
||||
import { Bus } from "@/bus"
|
||||
import { Command } from "@/command"
|
||||
import { Permission } from "@/permission"
|
||||
import { PermissionID } from "@/permission/schema"
|
||||
import { Instance } from "@/project/instance"
|
||||
import { ModelID, ProviderID } from "@/provider/schema"
|
||||
import { SessionShare } from "@/share"
|
||||
import { Session } from "@/session"
|
||||
import { SessionCompaction } from "@/session/compaction"
|
||||
import { MessageV2 } from "@/session/message-v2"
|
||||
import { SessionPrompt } from "@/session/prompt"
|
||||
import { SessionRevert } from "@/session/revert"
|
||||
import { SessionRunState } from "@/session/run-state"
|
||||
import { SessionStatus } from "@/session/status"
|
||||
import { SessionSummary } from "@/session/summary"
|
||||
import { Todo } from "@/session/todo"
|
||||
import { MessageID, PartID, SessionID } from "@/session/schema"
|
||||
import { Snapshot } from "@/snapshot"
|
||||
import { Log } from "@/util"
|
||||
import { NamedError } from "@opencode-ai/core/util/error"
|
||||
import { Effect, Layer, Schema, Struct } from "effect"
|
||||
import * as Stream from "effect/Stream"
|
||||
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import {
|
||||
HttpApi,
|
||||
HttpApiBuilder,
|
||||
HttpApiEndpoint,
|
||||
HttpApiError,
|
||||
HttpApiGroup,
|
||||
HttpApiSchema,
|
||||
OpenApi,
|
||||
} from "effect/unstable/httpapi"
|
||||
import { Authorization } from "./auth"
|
||||
|
||||
const log = Log.create({ service: "server" })
|
||||
const root = "/session"
|
||||
const ListQuery = Schema.Struct({
|
||||
directory: Schema.optional(Schema.String),
|
||||
roots: Schema.optional(Schema.Literals(["true", "false"])),
|
||||
start: Schema.optional(Schema.NumberFromString),
|
||||
search: Schema.optional(Schema.String),
|
||||
limit: Schema.optional(Schema.NumberFromString),
|
||||
})
|
||||
const DiffQuery = Schema.Struct(Struct.omit(SessionSummary.DiffInput.fields, ["sessionID"]))
|
||||
const MessagesQuery = Schema.Struct({
|
||||
limit: Schema.optional(Schema.NumberFromString.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))),
|
||||
before: Schema.optional(Schema.String),
|
||||
})
|
||||
const StatusMap = Schema.Record(Schema.String, SessionStatus.Info)
|
||||
const UpdatePayload = Schema.Struct({
|
||||
title: Schema.optional(Schema.String),
|
||||
permission: Schema.optional(Permission.Ruleset),
|
||||
time: Schema.optional(
|
||||
Schema.Struct({
|
||||
archived: Schema.optional(Schema.Number),
|
||||
}),
|
||||
),
|
||||
}).annotate({ identifier: "SessionUpdateInput" })
|
||||
const ForkPayload = Schema.Struct(Struct.omit(Session.ForkInput.fields, ["sessionID"])).annotate({
|
||||
identifier: "SessionForkInput",
|
||||
})
|
||||
const InitPayload = Schema.Struct({
|
||||
modelID: ModelID,
|
||||
providerID: ProviderID,
|
||||
messageID: MessageID,
|
||||
}).annotate({ identifier: "SessionInitInput" })
|
||||
const SummarizePayload = Schema.Struct({
|
||||
providerID: ProviderID,
|
||||
modelID: ModelID,
|
||||
auto: Schema.optional(Schema.Boolean),
|
||||
}).annotate({ identifier: "SessionSummarizeInput" })
|
||||
const PromptPayload = Schema.Struct(Struct.omit(SessionPrompt.PromptInput.fields, ["sessionID"])).annotate({
|
||||
identifier: "SessionPromptInput",
|
||||
})
|
||||
const CommandPayload = Schema.Struct(Struct.omit(SessionPrompt.CommandInput.fields, ["sessionID"])).annotate({
|
||||
identifier: "SessionCommandInput",
|
||||
})
|
||||
const ShellPayload = Schema.Struct(Struct.omit(SessionPrompt.ShellInput.fields, ["sessionID"])).annotate({
|
||||
identifier: "SessionShellInput",
|
||||
})
|
||||
const RevertPayload = Schema.Struct(Struct.omit(SessionRevert.RevertInput.fields, ["sessionID"])).annotate({
|
||||
identifier: "SessionRevertInput",
|
||||
})
|
||||
const PermissionResponsePayload = Schema.Struct({
|
||||
response: Permission.Reply,
|
||||
}).annotate({ identifier: "SessionPermissionResponseInput" })
|
||||
|
||||
export const SessionPaths = {
|
||||
list: root,
|
||||
status: `${root}/status`,
|
||||
get: `${root}/:sessionID`,
|
||||
children: `${root}/:sessionID/children`,
|
||||
todo: `${root}/:sessionID/todo`,
|
||||
diff: `${root}/:sessionID/diff`,
|
||||
messages: `${root}/:sessionID/message`,
|
||||
message: `${root}/:sessionID/message/:messageID`,
|
||||
create: root,
|
||||
remove: `${root}/:sessionID`,
|
||||
update: `${root}/:sessionID`,
|
||||
fork: `${root}/:sessionID/fork`,
|
||||
abort: `${root}/:sessionID/abort`,
|
||||
share: `${root}/:sessionID/share`,
|
||||
init: `${root}/:sessionID/init`,
|
||||
summarize: `${root}/:sessionID/summarize`,
|
||||
prompt: `${root}/:sessionID/message`,
|
||||
promptAsync: `${root}/:sessionID/prompt_async`,
|
||||
command: `${root}/:sessionID/command`,
|
||||
shell: `${root}/:sessionID/shell`,
|
||||
revert: `${root}/:sessionID/revert`,
|
||||
unrevert: `${root}/:sessionID/unrevert`,
|
||||
permissions: `${root}/:sessionID/permissions/:permissionID`,
|
||||
deleteMessage: `${root}/:sessionID/message/:messageID`,
|
||||
deletePart: `${root}/:sessionID/message/:messageID/part/:partID`,
|
||||
updatePart: `${root}/:sessionID/message/:messageID/part/:partID`,
|
||||
} as const
|
||||
|
||||
export const SessionApi = HttpApi.make("session")
|
||||
.add(
|
||||
HttpApiGroup.make("session")
|
||||
.add(
|
||||
HttpApiEndpoint.get("list", SessionPaths.list, {
|
||||
query: ListQuery,
|
||||
success: Schema.Array(Session.Info),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.list",
|
||||
summary: "List sessions",
|
||||
description: "Get a list of all OpenCode sessions, sorted by most recently updated.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("status", SessionPaths.status, {
|
||||
success: StatusMap,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.status",
|
||||
summary: "Get session status",
|
||||
description: "Retrieve the current status of all sessions, including active, idle, and completed states.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("get", SessionPaths.get, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.get",
|
||||
summary: "Get session",
|
||||
description: "Retrieve detailed information about a specific OpenCode session.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("children", SessionPaths.children, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Schema.Array(Session.Info),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.children",
|
||||
summary: "Get session children",
|
||||
description: "Retrieve all child sessions that were forked from the specified parent session.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("todo", SessionPaths.todo, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Schema.Array(Todo.Info),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.todo",
|
||||
summary: "Get session todos",
|
||||
description: "Retrieve the todo list associated with a specific session, showing tasks and action items.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("diff", SessionPaths.diff, {
|
||||
params: { sessionID: SessionID },
|
||||
query: DiffQuery,
|
||||
success: Schema.Array(Snapshot.FileDiff),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.diff",
|
||||
summary: "Get message diff",
|
||||
description: "Get the file changes (diff) that resulted from a specific user message in the session.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("messages", SessionPaths.messages, {
|
||||
params: { sessionID: SessionID },
|
||||
query: MessagesQuery,
|
||||
success: Schema.Array(MessageV2.WithParts),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.messages",
|
||||
summary: "Get session messages",
|
||||
description: "Retrieve all messages in a session, including user prompts and AI responses.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("message", SessionPaths.message, {
|
||||
params: { sessionID: SessionID, messageID: MessageID },
|
||||
success: MessageV2.WithParts,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.message",
|
||||
summary: "Get message",
|
||||
description: "Retrieve a specific message from a session by its message ID.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("create", SessionPaths.create, {
|
||||
payload: Session.CreateInput,
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.create",
|
||||
summary: "Create session",
|
||||
description: "Create a new OpenCode session for interacting with AI assistants and managing conversations.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.delete("remove", SessionPaths.remove, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.delete",
|
||||
summary: "Delete session",
|
||||
description: "Delete a session and permanently remove all associated data, including messages and history.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.patch("update", SessionPaths.update, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: UpdatePayload,
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.update",
|
||||
summary: "Update session",
|
||||
description: "Update properties of an existing session, such as title or other metadata.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("fork", SessionPaths.fork, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: ForkPayload,
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.fork",
|
||||
summary: "Fork session",
|
||||
description: "Create a new session by forking an existing session at a specific message point.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("abort", SessionPaths.abort, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.abort",
|
||||
summary: "Abort session",
|
||||
description: "Abort an active session and stop any ongoing AI processing or command execution.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("init", SessionPaths.init, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: InitPayload,
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.init",
|
||||
summary: "Initialize session",
|
||||
description:
|
||||
"Analyze the current application and create an AGENTS.md file with project-specific agent configurations.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("share", SessionPaths.share, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.share",
|
||||
summary: "Share session",
|
||||
description: "Create a shareable link for a session, allowing others to view the conversation.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.delete("unshare", SessionPaths.share, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.unshare",
|
||||
summary: "Unshare session",
|
||||
description: "Remove the shareable link for a session, making it private again.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("summarize", SessionPaths.summarize, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: SummarizePayload,
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.summarize",
|
||||
summary: "Summarize session",
|
||||
description: "Generate a concise summary of the session using AI compaction to preserve key information.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("prompt", SessionPaths.prompt, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: PromptPayload,
|
||||
success: MessageV2.WithParts,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.prompt",
|
||||
summary: "Send message",
|
||||
description: "Create and send a new message to a session, streaming the AI response.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("promptAsync", SessionPaths.promptAsync, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: PromptPayload,
|
||||
success: HttpApiSchema.NoContent,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.prompt_async",
|
||||
summary: "Send async message",
|
||||
description:
|
||||
"Create and send a new message to a session asynchronously, starting the session if needed and returning immediately.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("command", SessionPaths.command, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: CommandPayload,
|
||||
success: MessageV2.WithParts,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.command",
|
||||
summary: "Send command",
|
||||
description: "Send a new command to a session for execution by the AI assistant.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("shell", SessionPaths.shell, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: ShellPayload,
|
||||
success: MessageV2.WithParts,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.shell",
|
||||
summary: "Run shell command",
|
||||
description: "Execute a shell command within the session context and return the AI's response.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("revert", SessionPaths.revert, {
|
||||
params: { sessionID: SessionID },
|
||||
payload: RevertPayload,
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.revert",
|
||||
summary: "Revert message",
|
||||
description:
|
||||
"Revert a specific message in a session, undoing its effects and restoring the previous state.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("unrevert", SessionPaths.unrevert, {
|
||||
params: { sessionID: SessionID },
|
||||
success: Session.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.unrevert",
|
||||
summary: "Restore reverted messages",
|
||||
description: "Restore all previously reverted messages in a session.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("permissionRespond", SessionPaths.permissions, {
|
||||
params: { sessionID: SessionID, permissionID: PermissionID },
|
||||
payload: PermissionResponsePayload,
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "permission.respond",
|
||||
summary: "Respond to permission",
|
||||
description: "Approve or deny a permission request from the AI assistant.",
|
||||
deprecated: true,
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.delete("deleteMessage", SessionPaths.deleteMessage, {
|
||||
params: { sessionID: SessionID, messageID: MessageID },
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.deleteMessage",
|
||||
summary: "Delete message",
|
||||
description:
|
||||
"Permanently delete a specific message and all of its parts from a session without reverting file changes.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.delete("deletePart", SessionPaths.deletePart, {
|
||||
params: { sessionID: SessionID, messageID: MessageID, partID: PartID },
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "part.delete",
|
||||
description: "Delete a part from a message.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.patch("updatePart", SessionPaths.updatePart, {
|
||||
params: { sessionID: SessionID, messageID: MessageID, partID: PartID },
|
||||
payload: MessageV2.Part,
|
||||
success: MessageV2.Part,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "part.update",
|
||||
description: "Update a part in a message.",
|
||||
}),
|
||||
),
|
||||
)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "session",
|
||||
description: "Experimental HttpApi session routes.",
|
||||
}),
|
||||
)
|
||||
.middleware(Authorization),
|
||||
)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "opencode experimental HttpApi",
|
||||
version: "0.0.1",
|
||||
description: "Experimental HttpApi surface for selected instance routes.",
|
||||
}),
|
||||
)
|
||||
|
||||
export const sessionHandlers = Layer.unwrap(
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service
|
||||
const statusSvc = yield* SessionStatus.Service
|
||||
const todoSvc = yield* Todo.Service
|
||||
const summary = yield* SessionSummary.Service
|
||||
|
||||
const list = Effect.fn("SessionHttpApi.list")(function* (ctx: { query: typeof ListQuery.Type }) {
|
||||
const instance = yield* InstanceState.context
|
||||
return Instance.restore(instance, () =>
|
||||
Array.from(
|
||||
Session.list({
|
||||
directory: ctx.query.directory,
|
||||
roots: ctx.query.roots === "true" ? true : undefined,
|
||||
start: ctx.query.start,
|
||||
search: ctx.query.search,
|
||||
limit: ctx.query.limit,
|
||||
}),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const status = Effect.fn("SessionHttpApi.status")(function* () {
|
||||
return Object.fromEntries(yield* statusSvc.list())
|
||||
})
|
||||
|
||||
const get = Effect.fn("SessionHttpApi.get")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
return yield* session.get(ctx.params.sessionID)
|
||||
})
|
||||
|
||||
const children = Effect.fn("SessionHttpApi.children")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
return yield* session.children(ctx.params.sessionID)
|
||||
})
|
||||
|
||||
const todo = Effect.fn("SessionHttpApi.todo")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
return yield* todoSvc.get(ctx.params.sessionID)
|
||||
})
|
||||
|
||||
const diff = Effect.fn("SessionHttpApi.diff")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
query: typeof DiffQuery.Type
|
||||
}) {
|
||||
return yield* summary.diff({ sessionID: ctx.params.sessionID, messageID: ctx.query.messageID })
|
||||
})
|
||||
|
||||
const messages = Effect.fn("SessionHttpApi.messages")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
query: typeof MessagesQuery.Type
|
||||
}) {
|
||||
if (ctx.query.before !== undefined && ctx.query.limit === undefined) return yield* new HttpApiError.BadRequest({})
|
||||
if (ctx.query.before !== undefined) {
|
||||
const before = ctx.query.before
|
||||
yield* Effect.try({
|
||||
try: () => MessageV2.cursor.decode(before),
|
||||
catch: () => new HttpApiError.BadRequest({}),
|
||||
})
|
||||
}
|
||||
if (ctx.query.limit === undefined || ctx.query.limit === 0) {
|
||||
yield* session.get(ctx.params.sessionID)
|
||||
return yield* session.messages({ sessionID: ctx.params.sessionID })
|
||||
}
|
||||
|
||||
const page = MessageV2.page({
|
||||
sessionID: ctx.params.sessionID,
|
||||
limit: ctx.query.limit,
|
||||
before: ctx.query.before,
|
||||
})
|
||||
if (!page.cursor) return page.items
|
||||
|
||||
const request = yield* HttpServerRequest.HttpServerRequest
|
||||
const url = new URL(request.url, "http://localhost")
|
||||
url.searchParams.set("limit", ctx.query.limit.toString())
|
||||
url.searchParams.set("before", page.cursor)
|
||||
return HttpServerResponse.jsonUnsafe(page.items, {
|
||||
headers: {
|
||||
"Access-Control-Expose-Headers": "Link, X-Next-Cursor",
|
||||
Link: `<${url.toString()}>; rel="next"`,
|
||||
"X-Next-Cursor": page.cursor,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const message = Effect.fn("SessionHttpApi.message")(function* (ctx: {
|
||||
params: { sessionID: SessionID; messageID: MessageID }
|
||||
}) {
|
||||
return yield* Effect.sync(() =>
|
||||
MessageV2.get({ sessionID: ctx.params.sessionID, messageID: ctx.params.messageID }),
|
||||
)
|
||||
})
|
||||
|
||||
const create = Effect.fn("SessionHttpApi.create")(function* (ctx: { payload: Session.CreateInput }) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionShare.Service.use((svc) => svc.create(ctx.payload)).pipe(Effect.provide(SessionShare.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const remove = Effect.fn("SessionHttpApi.remove")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Session.Service.use((svc) => svc.remove(ctx.params.sessionID)).pipe(Effect.provide(Session.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
const update = Effect.fn("SessionHttpApi.update")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof UpdatePayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Session.Service.use((svc) =>
|
||||
Effect.gen(function* () {
|
||||
const current = yield* svc.get(ctx.params.sessionID)
|
||||
if (ctx.payload.title !== undefined) {
|
||||
yield* svc.setTitle({ sessionID: ctx.params.sessionID, title: ctx.payload.title })
|
||||
}
|
||||
if (ctx.payload.permission !== undefined) {
|
||||
yield* svc.setPermission({
|
||||
sessionID: ctx.params.sessionID,
|
||||
permission: Permission.merge(current.permission ?? [], ctx.payload.permission),
|
||||
})
|
||||
}
|
||||
if (ctx.payload.time?.archived !== undefined) {
|
||||
yield* svc.setArchived({ sessionID: ctx.params.sessionID, time: ctx.payload.time.archived })
|
||||
}
|
||||
return yield* svc.get(ctx.params.sessionID)
|
||||
}),
|
||||
).pipe(Effect.provide(Session.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const fork = Effect.fn("SessionHttpApi.fork")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof ForkPayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Session.Service.use((svc) =>
|
||||
svc.fork({ sessionID: ctx.params.sessionID, messageID: ctx.payload.messageID }),
|
||||
).pipe(Effect.provide(Session.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const abort = Effect.fn("SessionHttpApi.abort")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionPrompt.Service.use((svc) => svc.cancel(ctx.params.sessionID)).pipe(
|
||||
Effect.provide(SessionPrompt.defaultLayer),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
const init = Effect.fn("SessionHttpApi.init")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof InitPayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionPrompt.Service.use((svc) =>
|
||||
svc.command({
|
||||
sessionID: ctx.params.sessionID,
|
||||
messageID: ctx.payload.messageID,
|
||||
model: `${ctx.payload.providerID}/${ctx.payload.modelID}`,
|
||||
command: Command.Default.INIT,
|
||||
arguments: "",
|
||||
}),
|
||||
).pipe(Effect.provide(SessionPrompt.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
const share = Effect.fn("SessionHttpApi.share")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const share = yield* SessionShare.Service
|
||||
const session = yield* Session.Service
|
||||
yield* share.share(ctx.params.sessionID)
|
||||
return yield* session.get(ctx.params.sessionID)
|
||||
}).pipe(Effect.provide(SessionShare.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const unshare = Effect.fn("SessionHttpApi.unshare")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const share = yield* SessionShare.Service
|
||||
const session = yield* Session.Service
|
||||
yield* share.unshare(ctx.params.sessionID)
|
||||
return yield* session.get(ctx.params.sessionID)
|
||||
}).pipe(Effect.provide(SessionShare.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const summarize = Effect.fn("SessionHttpApi.summarize")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof SummarizePayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service
|
||||
const revert = yield* SessionRevert.Service
|
||||
const compact = yield* SessionCompaction.Service
|
||||
const prompt = yield* SessionPrompt.Service
|
||||
const agent = yield* Agent.Service
|
||||
|
||||
yield* revert.cleanup(yield* session.get(ctx.params.sessionID))
|
||||
const messages = yield* session.messages({ sessionID: ctx.params.sessionID })
|
||||
const defaultAgent = yield* agent.defaultAgent()
|
||||
const currentAgent =
|
||||
messages.findLast((message) => message.info.role === "user")?.info.agent ?? defaultAgent
|
||||
|
||||
yield* compact.create({
|
||||
sessionID: ctx.params.sessionID,
|
||||
agent: currentAgent,
|
||||
model: {
|
||||
providerID: ctx.payload.providerID,
|
||||
modelID: ctx.payload.modelID,
|
||||
},
|
||||
auto: ctx.payload.auto ?? false,
|
||||
})
|
||||
yield* prompt.loop({ sessionID: ctx.params.sessionID })
|
||||
}).pipe(
|
||||
Effect.provide(SessionRevert.defaultLayer),
|
||||
Effect.provide(SessionCompaction.defaultLayer),
|
||||
Effect.provide(SessionPrompt.defaultLayer),
|
||||
Effect.provide(Agent.defaultLayer),
|
||||
Effect.provide(Session.defaultLayer),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
const prompt = Effect.fn("SessionHttpApi.prompt")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof PromptPayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
return HttpServerResponse.stream(
|
||||
Stream.fromEffect(
|
||||
Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionPrompt.Service.use((svc) =>
|
||||
svc.prompt({
|
||||
...ctx.payload,
|
||||
sessionID: ctx.params.sessionID,
|
||||
} as unknown as SessionPrompt.PromptInput),
|
||||
).pipe(Effect.provide(SessionPrompt.defaultLayer)),
|
||||
),
|
||||
),
|
||||
),
|
||||
).pipe(
|
||||
Stream.map((message) => JSON.stringify(message)),
|
||||
Stream.encodeText,
|
||||
),
|
||||
{ contentType: "application/json" },
|
||||
)
|
||||
})
|
||||
|
||||
const promptAsync = Effect.fn("SessionHttpApi.promptAsync")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof PromptPayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.sync(() => {
|
||||
Instance.restore(instance, () => {
|
||||
void AppRuntime.runPromise(
|
||||
SessionPrompt.Service.use((svc) =>
|
||||
svc.prompt({ ...ctx.payload, sessionID: ctx.params.sessionID } as unknown as SessionPrompt.PromptInput),
|
||||
).pipe(Effect.provide(SessionPrompt.defaultLayer)),
|
||||
).catch((error) => {
|
||||
log.error("prompt_async failed", { sessionID: ctx.params.sessionID, error })
|
||||
void Bus.publish(Session.Event.Error, {
|
||||
sessionID: ctx.params.sessionID,
|
||||
error: new NamedError.Unknown({
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
}).toObject(),
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
return HttpApiSchema.NoContent.make()
|
||||
})
|
||||
|
||||
const command = Effect.fn("SessionHttpApi.command")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof CommandPayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionPrompt.Service.use((svc) =>
|
||||
svc.command({ ...ctx.payload, sessionID: ctx.params.sessionID } as SessionPrompt.CommandInput),
|
||||
).pipe(Effect.provide(SessionPrompt.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const shell = Effect.fn("SessionHttpApi.shell")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof ShellPayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionPrompt.Service.use((svc) =>
|
||||
svc.shell({ ...ctx.payload, sessionID: ctx.params.sessionID } as SessionPrompt.ShellInput),
|
||||
).pipe(Effect.provide(SessionPrompt.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const revert = Effect.fn("SessionHttpApi.revert")(function* (ctx: {
|
||||
params: { sessionID: SessionID }
|
||||
payload: typeof RevertPayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
log.info("revert", ctx.payload)
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionRevert.Service.use((svc) => svc.revert({ sessionID: ctx.params.sessionID, ...ctx.payload })).pipe(
|
||||
Effect.provide(SessionRevert.defaultLayer),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const unrevert = Effect.fn("SessionHttpApi.unrevert")(function* (ctx: { params: { sessionID: SessionID } }) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
SessionRevert.Service.use((svc) => svc.unrevert({ sessionID: ctx.params.sessionID })).pipe(
|
||||
Effect.provide(SessionRevert.defaultLayer),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const permissionRespond = Effect.fn("SessionHttpApi.permissionRespond")(function* (ctx: {
|
||||
params: { permissionID: PermissionID }
|
||||
payload: typeof PermissionResponsePayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Permission.Service.use((svc) =>
|
||||
svc.reply({ requestID: ctx.params.permissionID, reply: ctx.payload.response }),
|
||||
).pipe(Effect.provide(Permission.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
const deleteMessage = Effect.fn("SessionHttpApi.deleteMessage")(function* (ctx: {
|
||||
params: { sessionID: SessionID; messageID: MessageID }
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const state = yield* SessionRunState.Service
|
||||
const session = yield* Session.Service
|
||||
yield* state.assertNotBusy(ctx.params.sessionID)
|
||||
yield* session.removeMessage(ctx.params)
|
||||
}).pipe(Effect.provide(SessionRunState.defaultLayer), Effect.provide(Session.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
const deletePart = Effect.fn("SessionHttpApi.deletePart")(function* (ctx: {
|
||||
params: { sessionID: SessionID; messageID: MessageID; partID: PartID }
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Session.Service.use((svc) => svc.removePart(ctx.params)).pipe(Effect.provide(Session.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
const updatePart = Effect.fn("SessionHttpApi.updatePart")(function* (ctx: {
|
||||
params: { sessionID: SessionID; messageID: MessageID; partID: PartID }
|
||||
payload: typeof MessageV2.Part.Type
|
||||
}) {
|
||||
const payload = ctx.payload as MessageV2.Part
|
||||
if (
|
||||
payload.id !== ctx.params.partID ||
|
||||
payload.messageID !== ctx.params.messageID ||
|
||||
payload.sessionID !== ctx.params.sessionID
|
||||
) {
|
||||
throw new Error(
|
||||
`Part mismatch: body.id='${payload.id}' vs partID='${ctx.params.partID}', body.messageID='${payload.messageID}' vs messageID='${ctx.params.messageID}', body.sessionID='${payload.sessionID}' vs sessionID='${ctx.params.sessionID}'`,
|
||||
)
|
||||
}
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
AppRuntime.runPromise(
|
||||
Session.Service.use((svc) => svc.updatePart(payload)).pipe(Effect.provide(Session.defaultLayer)),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
return HttpApiBuilder.group(SessionApi, "session", (handlers) =>
|
||||
handlers
|
||||
.handle("list", list)
|
||||
.handle("status", status)
|
||||
.handle("get", get)
|
||||
.handle("children", children)
|
||||
.handle("todo", todo)
|
||||
.handle("diff", diff)
|
||||
.handle("messages", messages)
|
||||
.handle("message", message)
|
||||
.handle("create", create)
|
||||
.handle("remove", remove)
|
||||
.handle("update", update)
|
||||
.handle("fork", fork)
|
||||
.handle("abort", abort)
|
||||
.handle("init", init)
|
||||
.handle("share", share)
|
||||
.handle("unshare", unshare)
|
||||
.handle("summarize", summarize)
|
||||
.handle("prompt", prompt)
|
||||
.handle("promptAsync", promptAsync)
|
||||
.handle("command", command)
|
||||
.handle("shell", shell)
|
||||
.handle("revert", revert)
|
||||
.handle("unrevert", unrevert)
|
||||
.handle("permissionRespond", permissionRespond)
|
||||
.handle("deleteMessage", deleteMessage)
|
||||
.handle("deletePart", deletePart)
|
||||
.handle("updatePart", updatePart),
|
||||
)
|
||||
}),
|
||||
).pipe(
|
||||
Layer.provide(Session.defaultLayer),
|
||||
Layer.provide(SessionRunState.defaultLayer),
|
||||
Layer.provide(SessionStatus.defaultLayer),
|
||||
Layer.provide(Todo.defaultLayer),
|
||||
Layer.provide(SessionSummary.defaultLayer),
|
||||
)
|
||||
130
packages/opencode/src/server/routes/instance/httpapi/sync.ts
Normal file
130
packages/opencode/src/server/routes/instance/httpapi/sync.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import { startWorkspaceSyncing } from "@/control-plane/workspace"
|
||||
import * as InstanceState from "@/effect/instance-state"
|
||||
import { Database, asc, and, eq, lte, not, or } from "@/storage"
|
||||
import { SyncEvent } from "@/sync"
|
||||
import { EventTable } from "@/sync/event.sql"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { Authorization } from "./auth"
|
||||
|
||||
const root = "/sync"
|
||||
const ReplayEvent = Schema.Struct({
|
||||
id: Schema.String,
|
||||
aggregateID: Schema.String,
|
||||
seq: Schema.Number,
|
||||
type: Schema.String,
|
||||
data: Schema.Record(Schema.String, Schema.Unknown),
|
||||
}).annotate({ identifier: "SyncReplayEvent" })
|
||||
const ReplayPayload = Schema.Struct({
|
||||
directory: Schema.String,
|
||||
events: Schema.NonEmptyArray(ReplayEvent),
|
||||
}).annotate({ identifier: "SyncReplayInput" })
|
||||
const ReplayResponse = Schema.Struct({
|
||||
sessionID: Schema.String,
|
||||
}).annotate({ identifier: "SyncReplayResponse" })
|
||||
const HistoryPayload = Schema.Record(Schema.String, Schema.Number)
|
||||
const HistoryEvent = Schema.Struct({
|
||||
id: Schema.String,
|
||||
aggregate_id: Schema.String,
|
||||
seq: Schema.Number,
|
||||
type: Schema.String,
|
||||
data: Schema.Record(Schema.String, Schema.Unknown),
|
||||
}).annotate({ identifier: "SyncHistoryEvent" })
|
||||
|
||||
export const SyncPaths = {
|
||||
start: `${root}/start`,
|
||||
replay: `${root}/replay`,
|
||||
history: `${root}/history`,
|
||||
} as const
|
||||
|
||||
export const SyncApi = HttpApi.make("sync")
|
||||
.add(
|
||||
HttpApiGroup.make("sync")
|
||||
.add(
|
||||
HttpApiEndpoint.post("start", SyncPaths.start, {
|
||||
success: Schema.Boolean,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "sync.start",
|
||||
summary: "Start workspace sync",
|
||||
description: "Start sync loops for workspaces in the current project that have active sessions.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("replay", SyncPaths.replay, {
|
||||
payload: ReplayPayload,
|
||||
success: ReplayResponse,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "sync.replay",
|
||||
summary: "Replay sync events",
|
||||
description: "Validate and replay a complete sync event history.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("history", SyncPaths.history, {
|
||||
payload: HistoryPayload,
|
||||
success: Schema.Array(HistoryEvent),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "sync.history.list",
|
||||
summary: "List sync events",
|
||||
description:
|
||||
"List sync events for all aggregates. Keys are aggregate IDs the client already knows about, values are the last known sequence ID. Events with seq > value are returned for those aggregates. Aggregates not listed in the input get their full history.",
|
||||
}),
|
||||
),
|
||||
)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "sync",
|
||||
description: "Experimental HttpApi sync routes.",
|
||||
}),
|
||||
)
|
||||
.middleware(Authorization),
|
||||
)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "opencode experimental HttpApi",
|
||||
version: "0.0.1",
|
||||
description: "Experimental HttpApi surface for selected instance routes.",
|
||||
}),
|
||||
)
|
||||
|
||||
export const syncHandlers = Layer.unwrap(
|
||||
Effect.gen(function* () {
|
||||
const start = Effect.fn("SyncHttpApi.start")(function* () {
|
||||
startWorkspaceSyncing((yield* InstanceState.context).project.id)
|
||||
return true
|
||||
})
|
||||
|
||||
const replay = Effect.fn("SyncHttpApi.replay")(function* (ctx: { payload: typeof ReplayPayload.Type }) {
|
||||
const events: SyncEvent.SerializedEvent[] = ctx.payload.events.map((event) => ({
|
||||
id: event.id,
|
||||
aggregateID: event.aggregateID,
|
||||
seq: event.seq,
|
||||
type: event.type,
|
||||
data: { ...event.data },
|
||||
}))
|
||||
SyncEvent.replayAll(events)
|
||||
return { sessionID: events[0].aggregateID }
|
||||
})
|
||||
|
||||
const history = Effect.fn("SyncHttpApi.history")(function* (ctx: { payload: typeof HistoryPayload.Type }) {
|
||||
const exclude = Object.entries(ctx.payload)
|
||||
return Database.use((db) =>
|
||||
db
|
||||
.select()
|
||||
.from(EventTable)
|
||||
.where(
|
||||
exclude.length > 0
|
||||
? not(or(...exclude.map(([id, seq]) => and(eq(EventTable.aggregate_id, id), lte(EventTable.seq, seq))))!)
|
||||
: undefined,
|
||||
)
|
||||
.orderBy(asc(EventTable.seq))
|
||||
.all(),
|
||||
)
|
||||
})
|
||||
|
||||
return HttpApiBuilder.group(SyncApi, "sync", (handlers) =>
|
||||
handlers.handle("start", start).handle("replay", replay).handle("history", history),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
|
@ -2,15 +2,30 @@ import { listAdaptors } from "@/control-plane/adaptors"
|
|||
import { Workspace } from "@/control-plane/workspace"
|
||||
import { WorkspaceAdaptorEntry } from "@/control-plane/types"
|
||||
import * as InstanceState from "@/effect/instance-state"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { Instance } from "@/project/instance"
|
||||
import { Effect, Layer, Schema, Struct } from "effect"
|
||||
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { Authorization } from "./auth"
|
||||
|
||||
const root = "/experimental/workspace"
|
||||
const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"])).annotate({
|
||||
identifier: "WorkspaceCreateInput",
|
||||
})
|
||||
const SessionRestorePayload = Schema.Struct(
|
||||
Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]),
|
||||
).annotate({
|
||||
identifier: "WorkspaceSessionRestoreInput",
|
||||
})
|
||||
const SessionRestoreResponse = Schema.Struct({
|
||||
total: Schema.Number,
|
||||
}).annotate({ identifier: "WorkspaceSessionRestoreResponse" })
|
||||
|
||||
export const WorkspacePaths = {
|
||||
adaptors: `${root}/adaptor`,
|
||||
list: root,
|
||||
status: `${root}/status`,
|
||||
remove: `${root}/:id`,
|
||||
sessionRestore: `${root}/:id/session-restore`,
|
||||
} as const
|
||||
|
||||
export const WorkspaceApi = HttpApi.make("workspace")
|
||||
|
|
@ -35,6 +50,16 @@ export const WorkspaceApi = HttpApi.make("workspace")
|
|||
description: "List all workspaces.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("create", WorkspacePaths.list, {
|
||||
payload: CreatePayload,
|
||||
success: Workspace.Info,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "experimental.workspace.create",
|
||||
summary: "Create workspace",
|
||||
description: "Create a workspace for the current project.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("status", WorkspacePaths.status, {
|
||||
success: Schema.Array(Workspace.ConnectionStatus),
|
||||
}).annotateMerge(
|
||||
|
|
@ -44,6 +69,27 @@ export const WorkspaceApi = HttpApi.make("workspace")
|
|||
description: "Get connection status for workspaces in the current project.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.delete("remove", WorkspacePaths.remove, {
|
||||
params: { id: Workspace.Info.fields.id },
|
||||
success: Schema.UndefinedOr(Workspace.Info),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "experimental.workspace.remove",
|
||||
summary: "Remove workspace",
|
||||
description: "Remove an existing workspace.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("sessionRestore", WorkspacePaths.sessionRestore, {
|
||||
params: { id: Workspace.Info.fields.id },
|
||||
payload: SessionRestorePayload,
|
||||
success: SessionRestoreResponse,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "experimental.workspace.sessionRestore",
|
||||
summary: "Restore session into workspace",
|
||||
description: "Replay a session's sync events into the target workspace in batches.",
|
||||
}),
|
||||
),
|
||||
)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
|
|
@ -72,13 +118,51 @@ export const workspaceHandlers = Layer.unwrap(
|
|||
return Workspace.list((yield* InstanceState.context).project)
|
||||
})
|
||||
|
||||
const create = Effect.fn("WorkspaceHttpApi.create")(function* (ctx: { payload: typeof CreatePayload.Type }) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
Workspace.create({
|
||||
...ctx.payload,
|
||||
projectID: instance.project.id,
|
||||
}),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const status = Effect.fn("WorkspaceHttpApi.status")(function* () {
|
||||
const ids = new Set(Workspace.list((yield* InstanceState.context).project).map((item) => item.id))
|
||||
return Workspace.status().filter((item) => ids.has(item.workspaceID))
|
||||
})
|
||||
|
||||
const remove = Effect.fn("WorkspaceHttpApi.remove")(function* (ctx: { params: { id: Workspace.Info["id"] } }) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() => Instance.restore(instance, () => Workspace.remove(ctx.params.id)))
|
||||
})
|
||||
|
||||
const sessionRestore = Effect.fn("WorkspaceHttpApi.sessionRestore")(function* (ctx: {
|
||||
params: { id: Workspace.Info["id"] }
|
||||
payload: typeof SessionRestorePayload.Type
|
||||
}) {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() =>
|
||||
Instance.restore(instance, () =>
|
||||
Workspace.sessionRestore({
|
||||
workspaceID: ctx.params.id,
|
||||
sessionID: ctx.payload.sessionID,
|
||||
}),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
return HttpApiBuilder.group(WorkspaceApi, "workspace", (handlers) =>
|
||||
handlers.handle("adaptors", adaptors).handle("list", list).handle("status", status),
|
||||
handlers
|
||||
.handle("adaptors", adaptors)
|
||||
.handle("list", list)
|
||||
.handle("create", create)
|
||||
.handle("status", status)
|
||||
.handle("remove", remove)
|
||||
.handle("sessionRestore", sessionRestore),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import { ExperimentalPaths } from "./httpapi/experimental"
|
|||
import { FilePaths } from "./httpapi/file"
|
||||
import { InstancePaths } from "./httpapi/instance"
|
||||
import { McpPaths } from "./httpapi/mcp"
|
||||
import { SessionPaths } from "./httpapi/session"
|
||||
import { SyncPaths } from "./httpapi/sync"
|
||||
import { ProjectRoutes } from "./project"
|
||||
import { SessionRoutes } from "./session"
|
||||
import { PtyRoutes } from "./pty"
|
||||
|
|
@ -49,11 +51,14 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
|
|||
app.get("/config/providers", (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.console, (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.consoleOrgs, (c) => handler(c.req.raw, context))
|
||||
app.post(ExperimentalPaths.consoleSwitch, (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.tool, (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.toolIDs, (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context))
|
||||
app.post(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context))
|
||||
app.delete(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context))
|
||||
app.post(ExperimentalPaths.worktreeReset, (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.session, (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.resource, (c) => handler(c.req.raw, context))
|
||||
app.get("/provider", (c) => handler(c.req.raw, context))
|
||||
app.get("/provider/auth", (c) => handler(c.req.raw, context))
|
||||
|
|
@ -86,6 +91,36 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
|
|||
app.delete(McpPaths.auth, (c) => handler(c.req.raw, context))
|
||||
app.post(McpPaths.connect, (c) => handler(c.req.raw, context))
|
||||
app.post(McpPaths.disconnect, (c) => handler(c.req.raw, context))
|
||||
app.post(SyncPaths.start, (c) => handler(c.req.raw, context))
|
||||
app.post(SyncPaths.replay, (c) => handler(c.req.raw, context))
|
||||
app.post(SyncPaths.history, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.list, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.status, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.get, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.children, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.todo, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.diff, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.messages, (c) => handler(c.req.raw, context))
|
||||
app.get(SessionPaths.message, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.create, (c) => handler(c.req.raw, context))
|
||||
app.delete(SessionPaths.remove, (c) => handler(c.req.raw, context))
|
||||
app.patch(SessionPaths.update, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.init, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.fork, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.abort, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.share, (c) => handler(c.req.raw, context))
|
||||
app.delete(SessionPaths.share, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.summarize, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.prompt, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.promptAsync, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.command, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.shell, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.revert, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.unrevert, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionPaths.permissions, (c) => handler(c.req.raw, context))
|
||||
app.delete(SessionPaths.deleteMessage, (c) => handler(c.req.raw, context))
|
||||
app.delete(SessionPaths.deletePart, (c) => handler(c.req.raw, context))
|
||||
app.patch(SessionPaths.updatePart, (c) => handler(c.req.raw, context))
|
||||
}
|
||||
|
||||
return app
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ function create(opts: { cors?: string[] }) {
|
|||
const context = Context.empty() as Context.Context<unknown>
|
||||
workspaceApp.get(WorkspacePaths.adaptors, (c) => handler(c.req.raw, context))
|
||||
workspaceApp.get(WorkspacePaths.list, (c) => handler(c.req.raw, context))
|
||||
workspaceApp.post(WorkspacePaths.list, (c) => handler(c.req.raw, context))
|
||||
workspaceApp.get(WorkspacePaths.status, (c) => handler(c.req.raw, context))
|
||||
workspaceApp.delete(WorkspacePaths.remove, (c) => handler(c.req.raw, context))
|
||||
workspaceApp.post(WorkspacePaths.sessionRestore, (c) => handler(c.req.raw, context))
|
||||
}
|
||||
workspaceApp.route("/", workspaceLegacyApp)
|
||||
|
||||
|
|
|
|||
|
|
@ -376,6 +376,7 @@ const live: Layer.Layer<
|
|||
"x-opencode-session": input.sessionID,
|
||||
"x-opencode-request": input.user.id,
|
||||
"x-opencode-client": Flag.OPENCODE_CLIENT,
|
||||
"User-Agent": `opencode/${InstallationVersion}`,
|
||||
}
|
||||
: {
|
||||
"x-session-affinity": input.sessionID,
|
||||
|
|
|
|||
|
|
@ -55,6 +55,14 @@ async function defaultModel() {
|
|||
return run((provider) => provider.defaultModel())
|
||||
}
|
||||
|
||||
async function markPluginDependenciesReady(dir: string) {
|
||||
await mkdir(path.join(dir, "node_modules"), { recursive: true })
|
||||
await Bun.write(
|
||||
path.join(dir, "package-lock.json"),
|
||||
JSON.stringify({ packages: { "": { dependencies: { "@opencode-ai/plugin": "0.0.0" } } } }),
|
||||
)
|
||||
}
|
||||
|
||||
function paid(providers: Awaited<ReturnType<typeof list>>) {
|
||||
const item = providers[ProviderID.make("opencode")]
|
||||
expect(item).toBeDefined()
|
||||
|
|
@ -2439,8 +2447,11 @@ test("cloudflare-ai-gateway forwards config metadata options", async () => {
|
|||
test("plugin config providers persist after instance dispose", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const root = path.join(dir, ".opencode", "plugin")
|
||||
const configDir = path.join(dir, ".opencode")
|
||||
const root = path.join(configDir, "plugin")
|
||||
await mkdir(root, { recursive: true })
|
||||
await markPluginDependenciesReady(configDir)
|
||||
await markPluginDependenciesReady(Global.Path.config)
|
||||
await Bun.write(
|
||||
path.join(root, "demo-provider.ts"),
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import type { UpgradeWebSocket } from "hono/ws"
|
||||
import { Effect } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { GlobalBus } from "@/bus/global"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceRoutes } from "../../src/server/routes/instance"
|
||||
import { ExperimentalPaths } from "../../src/server/routes/instance/httpapi/experimental"
|
||||
import { Session } from "../../src/session"
|
||||
import { Database } from "../../src/storage"
|
||||
import { Log } from "../../src/util"
|
||||
import { Worktree } from "../../src/worktree"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
|
|
@ -14,12 +17,21 @@ void Log.init({ print: false })
|
|||
|
||||
const original = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI
|
||||
const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket
|
||||
const testWorktreeMutations = process.platform === "win32" ? test.skip : test
|
||||
|
||||
function app() {
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true
|
||||
return InstanceRoutes(websocket)
|
||||
}
|
||||
|
||||
function runSession<A, E>(fx: Effect.Effect<A, E, Session.Service>) {
|
||||
return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer)))
|
||||
}
|
||||
|
||||
function createSession(input?: Session.CreateInput) {
|
||||
return runSession(Session.Service.use((svc) => svc.create(input)))
|
||||
}
|
||||
|
||||
async function waitReady(directory: string) {
|
||||
return await new Promise<void>((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
|
|
@ -61,9 +73,10 @@ describe("experimental HttpApi", () => {
|
|||
})
|
||||
|
||||
const headers = { "x-opencode-directory": tmp.path }
|
||||
const [consoleState, consoleOrgs, toolIDs, worktrees, resources] = await Promise.all([
|
||||
const [consoleState, consoleOrgs, toolList, toolIDs, worktrees, resources] = await Promise.all([
|
||||
app().request(ExperimentalPaths.console, { headers }),
|
||||
app().request(ExperimentalPaths.consoleOrgs, { headers }),
|
||||
app().request(`${ExperimentalPaths.tool}?provider=opencode&model=gpt-5`, { headers }),
|
||||
app().request(ExperimentalPaths.toolIDs, { headers }),
|
||||
app().request(ExperimentalPaths.worktree, { headers }),
|
||||
app().request(ExperimentalPaths.resource, { headers }),
|
||||
|
|
@ -78,6 +91,15 @@ describe("experimental HttpApi", () => {
|
|||
expect(consoleOrgs.status).toBe(200)
|
||||
expect(await consoleOrgs.json()).toEqual({ orgs: [] })
|
||||
|
||||
expect(toolList.status).toBe(200)
|
||||
expect(await toolList.json()).toContainEqual(
|
||||
expect.objectContaining({
|
||||
id: "bash",
|
||||
description: expect.any(String),
|
||||
parameters: expect.any(Object),
|
||||
}),
|
||||
)
|
||||
|
||||
expect(toolIDs.status).toBe(200)
|
||||
expect(await toolIDs.json()).toContain("bash")
|
||||
|
||||
|
|
@ -88,7 +110,70 @@ describe("experimental HttpApi", () => {
|
|||
expect(await resources.json()).toEqual({})
|
||||
})
|
||||
|
||||
test("serves worktree mutations through Hono bridge", async () => {
|
||||
test("serves Console org switch through Hono bridge", async () => {
|
||||
await using tmp = await tmpdir({ config: { formatter: false, lsp: false } })
|
||||
Database.Client()
|
||||
.$client.prepare(
|
||||
"INSERT INTO account (id, email, url, access_token, refresh_token, time_created, time_updated) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
)
|
||||
.run(
|
||||
"account-test",
|
||||
"test@example.com",
|
||||
"https://console.example.com",
|
||||
"access",
|
||||
"refresh",
|
||||
Date.now(),
|
||||
Date.now(),
|
||||
)
|
||||
|
||||
const switched = await app().request(ExperimentalPaths.consoleSwitch, {
|
||||
method: "POST",
|
||||
headers: { "x-opencode-directory": tmp.path, "content-type": "application/json" },
|
||||
body: JSON.stringify({ accountID: "account-test", orgID: "org-test" }),
|
||||
})
|
||||
|
||||
expect(switched.status).toBe(200)
|
||||
expect(await switched.json()).toBe(true)
|
||||
})
|
||||
|
||||
test("serves global session list through Hono bridge", async () => {
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
||||
|
||||
const first = await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => createSession({ title: "page-one" }),
|
||||
})
|
||||
await new Promise((resolve) => setTimeout(resolve, 5))
|
||||
const second = await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => createSession({ title: "page-two" }),
|
||||
})
|
||||
|
||||
const headers = { "x-opencode-directory": tmp.path }
|
||||
const page = await app().request(
|
||||
`${ExperimentalPaths.session}?${new URLSearchParams({ directory: tmp.path, limit: "1" })}`,
|
||||
{ headers },
|
||||
)
|
||||
expect(page.status).toBe(200)
|
||||
expect(page.headers.get("x-next-cursor")).toBeTruthy()
|
||||
|
||||
const body = (await page.json()) as Session.GlobalInfo[]
|
||||
expect(body.map((session) => session.id)).toEqual([second.id])
|
||||
expect(body[0].project?.id).toBe(second.projectID)
|
||||
|
||||
const next = await app().request(
|
||||
`${ExperimentalPaths.session}?${new URLSearchParams({
|
||||
directory: tmp.path,
|
||||
limit: "10",
|
||||
cursor: body[0].time.updated.toString(),
|
||||
})}`,
|
||||
{ headers },
|
||||
)
|
||||
expect(next.status).toBe(200)
|
||||
expect(((await next.json()) as Session.GlobalInfo[]).map((session) => session.id)).toContain(first.id)
|
||||
})
|
||||
|
||||
testWorktreeMutations("serves worktree mutations through Hono bridge", async () => {
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
||||
|
||||
const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" }
|
||||
|
|
|
|||
280
packages/opencode/test/server/httpapi-session.test.ts
Normal file
280
packages/opencode/test/server/httpapi-session.test.ts
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import type { UpgradeWebSocket } from "hono/ws"
|
||||
import { Effect } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { PermissionID } from "../../src/permission/schema"
|
||||
import { ModelID, ProviderID } from "../../src/provider/schema"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceRoutes } from "../../src/server/routes/instance"
|
||||
import { SessionPaths } from "../../src/server/routes/instance/httpapi/session"
|
||||
import { Session } from "../../src/session"
|
||||
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
||||
import { MessageV2 } from "../../src/session/message-v2"
|
||||
import { Log } from "../../src/util"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
|
||||
void Log.init({ print: false })
|
||||
|
||||
const original = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI
|
||||
const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket
|
||||
|
||||
function app() {
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true
|
||||
return InstanceRoutes(websocket)
|
||||
}
|
||||
|
||||
function runSession<A, E>(fx: Effect.Effect<A, E, Session.Service>) {
|
||||
return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer)))
|
||||
}
|
||||
|
||||
function pathFor(path: string, params: Record<string, string>) {
|
||||
return Object.entries(params).reduce((result, [key, value]) => result.replace(`:${key}`, value), path)
|
||||
}
|
||||
|
||||
async function createSession(directory: string, input?: Session.CreateInput) {
|
||||
return Instance.provide({
|
||||
directory,
|
||||
fn: async () => runSession(Session.Service.use((svc) => svc.create(input))),
|
||||
})
|
||||
}
|
||||
|
||||
async function createTextMessage(directory: string, sessionID: SessionID, text: string) {
|
||||
return Instance.provide({
|
||||
directory,
|
||||
fn: async () =>
|
||||
runSession(
|
||||
Effect.gen(function* () {
|
||||
const svc = yield* Session.Service
|
||||
const info = yield* svc.updateMessage({
|
||||
id: MessageID.ascending(),
|
||||
role: "user",
|
||||
sessionID,
|
||||
agent: "build",
|
||||
model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") },
|
||||
time: { created: Date.now() },
|
||||
})
|
||||
const part = yield* svc.updatePart({
|
||||
id: PartID.ascending(),
|
||||
sessionID,
|
||||
messageID: info.id,
|
||||
type: "text",
|
||||
text,
|
||||
})
|
||||
return { info, part }
|
||||
}),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
async function json<T>(response: Response) {
|
||||
if (response.status !== 200) throw new Error(await response.text())
|
||||
return (await response.json()) as T
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = original
|
||||
await Instance.disposeAll()
|
||||
await resetDatabase()
|
||||
})
|
||||
|
||||
describe("session HttpApi", () => {
|
||||
test("serves read routes through Hono bridge", async () => {
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
||||
const headers = { "x-opencode-directory": tmp.path }
|
||||
const parent = await createSession(tmp.path, { title: "parent" })
|
||||
const child = await createSession(tmp.path, { title: "child", parentID: parent.id })
|
||||
const message = await createTextMessage(tmp.path, parent.id, "hello")
|
||||
await createTextMessage(tmp.path, parent.id, "world")
|
||||
|
||||
expect(
|
||||
(await json<Session.Info[]>(await app().request(`${SessionPaths.list}?roots=true`, { headers }))).map(
|
||||
(item) => item.id,
|
||||
),
|
||||
).toContain(parent.id)
|
||||
|
||||
expect(await json<Record<string, unknown>>(await app().request(SessionPaths.status, { headers }))).toEqual({})
|
||||
|
||||
expect(
|
||||
await json<Session.Info>(await app().request(pathFor(SessionPaths.get, { sessionID: parent.id }), { headers })),
|
||||
).toMatchObject({ id: parent.id, title: "parent" })
|
||||
|
||||
expect(
|
||||
(
|
||||
await json<Session.Info[]>(
|
||||
await app().request(pathFor(SessionPaths.children, { sessionID: parent.id }), { headers }),
|
||||
)
|
||||
).map((item) => item.id),
|
||||
).toEqual([child.id])
|
||||
|
||||
expect(
|
||||
await json<unknown[]>(await app().request(pathFor(SessionPaths.todo, { sessionID: parent.id }), { headers })),
|
||||
).toEqual([])
|
||||
|
||||
expect(
|
||||
await json<unknown[]>(await app().request(pathFor(SessionPaths.diff, { sessionID: parent.id }), { headers })),
|
||||
).toEqual([])
|
||||
|
||||
const messages = await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?limit=1`, {
|
||||
headers,
|
||||
})
|
||||
const messagePage = await json<MessageV2.WithParts[]>(messages)
|
||||
const nextCursor = messages.headers.get("x-next-cursor")
|
||||
expect(nextCursor).toBeTruthy()
|
||||
expect(messagePage[0]?.parts[0]).toMatchObject({ type: "text" })
|
||||
|
||||
expect(
|
||||
(
|
||||
await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?before=${nextCursor}`, {
|
||||
headers,
|
||||
})
|
||||
).status,
|
||||
).toBe(400)
|
||||
expect(
|
||||
(
|
||||
await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?limit=1&before=invalid`, {
|
||||
headers,
|
||||
})
|
||||
).status,
|
||||
).toBe(400)
|
||||
|
||||
expect(
|
||||
await json<MessageV2.WithParts>(
|
||||
await app().request(pathFor(SessionPaths.message, { sessionID: parent.id, messageID: message.info.id }), {
|
||||
headers,
|
||||
}),
|
||||
),
|
||||
).toMatchObject({ info: { id: message.info.id } })
|
||||
})
|
||||
|
||||
test("serves lifecycle mutation routes through Hono bridge", async () => {
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false, share: "disabled" } })
|
||||
const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" }
|
||||
|
||||
const created = await json<Session.Info>(
|
||||
await app().request(SessionPaths.create, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({ title: "created" }),
|
||||
}),
|
||||
)
|
||||
expect(created.title).toBe("created")
|
||||
|
||||
const updated = await json<Session.Info>(
|
||||
await app().request(pathFor(SessionPaths.update, { sessionID: created.id }), {
|
||||
method: "PATCH",
|
||||
headers,
|
||||
body: JSON.stringify({ title: "updated", time: { archived: 1 } }),
|
||||
}),
|
||||
)
|
||||
expect(updated).toMatchObject({ id: created.id, title: "updated", time: { archived: 1 } })
|
||||
|
||||
const forked = await json<Session.Info>(
|
||||
await app().request(pathFor(SessionPaths.fork, { sessionID: created.id }), {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({}),
|
||||
}),
|
||||
)
|
||||
expect(forked.id).not.toBe(created.id)
|
||||
|
||||
expect(
|
||||
await json<boolean>(
|
||||
await app().request(pathFor(SessionPaths.abort, { sessionID: created.id }), { method: "POST", headers }),
|
||||
),
|
||||
).toBe(true)
|
||||
|
||||
expect(
|
||||
await json<boolean>(
|
||||
await app().request(pathFor(SessionPaths.remove, { sessionID: created.id }), { method: "DELETE", headers }),
|
||||
),
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
test("serves message mutation routes through Hono bridge", async () => {
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
||||
const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" }
|
||||
const session = await createSession(tmp.path, { title: "messages" })
|
||||
const first = await createTextMessage(tmp.path, session.id, "first")
|
||||
const second = await createTextMessage(tmp.path, session.id, "second")
|
||||
|
||||
const updated = await json<MessageV2.Part>(
|
||||
await app().request(
|
||||
pathFor(SessionPaths.updatePart, {
|
||||
sessionID: session.id,
|
||||
messageID: first.info.id,
|
||||
partID: first.part.id,
|
||||
}),
|
||||
{
|
||||
method: "PATCH",
|
||||
headers,
|
||||
body: JSON.stringify({ ...first.part, text: "updated" }),
|
||||
},
|
||||
),
|
||||
)
|
||||
expect(updated).toMatchObject({ id: first.part.id, type: "text", text: "updated" })
|
||||
|
||||
expect(
|
||||
await json<boolean>(
|
||||
await app().request(
|
||||
pathFor(SessionPaths.deletePart, {
|
||||
sessionID: session.id,
|
||||
messageID: first.info.id,
|
||||
partID: first.part.id,
|
||||
}),
|
||||
{ method: "DELETE", headers },
|
||||
),
|
||||
),
|
||||
).toBe(true)
|
||||
|
||||
expect(
|
||||
await json<boolean>(
|
||||
await app().request(pathFor(SessionPaths.deleteMessage, { sessionID: session.id, messageID: second.info.id }), {
|
||||
method: "DELETE",
|
||||
headers,
|
||||
}),
|
||||
),
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
test("serves remaining non-LLM session mutation routes through Hono bridge", async () => {
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
||||
const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" }
|
||||
const session = await createSession(tmp.path, { title: "remaining" })
|
||||
|
||||
expect(
|
||||
await json<Session.Info>(
|
||||
await app().request(pathFor(SessionPaths.revert, { sessionID: session.id }), {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({ messageID: MessageID.ascending() }),
|
||||
}),
|
||||
),
|
||||
).toMatchObject({ id: session.id })
|
||||
|
||||
expect(
|
||||
await json<Session.Info>(
|
||||
await app().request(pathFor(SessionPaths.unrevert, { sessionID: session.id }), {
|
||||
method: "POST",
|
||||
headers,
|
||||
}),
|
||||
),
|
||||
).toMatchObject({ id: session.id })
|
||||
|
||||
expect(
|
||||
await json<boolean>(
|
||||
await app().request(
|
||||
pathFor(SessionPaths.permissions, {
|
||||
sessionID: session.id,
|
||||
permissionID: String(PermissionID.ascending()),
|
||||
}),
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({ response: "once" }),
|
||||
},
|
||||
),
|
||||
),
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
84
packages/opencode/test/server/httpapi-sync.test.ts
Normal file
84
packages/opencode/test/server/httpapi-sync.test.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import type { UpgradeWebSocket } from "hono/ws"
|
||||
import { Effect } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceRoutes } from "../../src/server/routes/instance"
|
||||
import { SyncPaths } from "../../src/server/routes/instance/httpapi/sync"
|
||||
import { Session } from "../../src/session"
|
||||
import { Log } from "../../src/util"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
|
||||
void Log.init({ print: false })
|
||||
|
||||
const originalHttpApi = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI
|
||||
const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
||||
const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket
|
||||
|
||||
function app() {
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true
|
||||
return InstanceRoutes(websocket)
|
||||
}
|
||||
|
||||
function runSession<A, E>(fx: Effect.Effect<A, E, Session.Service>) {
|
||||
return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer)))
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = originalHttpApi
|
||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces
|
||||
await Instance.disposeAll()
|
||||
await resetDatabase()
|
||||
})
|
||||
|
||||
describe("sync HttpApi", () => {
|
||||
test("serves sync routes through Hono bridge", async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
|
||||
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
||||
const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" }
|
||||
|
||||
const session = await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => runSession(Session.Service.use((svc) => svc.create({ title: "sync" }))),
|
||||
})
|
||||
|
||||
const started = await app().request(SyncPaths.start, { method: "POST", headers })
|
||||
expect(started.status).toBe(200)
|
||||
expect(await started.json()).toBe(true)
|
||||
|
||||
const history = await app().request(SyncPaths.history, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
expect(history.status).toBe(200)
|
||||
const rows = (await history.json()) as Array<{
|
||||
id: string
|
||||
aggregate_id: string
|
||||
seq: number
|
||||
type: string
|
||||
data: Record<string, unknown>
|
||||
}>
|
||||
expect(rows.map((row) => row.aggregate_id)).toContain(session.id)
|
||||
|
||||
const replayed = await app().request(SyncPaths.replay, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
directory: tmp.path,
|
||||
events: rows
|
||||
.filter((row) => row.aggregate_id === session.id)
|
||||
.map((row) => ({
|
||||
id: row.id,
|
||||
aggregateID: row.aggregate_id,
|
||||
seq: row.seq,
|
||||
type: row.type,
|
||||
data: row.data,
|
||||
})),
|
||||
}),
|
||||
})
|
||||
expect(replayed.status).toBe(200)
|
||||
expect(await replayed.json()).toEqual({ sessionID: session.id })
|
||||
})
|
||||
})
|
||||
|
|
@ -1,7 +1,14 @@
|
|||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import { Context } from "effect"
|
||||
import { mkdir } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import { Context, Effect } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
||||
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server"
|
||||
import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/workspace"
|
||||
import { Session } from "../../src/session"
|
||||
import { Log } from "../../src/util"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
|
|
@ -10,19 +17,50 @@ import { Instance } from "../../src/project/instance"
|
|||
void Log.init({ print: false })
|
||||
|
||||
const context = Context.empty() as Context.Context<unknown>
|
||||
const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
||||
|
||||
function request(path: string, directory: string) {
|
||||
function request(path: string, directory: string, init: RequestInit = {}) {
|
||||
const headers = new Headers(init.headers)
|
||||
headers.set("x-opencode-directory", directory)
|
||||
return ExperimentalHttpApiServer.webHandler().handler(
|
||||
new Request(`http://localhost${path}`, {
|
||||
headers: {
|
||||
"x-opencode-directory": directory,
|
||||
},
|
||||
...init,
|
||||
headers,
|
||||
}),
|
||||
context,
|
||||
)
|
||||
}
|
||||
|
||||
function runSession<A, E>(fx: Effect.Effect<A, E, Session.Service>) {
|
||||
return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer)))
|
||||
}
|
||||
|
||||
function localAdaptor(directory: string): WorkspaceAdaptor {
|
||||
return {
|
||||
name: "Local Test",
|
||||
description: "Create a local test workspace",
|
||||
configure(info) {
|
||||
return {
|
||||
...info,
|
||||
name: "local-test",
|
||||
directory,
|
||||
}
|
||||
},
|
||||
async create() {
|
||||
await mkdir(directory, { recursive: true })
|
||||
},
|
||||
async remove() {},
|
||||
target() {
|
||||
return {
|
||||
type: "local" as const,
|
||||
directory,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces
|
||||
await Instance.disposeAll()
|
||||
await resetDatabase()
|
||||
})
|
||||
|
|
@ -52,4 +90,43 @@ describe("workspace HttpApi", () => {
|
|||
expect(status.status).toBe(200)
|
||||
expect(await status.json()).toEqual([])
|
||||
})
|
||||
|
||||
test("serves mutation endpoints", async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () =>
|
||||
registerAdaptor(Instance.project.id, "local-test", localAdaptor(path.join(tmp.path, ".workspace"))),
|
||||
})
|
||||
|
||||
const created = await request(WorkspacePaths.list, tmp.path, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ type: "local-test", branch: null, extra: null }),
|
||||
})
|
||||
expect(created.status).toBe(200)
|
||||
const workspace = (await created.json()) as Workspace.Info
|
||||
expect(workspace).toMatchObject({ type: "local-test", name: "local-test" })
|
||||
|
||||
const session = await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => runSession(Session.Service.use((svc) => svc.create({}))),
|
||||
})
|
||||
const restored = await request(WorkspacePaths.sessionRestore.replace(":id", workspace.id), tmp.path, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ sessionID: session.id }),
|
||||
})
|
||||
expect(restored.status).toBe(200)
|
||||
expect((await restored.json()) as { total: number }).toMatchObject({ total: expect.any(Number) })
|
||||
|
||||
const removed = await request(WorkspacePaths.remove.replace(":id", workspace.id), tmp.path, { method: "DELETE" })
|
||||
expect(removed.status).toBe(200)
|
||||
expect(await removed.json()).toMatchObject({ id: workspace.id })
|
||||
|
||||
const listed = await request(WorkspacePaths.list, tmp.path)
|
||||
expect(listed.status).toBe(200)
|
||||
expect(await listed.json()).toEqual([])
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -873,6 +873,79 @@ describe("session.message-v2.toModelMessage", () => {
|
|||
])
|
||||
})
|
||||
|
||||
test("preserves OpenRouter reasoning details through provider transform", async () => {
|
||||
const assistantID = "m-assistant"
|
||||
const openrouterModel: Provider.Model = {
|
||||
...model,
|
||||
id: ModelID.make("deepseek/deepseek-v4-pro"),
|
||||
providerID: ProviderID.make("openrouter"),
|
||||
api: {
|
||||
id: "deepseek/deepseek-v4-pro",
|
||||
url: "https://openrouter.ai/api/v1",
|
||||
npm: "@openrouter/ai-sdk-provider",
|
||||
},
|
||||
capabilities: {
|
||||
...model.capabilities,
|
||||
reasoning: true,
|
||||
interleaved: { field: "reasoning_details" },
|
||||
},
|
||||
}
|
||||
const reasoningDetails = [
|
||||
{
|
||||
type: "reasoning.text",
|
||||
text: "thinking",
|
||||
format: "unknown",
|
||||
index: 0,
|
||||
},
|
||||
]
|
||||
const input: MessageV2.WithParts[] = [
|
||||
{
|
||||
info: assistantInfo(assistantID, "m-parent", undefined, {
|
||||
providerID: openrouterModel.providerID,
|
||||
modelID: openrouterModel.id,
|
||||
}),
|
||||
parts: [
|
||||
{
|
||||
...basePart(assistantID, "a1"),
|
||||
type: "reasoning",
|
||||
text: "thinking",
|
||||
time: { start: 0 },
|
||||
metadata: {
|
||||
openrouter: {
|
||||
reasoning_details: reasoningDetails,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
...basePart(assistantID, "a2"),
|
||||
type: "text",
|
||||
text: "answer",
|
||||
},
|
||||
] as MessageV2.Part[],
|
||||
},
|
||||
]
|
||||
|
||||
expect(
|
||||
ProviderTransform.message(await MessageV2.toModelMessages(input, openrouterModel), openrouterModel, {}),
|
||||
).toStrictEqual([
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "reasoning",
|
||||
text: "thinking",
|
||||
providerOptions: {
|
||||
openrouter: {
|
||||
reasoning_details: reasoningDetails,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ type: "text", text: "answer" },
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("splits assistant messages on step-start boundaries", async () => {
|
||||
const assistantID = "m-assistant"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
@ -22,8 +22,8 @@
|
|||
"zod": "catalog:"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentui/core": ">=0.1.103",
|
||||
"@opentui/solid": ">=0.1.103"
|
||||
"@opentui/core": ">=0.1.104",
|
||||
"@opentui/solid": ">=0.1.104"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opentui/core": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ OpenCode Go حاليًا في المرحلة التجريبية.
|
|||
|
||||
يوضح الجدول أدناه عددًا تقديريًا للطلبات بناءً على أنماط استخدام Go المعتادة:
|
||||
|
||||
| Model | الطلبات لكل 5 ساعات | الطلبات في الأسبوع | الطلبات في الشهر |
|
||||
| ----------------- | ------------------- | ------------------ | ---------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | الطلبات لكل 5 ساعات | الطلبات في الأسبوع | الطلبات في الشهر |
|
||||
| ------------------ | ------------------- | ------------------ | ---------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
تستند التقديرات إلى متوسطات أنماط الطلبات المرصودة:
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ OpenCode Go حاليًا في المرحلة التجريبية.
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ OpenCode Go حاليًا في المرحلة التجريبية.
|
|||
|
||||
---
|
||||
|
||||
### النماذج
|
||||
|
||||
يمكنك جلب القائمة الكاملة بالنماذج المتاحة وبياناتها الوصفية من:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## الخصوصية
|
||||
|
||||
صُمّمت هذه الخطة أساسًا للمستخدمين الدوليين، مع استضافة النماذج في الولايات المتحدة والاتحاد الأوروبي وسنغافورة لضمان وصول عالمي مستقر. ويتّبع مزودونا سياسة عدم الاحتفاظ بالبيانات، ولا يستخدمون بياناتك لتدريب النماذج.
|
||||
|
|
|
|||
|
|
@ -144,7 +144,6 @@ https://opencode.ai/zen/v1/models
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -204,15 +203,23 @@ https://opencode.ai/zen/v1/models
|
|||
|
||||
### النماذج المهملة
|
||||
|
||||
| النموذج | تاريخ الإيقاف |
|
||||
| ---------------- | ------------- |
|
||||
| MiniMax M2.1 | 15 مارس 2026 |
|
||||
| GLM 4.7 | 15 مارس 2026 |
|
||||
| GLM 4.6 | 15 مارس 2026 |
|
||||
| Gemini 3 Pro | 9 مارس 2026 |
|
||||
| Kimi K2 Thinking | 6 مارس 2026 |
|
||||
| Kimi K2 | 6 مارس 2026 |
|
||||
| Qwen3 Coder 480B | 6 فبراير 2026 |
|
||||
| النموذج | تاريخ الإيقاف |
|
||||
| ------------------ | -------------- |
|
||||
| GPT 5.2 Codex | 23 يوليو 2026 |
|
||||
| GPT 5.1 Codex | 23 يوليو 2026 |
|
||||
| GPT 5.1 Codex Max | 23 يوليو 2026 |
|
||||
| GPT 5.1 Codex Mini | 23 يوليو 2026 |
|
||||
| GPT 5 Codex | 23 يوليو 2026 |
|
||||
| Claude Sonnet 4 | 15 يونيو 2026 |
|
||||
| GLM 5 | 14 مايو 2026 |
|
||||
| MiniMax M2.1 | 15 مارس 2026 |
|
||||
| GLM 4.7 | 15 مارس 2026 |
|
||||
| GLM 4.6 | 15 مارس 2026 |
|
||||
| Gemini 3 Pro | 9 مارس 2026 |
|
||||
| Kimi K2 Thinking | 6 مارس 2026 |
|
||||
| Kimi K2 | 6 مارس 2026 |
|
||||
| Claude Haiku 3.5 | 16 فبراير 2026 |
|
||||
| Qwen3 Coder 480B | 6 فبراير 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -94,22 +94,22 @@ Ograničenja su definisana u dolarskoj vrijednosti. To znači da vaš stvarni br
|
|||
|
||||
Tabela ispod pruža procijenjeni broj zahtjeva na osnovu tipičnih obrazaca korištenja Go pretplate:
|
||||
|
||||
| Model | zahtjeva na 5 sati | zahtjeva sedmično | zahtjeva mjesečno |
|
||||
| ----------------- | ------------------ | ----------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | zahtjeva na 5 sati | zahtjeva sedmično | zahtjeva mjesečno |
|
||||
| ------------------ | ------------------ | ----------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Procjene se zasnivaju na zapaženim prosječnim obrascima zahtjeva:
|
||||
|
||||
|
|
@ -152,8 +152,8 @@ Također možete pristupiti Go modelima putem sljedećih API endpointa.
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -169,6 +169,16 @@ koristi format `opencode-go/<model-id>`. Na primjer, za Kimi K2.6, koristili bis
|
|||
|
||||
---
|
||||
|
||||
### Modeli
|
||||
|
||||
Pun spisak dostupnih modela i njihovih metapodataka možete preuzeti na:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privatnost
|
||||
|
||||
Plan je prvenstveno namijenjen međunarodnim korisnicima, a modeli su smješteni u US, EU i Singaporeu radi stabilnog globalnog pristupa. Naši pružaoci usluga primjenjuju politiku nultog zadržavanja podataka i ne koriste vaše podatke za treniranje modela.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ Podržavamo pay-as-you-go model. Ispod su cijene **po 1M tokena**.
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -215,15 +214,23 @@ vam ipak može naplatiti više od $20 ako vam stanje padne ispod $5.
|
|||
|
||||
### Zastarjeli modeli
|
||||
|
||||
| Model | Datum zastarijevanja |
|
||||
| ---------------- | -------------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Datum zastarijevanja |
|
||||
| ------------------ | -------------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -94,22 +94,22 @@ Grænserne er defineret i dollarværdi. Det betyder, at dit faktiske antal anmod
|
|||
|
||||
Tabellen nedenfor giver et estimeret antal anmodninger baseret på typiske Go-forbrugsmønstre:
|
||||
|
||||
| Model | anmodninger pr. 5 timer | anmodninger pr. uge | anmodninger pr. måned |
|
||||
| ----------------- | ----------------------- | ------------------- | --------------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | anmodninger pr. 5 timer | anmodninger pr. uge | anmodninger pr. måned |
|
||||
| ------------------ | ----------------------- | ------------------- | --------------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Estimaterne er baseret på observerede gennemsnitlige anmodningsmønstre:
|
||||
|
||||
|
|
@ -152,8 +152,8 @@ Du kan også få adgang til Go-modeller gennem følgende API-endpoints.
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -169,6 +169,16 @@ bruge `opencode-go/kimi-k2.6` i din config.
|
|||
|
||||
---
|
||||
|
||||
### Modeller
|
||||
|
||||
Du kan hente den fulde liste over tilgængelige modeller og deres metadata fra:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privatliv
|
||||
|
||||
Planen er primært designet til internationale brugere med modeller hostet i US, EU og Singapore for stabil global adgang. Vores udbydere følger en zero-retention-policy og bruger ikke dine data til modeltræning.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ Vi understøtter en pay-as-you-go-model. Nedenfor er priserne **pr. 1M tokens**.
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -214,15 +213,23 @@ at opkræve dig mere end $20, hvis din saldo kommer under $5.
|
|||
|
||||
### Udfasede modeller
|
||||
|
||||
| Model | Udfasningsdato |
|
||||
| ---------------- | -------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Udfasningsdato |
|
||||
| ------------------ | -------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -86,22 +86,22 @@ Limits sind in Dollarwerten definiert. Das bedeutet, dass die tatsächliche Anza
|
|||
|
||||
Die folgende Tabelle zeigt eine geschätzte Anzahl von Anfragen basierend auf typischen Go-Nutzungsmustern:
|
||||
|
||||
| Model | Anfragen pro 5 Stunden | Anfragen pro Woche | Anfragen pro Monat |
|
||||
| ----------------- | ---------------------- | ------------------ | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | Anfragen pro 5 Stunden | Anfragen pro Woche | Anfragen pro Monat |
|
||||
| ------------------ | ---------------------- | ------------------ | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Die Schätzungen basieren auf beobachteten durchschnittlichen Anfragemustern:
|
||||
|
||||
|
|
@ -142,8 +142,8 @@ Du kannst auf die Go-Modelle auch über die folgenden API-Endpunkte zugreifen.
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -157,6 +157,16 @@ Die [Modell-ID](/docs/config/#models) in deiner OpenCode Config verwendet das Fo
|
|||
|
||||
---
|
||||
|
||||
### Models
|
||||
|
||||
Du kannst die vollständige Liste der verfügbaren Modelle und ihrer Metadaten hier abrufen:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Datenschutz
|
||||
|
||||
Der Plan ist in erster Linie für internationale Nutzer konzipiert, mit in US, EU und Singapore gehosteten Modellen für einen stabilen weltweiten Zugriff. Unsere Anbieter befolgen eine Zero-Retention-Richtlinie und verwenden Ihre Daten nicht für das Modelltraining.
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ Wir unterstützen ein Pay-as-you-go-Modell. Unten findest du die Preise **pro 1M
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,15 +199,23 @@ Angenommen, du setzt ein monatliches Nutzungslimit von $20, dann wird Zen in ein
|
|||
|
||||
### Veraltete Modelle
|
||||
|
||||
| Model | Deprecation date |
|
||||
| ---------------- | ---------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Deprecation date |
|
||||
| ------------------ | ---------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -94,22 +94,22 @@ Los límites se definen en valor en dólares. Esto significa que tu cantidad rea
|
|||
|
||||
La siguiente tabla proporciona una cantidad estimada de peticiones basada en los patrones típicos de uso de Go:
|
||||
|
||||
| Model | peticiones por 5 horas | peticiones por semana | peticiones por mes |
|
||||
| ----------------- | ---------------------- | --------------------- | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | peticiones por 5 horas | peticiones por semana | peticiones por mes |
|
||||
| ------------------ | ---------------------- | --------------------- | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Las estimaciones se basan en los patrones de peticiones promedio observados:
|
||||
|
||||
|
|
@ -152,8 +152,8 @@ También puedes acceder a los modelos de Go a través de los siguientes endpoint
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -169,6 +169,16 @@ usa el formato `opencode-go/<model-id>`. Por ejemplo, para Kimi K2.6, usarías
|
|||
|
||||
---
|
||||
|
||||
### Modelos
|
||||
|
||||
Puedes obtener la lista completa de modelos disponibles y sus metadatos desde:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privacidad
|
||||
|
||||
El plan está diseñado principalmente para usuarios internacionales, con modelos alojados en US, EU y Singapore para ofrecer un acceso global estable. Nuestros proveedores siguen una política de retención cero y no utilizan tus datos para el entrenamiento de modelos.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ Admitimos un modelo de pago por uso. A continuación se muestran los precios **p
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -214,15 +213,23 @@ cobrándote más de $20 si tu saldo baja de $5.
|
|||
|
||||
### Modelos obsoletos
|
||||
|
||||
| Modelo | Fecha de retirada |
|
||||
| ---------------- | ----------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Modelo | Fecha de retirada |
|
||||
| ------------------ | ----------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ Les limites sont définies en valeur monétaire (dollars). Cela signifie que vot
|
|||
|
||||
Le tableau ci-dessous fournit une estimation du nombre de requêtes basée sur des modèles d'utilisation typiques de Go :
|
||||
|
||||
| Model | requêtes par 5 heures | requêtes par semaine | requêtes par mois |
|
||||
| ----------------- | --------------------- | -------------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | requêtes par 5 heures | requêtes par semaine | requêtes par mois |
|
||||
| ------------------ | --------------------- | -------------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Les estimations sont basées sur les modèles de requêtes moyens observés :
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ Vous pouvez également accéder aux modèles Go via les points de terminaison d'
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ L'[ID de modèle](/docs/config/#models) dans votre configuration OpenCode utilis
|
|||
|
||||
---
|
||||
|
||||
### Modèles
|
||||
|
||||
Vous pouvez récupérer la liste complète des modèles disponibles et leurs métadonnées à partir de :
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Confidentialité
|
||||
|
||||
Cette offre est conçue avant tout pour les utilisateurs internationaux, avec des modèles hébergés aux US, dans l’EU et à Singapore afin d’assurer un accès mondial stable. Nos fournisseurs appliquent une politique de rétention zéro et n’utilisent pas vos données pour l’entraînement des modèles.
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ Nous prenons en charge un modèle de paiement à l'utilisation. Vous trouverez c
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,15 +199,23 @@ Par exemple, si vous définissez une limite d'utilisation mensuelle à $20, Zen
|
|||
|
||||
### Modèles obsolètes
|
||||
|
||||
| Modèle | Date de dépréciation |
|
||||
| ---------------- | -------------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Modèle | Date de dépréciation |
|
||||
| ------------------ | -------------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -94,22 +94,22 @@ Limits are defined in dollar value. This means your actual request count depends
|
|||
|
||||
The table below provides an estimated request count based on typical Go usage patterns:
|
||||
|
||||
| Model | requests per 5 hour | requests per week | requests per month |
|
||||
| ----------------- | ------------------- | ----------------- | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | requests per 5 hour | requests per week | requests per month |
|
||||
| ------------------ | ------------------- | ----------------- | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Estimates are based on observed average request patterns:
|
||||
|
||||
|
|
@ -152,8 +152,8 @@ You can also access Go models through the following API endpoints.
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -169,6 +169,16 @@ use `opencode-go/kimi-k2.6` in your config.
|
|||
|
||||
---
|
||||
|
||||
### Models
|
||||
|
||||
You can fetch the full list of available models and their metadata from:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privacy
|
||||
|
||||
The plan is designed primarily for international users, with models hosted in the US, EU, and Singapore for stable global access. Our providers follow a zero-retention policy and do not use your data for model training.
|
||||
|
|
|
|||
|
|
@ -92,22 +92,22 @@ I limiti sono definiti in valore in dollari. Questo significa che il conteggio e
|
|||
|
||||
La tabella seguente fornisce una stima del conteggio delle richieste in base a pattern di utilizzo tipici di Go:
|
||||
|
||||
| Model | richieste ogni 5 ore | richieste a settimana | richieste al mese |
|
||||
| ----------------- | -------------------- | --------------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | richieste ogni 5 ore | richieste a settimana | richieste al mese |
|
||||
| ------------------ | -------------------- | --------------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Le stime si basano sui pattern medi di richieste osservati:
|
||||
|
||||
|
|
@ -150,8 +150,8 @@ Puoi anche accedere ai modelli Go tramite i seguenti endpoint API.
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -167,6 +167,16 @@ utilizza il formato `opencode-go/<model-id>`. Ad esempio, per Kimi K2.6, userest
|
|||
|
||||
---
|
||||
|
||||
### Modelli
|
||||
|
||||
Puoi recuperare l'elenco completo dei modelli disponibili e i relativi metadati da:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privacy
|
||||
|
||||
Il piano è pensato principalmente per gli utenti internazionali, con modelli ospitati negli US, nell’EU e a Singapore per un accesso globale stabile. I nostri provider seguono una politica di zero-retention e non utilizzano i tuoi dati per l’addestramento dei modelli.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ Supportiamo un modello pay-as-you-go. Qui sotto trovi i prezzi **per 1M token**.
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -214,15 +213,23 @@ per addebitarti più di $20 se il tuo saldo scende sotto $5.
|
|||
|
||||
### Modelli deprecati
|
||||
|
||||
| Modello | Data di deprecazione |
|
||||
| ---------------- | -------------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Modello | Data di deprecazione |
|
||||
| ------------------ | -------------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ OpenCode Goには以下の制限が含まれています:
|
|||
|
||||
以下の表は、一般的なGoの利用パターンに基づいた推定リクエスト数を示しています:
|
||||
|
||||
| Model | 5時間あたりのリクエスト数 | 週間リクエスト数 | 月間リクエスト数 |
|
||||
| ----------------- | ------------------------- | ---------------- | ---------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | 5時間あたりのリクエスト数 | 週間リクエスト数 | 月間リクエスト数 |
|
||||
| ------------------ | ------------------------- | ---------------- | ---------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
推定値は、観測された平均的なリクエストパターンに基づいています:
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ Zen残高にクレジットがある場合は、コンソールで**Use balance*
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ OpenCode設定の[model id](/docs/config/#models)は、`opencode-go/<model-id>`
|
|||
|
||||
---
|
||||
|
||||
### モデル
|
||||
|
||||
利用可能なモデルとそのメタデータの完全な一覧は、次から取得できます。
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## プライバシー
|
||||
|
||||
このプランは主に海外ユーザー向けに設計されており、安定したグローバルアクセスのため、モデルは US、EU、Singapore でホストされています。各プロバイダーはデータを保持しないポリシーに従っており、お客様のデータをモデルのトレーニングに使用することはありません。
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ https://opencode.ai/zen/v1/models
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,15 +199,23 @@ https://opencode.ai/zen/v1/models
|
|||
|
||||
### 非推奨モデル
|
||||
|
||||
| Model | Deprecation date |
|
||||
| ---------------- | ---------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Deprecation date |
|
||||
| ------------------ | ---------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ OpenCode Go에는 다음과 같은 한도가 포함됩니다.
|
|||
|
||||
아래 표는 일반적인 Go 사용 패턴을 기준으로 한 예상 요청 횟수를 보여줍니다.
|
||||
|
||||
| Model | 5시간당 요청 횟수 | 주간 요청 횟수 | 월간 요청 횟수 |
|
||||
| ----------------- | ----------------- | -------------- | -------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | 5시간당 요청 횟수 | 주간 요청 횟수 | 월간 요청 횟수 |
|
||||
| ------------------ | ----------------- | -------------- | -------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
예상치는 관찰된 평균 요청 패턴을 기준으로 합니다.
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ Zen 잔액에 크레딧도 있다면, console에서 **Use balance** 옵션을
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ OpenCode config의 [model id](/docs/config/#models)는 `opencode-go/<model-id>`
|
|||
|
||||
---
|
||||
|
||||
### 모델
|
||||
|
||||
사용 가능한 전체 모델 목록과 메타데이터는 다음에서 가져올 수 있습니다.
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 개인정보 보호
|
||||
|
||||
이 플랜은 안정적인 전 세계 액세스를 위해 모델을 미국, EU, 싱가포르에 호스팅하며, 주로 해외 사용자를 위해 설계되었습니다. 저희 제공자는 zero-retention 정책을 따르며, 고객 데이터를 모델 학습에 사용하지 않습니다.
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ https://opencode.ai/zen/v1/models
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,15 +199,23 @@ https://opencode.ai/zen/v1/models
|
|||
|
||||
### 지원 중단 모델
|
||||
|
||||
| 모델 | 지원 중단 날짜 |
|
||||
| ---------------- | -------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| 모델 | 지원 중단 날짜 |
|
||||
| ------------------ | -------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -94,22 +94,22 @@ Grensene er definert i dollarverdi. Dette betyr at ditt faktiske antall forespø
|
|||
|
||||
Tabellen nedenfor gir et estimert antall forespørsler basert på typiske bruksmønstre for Go:
|
||||
|
||||
| Model | forespørsler per 5 timer | forespørsler per uke | forespørsler per måned |
|
||||
| ----------------- | ------------------------ | -------------------- | ---------------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | forespørsler per 5 timer | forespørsler per uke | forespørsler per måned |
|
||||
| ------------------ | ------------------------ | -------------------- | ---------------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Estimatene er basert på observerte gjennomsnittlige forespørselsmønstre:
|
||||
|
||||
|
|
@ -152,8 +152,8 @@ Du kan også få tilgang til Go-modeller gjennom følgende API-endepunkter.
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -169,6 +169,16 @@ bruke `opencode-go/kimi-k2.6` i konfigurasjonen din.
|
|||
|
||||
---
|
||||
|
||||
### Modeller
|
||||
|
||||
Du kan hente hele listen over tilgjengelige modeller og metadataene deres fra:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Personvern
|
||||
|
||||
Planen er primært utformet for internasjonale brukere, med modeller hostet i US, EU og Singapore for stabil global tilgang. Våre leverandører følger en zero-retention-policy og bruker ikke dataene dine til modelltrening.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ Vi støtter en pay-as-you-go-modell. Nedenfor er prisene **per 1M tokens**.
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -214,15 +213,23 @@ med å belaste deg mer enn $20 hvis saldoen din går under $5.
|
|||
|
||||
### Utfasede modeller
|
||||
|
||||
| Modell | Utfasingsdato |
|
||||
| ---------------- | -------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Modell | Utfasingsdato |
|
||||
| ------------------ | -------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -88,22 +88,22 @@ Limity są zdefiniowane w wartości w dolarach. Oznacza to, że rzeczywista licz
|
|||
|
||||
Poniższa tabela przedstawia szacunkową liczbę żądań na podstawie typowych wzorców korzystania z Go:
|
||||
|
||||
| Model | żądania na 5 godzin | żądania na tydzień | żądania na miesiąc |
|
||||
| ----------------- | ------------------- | ------------------ | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | żądania na 5 godzin | żądania na tydzień | żądania na miesiąc |
|
||||
| ------------------ | ------------------- | ------------------ | ------------------ |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Szacunki opierają się na zaobserwowanych średnich wzorcach żądań:
|
||||
|
||||
|
|
@ -144,8 +144,8 @@ Możesz również uzyskać dostęp do modeli Go za pośrednictwem następującyc
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -161,6 +161,16 @@ używa formatu `opencode-go/<model-id>`. Na przykład dla Kimi K2.6 należy uży
|
|||
|
||||
---
|
||||
|
||||
### Modele
|
||||
|
||||
Pełną listę dostępnych modeli i ich metadane możesz pobrać z:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prywatność
|
||||
|
||||
Plan został zaprojektowany przede wszystkim z myślą o użytkownikach międzynarodowych, a modele są hostowane w US, EU i Singapore, aby zapewnić stabilny dostęp na całym świecie. Nasi dostawcy stosują politykę zerowej retencji i nie wykorzystują Twoich danych do trenowania modeli.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ Obsługujemy model pay-as-you-go. Poniżej znajdują się ceny **za 1M tokenów*
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -213,17 +212,25 @@ ostatecznie obciążyć Cię kwotą wyższą niż $20, jeśli Twoje saldo spadni
|
|||
|
||||
---
|
||||
|
||||
### Deprecated models
|
||||
### Wycofane modele
|
||||
|
||||
| Model | Deprecation date |
|
||||
| ---------------- | ---------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Data wycofania |
|
||||
| ------------------ | -------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -94,22 +94,22 @@ Os limites são definidos em valor em dólares. Isso significa que a sua contage
|
|||
|
||||
A tabela abaixo fornece uma contagem estimada de requisições com base nos padrões típicos de uso do Go:
|
||||
|
||||
| Model | requisições por 5 horas | requisições por semana | requisições por mês |
|
||||
| ----------------- | ----------------------- | ---------------------- | ------------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | requisições por 5 horas | requisições por semana | requisições por mês |
|
||||
| ------------------ | ----------------------- | ---------------------- | ------------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
As estimativas baseiam-se nos padrões médios de requisições observados:
|
||||
|
||||
|
|
@ -152,8 +152,8 @@ Você também pode acessar os modelos do Go através dos seguintes endpoints de
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -169,6 +169,16 @@ usa o formato `opencode-go/<model-id>`. Por exemplo, para o Kimi K2.6, você usa
|
|||
|
||||
---
|
||||
|
||||
### Modelos
|
||||
|
||||
Você pode buscar a lista completa de modelos disponíveis e seus metadados em:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privacidade
|
||||
|
||||
O plano foi projetado principalmente para usuários internacionais, com modelos hospedados em US, EU e Singapore para garantir acesso global estável. Nossos provedores seguem uma política de retenção zero e não usam seus dados para treinamento de modelos.
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ Oferecemos um modelo pay-as-you-go. Abaixo estão os preços **por 1M tokens**.
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,15 +199,23 @@ Por exemplo, digamos que você defina um limite mensal de uso de $20; o Zen não
|
|||
|
||||
### Modelos obsoletos
|
||||
|
||||
| Modelo | Data de descontinuação |
|
||||
| ---------------- | ---------------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Modelo | Data de descontinuação |
|
||||
| ------------------ | ---------------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -94,22 +94,22 @@ OpenCode Go включает следующие лимиты:
|
|||
|
||||
В таблице ниже приведено примерное количество запросов на основе типичных сценариев использования Go:
|
||||
|
||||
| Model | запросов за 5 часов | запросов в неделю | запросов в месяц |
|
||||
| ----------------- | ------------------- | ----------------- | ---------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | запросов за 5 часов | запросов в неделю | запросов в месяц |
|
||||
| ------------------ | ------------------- | ----------------- | ---------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Оценки основаны на наблюдаемых средних показателях запросов:
|
||||
|
||||
|
|
@ -152,8 +152,8 @@ OpenCode Go включает следующие лимиты:
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -169,6 +169,16 @@ OpenCode Go включает следующие лимиты:
|
|||
|
||||
---
|
||||
|
||||
### Модели
|
||||
|
||||
Вы можете получить полный список доступных моделей и их метаданных по адресу:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Конфиденциальность
|
||||
|
||||
Этот план разработан в первую очередь для международных пользователей: модели размещены в US, EU и Singapore, чтобы обеспечить стабильный доступ по всему миру. Наши провайдеры придерживаются политики zero-retention и не используют ваши данные для обучения моделей.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ https://opencode.ai/zen/v1/models
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -214,15 +213,23 @@ https://opencode.ai/zen/v1/models
|
|||
|
||||
### Устаревшие модели
|
||||
|
||||
| Модель | Дата устаревания |
|
||||
| ---------------- | ---------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Модель | Дата устаревания |
|
||||
| ------------------ | ---------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้:
|
|||
|
||||
ตารางด้านล่างแสดงจำนวน request โดยประมาณตามรูปแบบการใช้งานปกติของ Go:
|
||||
|
||||
| Model | requests ต่อ 5 ชั่วโมง | requests ต่อสัปดาห์ | requests ต่อเดือน |
|
||||
| ----------------- | ---------------------- | ------------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | requests ต่อ 5 ชั่วโมง | requests ต่อสัปดาห์ | requests ต่อเดือน |
|
||||
| ------------------ | ---------------------- | ------------------- | ----------------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
การประมาณการอ้างอิงจากรูปแบบการใช้งาน request โดยเฉลี่ยที่สังเกตพบ:
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้:
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้:
|
|||
|
||||
---
|
||||
|
||||
### Models
|
||||
|
||||
คุณสามารถดึงรายการโมเดลทั้งหมดที่พร้อมใช้งานและ metadata ของมันได้จาก:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privacy
|
||||
|
||||
แพลนนี้ออกแบบมาสำหรับผู้ใช้ทั่วโลกเป็นหลัก โดยโฮสต์โมเดลไว้ใน US, EU และ Singapore เพื่อให้เข้าถึงได้อย่างเสถียรจากทั่วโลก ผู้ให้บริการของเราปฏิบัติตามนโยบาย zero-retention และไม่นำข้อมูลของคุณไปใช้ในการฝึกโมเดล
|
||||
|
|
|
|||
|
|
@ -142,7 +142,6 @@ https://opencode.ai/zen/v1/models
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,17 +199,25 @@ https://opencode.ai/zen/v1/models
|
|||
|
||||
---
|
||||
|
||||
### Deprecated models
|
||||
### โมเดลที่ถูกยกเลิก
|
||||
|
||||
| Model | Deprecation date |
|
||||
| ---------------- | ---------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Deprecation date |
|
||||
| ------------------ | ---------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ Limitler dolar değeri üzerinden belirlenmiştir. Bu, gerçek istek sayınızı
|
|||
|
||||
Aşağıdaki tablo, tipik Go kullanım modellerine dayalı tahmini bir istek sayısı sunmaktadır:
|
||||
|
||||
| Model | 5 saatte bir istek | haftalık istek | aylık istek |
|
||||
| ----------------- | ------------------ | -------------- | ----------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | 5 saatte bir istek | haftalık istek | aylık istek |
|
||||
| ------------------ | ------------------ | -------------- | ----------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
Tahminler, gözlemlenen ortalama istek modellerine dayanmaktadır:
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ Go modellerine aşağıdaki API uç noktaları aracılığıyla da erişebilirsi
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ OpenCode yapılandırmanızdaki [model id](/docs/config/#models) formatı `openc
|
|||
|
||||
---
|
||||
|
||||
### Modeller
|
||||
|
||||
Mevcut modellerin tam listesini ve metadata'larını şuradan alabilirsiniz:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gizlilik
|
||||
|
||||
Plan, öncelikle uluslararası kullanıcılar için tasarlanmıştır; dünya genelinde istikrarlı erişim sağlamak için modeller US, EU ve Singapore'da barındırılır. Sağlayıcılarımız sıfır veri saklama politikası uygular ve verilerinizi model eğitimi için kullanmaz.
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ Kullandıkça öde modelini destekliyoruz. Aşağıda **1M token başına** fiya
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,15 +199,23 @@ Ayrıca tüm çalışma alanı için ve ekibinizdeki her üye için aylık kulla
|
|||
|
||||
### Kullanımdan kaldırılan modeller
|
||||
|
||||
| Model | Kullanımdan kaldırılma tarihi |
|
||||
| ---------------- | ----------------------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Kullanımdan kaldırılma tarihi |
|
||||
| ------------------ | ----------------------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**.
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -214,15 +213,23 @@ charging you more than $20 if your balance goes below $5.
|
|||
|
||||
### Deprecated models
|
||||
|
||||
| Model | Deprecation date |
|
||||
| ---------------- | ---------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| Model | Deprecation date |
|
||||
| ------------------ | ---------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ OpenCode Go 包含以下限制:
|
|||
|
||||
下表提供了基于典型 Go 使用模式的预估请求数:
|
||||
|
||||
| Model | 每 5 小时请求数 | 每周请求数 | 每月请求数 |
|
||||
| ----------------- | --------------- | ---------- | ---------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | 每 5 小时请求数 | 每周请求数 | 每月请求数 |
|
||||
| ------------------ | --------------- | ---------- | ---------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
预估值基于观察到的平均请求模式:
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ OpenCode Go 包含以下限制:
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ OpenCode Go 包含以下限制:
|
|||
|
||||
---
|
||||
|
||||
### 模型
|
||||
|
||||
你可以从以下地址获取可用模型及其元数据的完整列表:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 隐私保护
|
||||
|
||||
该方案主要面向国际用户,模型托管在 US、EU 和 Singapore,以提供稳定的全球访问。我们的提供商遵循零保留政策,不会将您的数据用于模型训练。
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ https://opencode.ai/zen/v1/models
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -200,15 +199,23 @@ https://opencode.ai/zen/v1/models
|
|||
|
||||
### 已弃用模型
|
||||
|
||||
| 模型 | 弃用日期 |
|
||||
| ---------------- | -------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| 模型 | 弃用日期 |
|
||||
| ------------------ | -------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -84,22 +84,22 @@ OpenCode Go 包含以下限制:
|
|||
|
||||
下表提供了基於典型 Go 使用模式的預估請求次數:
|
||||
|
||||
| Model | 每 5 小時請求數 | 每週請求數 | 每月請求數 |
|
||||
| ----------------- | --------------- | ---------- | ---------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 | 2,150 | 5,450 | 10,900 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
| Model | 每 5 小時請求數 | 每週請求數 | 每月請求數 |
|
||||
| ------------------ | --------------- | ---------- | ---------- |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5 | 1,150 | 2,880 | 5,750 |
|
||||
| Kimi K2.5 | 1,850 | 4,630 | 9,250 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 |
|
||||
| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 |
|
||||
| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| MiniMax M2.5 | 6,300 | 15,900 | 31,800 |
|
||||
| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 |
|
||||
| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 |
|
||||
| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 |
|
||||
|
||||
預估值是基於觀察到的平均請求模式:
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ OpenCode Go 包含以下限制:
|
|||
| GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
|
@ -155,6 +155,16 @@ OpenCode Go 包含以下限制:
|
|||
|
||||
---
|
||||
|
||||
### 模型
|
||||
|
||||
你可以從以下位置取得所有可用模型及其中繼資料的完整清單:
|
||||
|
||||
```
|
||||
https://opencode.ai/zen/go/v1/models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 隱私權
|
||||
|
||||
此方案主要為國際使用者設計,模型部署於 US、EU 與 Singapore,以提供穩定的全球存取體驗。我們的供應商遵循零保留政策,不會將你的資料用於模型訓練。
|
||||
|
|
|
|||
|
|
@ -145,7 +145,6 @@ https://opencode.ai/zen/v1/models
|
|||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - |
|
||||
|
|
@ -207,15 +206,23 @@ https://opencode.ai/zen/v1/models
|
|||
|
||||
### 已棄用模型
|
||||
|
||||
| 模型 | 棄用日期 |
|
||||
| ---------------- | -------------- |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
| 模型 | 棄用日期 |
|
||||
| ------------------ | -------------- |
|
||||
| GPT 5.2 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex | July 23, 2026 |
|
||||
| GPT 5.1 Codex Max | July 23, 2026 |
|
||||
| GPT 5.1 Codex Mini | July 23, 2026 |
|
||||
| GPT 5 Codex | July 23, 2026 |
|
||||
| Claude Sonnet 4 | June 15, 2026 |
|
||||
| GLM 5 | May 14, 2026 |
|
||||
| MiniMax M2.1 | March 15, 2026 |
|
||||
| GLM 4.7 | March 15, 2026 |
|
||||
| GLM 4.6 | March 15, 2026 |
|
||||
| Gemini 3 Pro | March 9, 2026 |
|
||||
| Kimi K2 Thinking | March 6, 2026 |
|
||||
| Kimi K2 | March 6, 2026 |
|
||||
| Claude Haiku 3.5 | Feb 16, 2026 |
|
||||
| Qwen3 Coder 480B | Feb 6, 2026 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.14.25",
|
||||
"version": "1.14.26",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue