mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-05 16:41:01 -04:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8aa7e65dc8 | |||
| 58ba486375 | |||
| 121016af81 | |||
| f40bdd1ac3 | |||
| efc9303b27 | |||
| b938e1ea99 | |||
| 9cfdbbb1af | |||
| 29ea9fcf25 | |||
| 870c38a6aa | |||
| b937fe9450 | |||
| 427ef95f7d | |||
| d8fe12aafc | |||
| a7d7f5bb07 | |||
| 8cdb82038a | |||
| a9e757b5bb | |||
| 4d2696e027 | |||
| 0c8de47f7d | |||
| 7ad165fbdc | |||
| e5b33f8a5e | |||
| 90a7e3d64e | |||
| 33dc70b754 | |||
| 832bbba616 | |||
| 40d5d14304 | |||
| 36df0d823a | |||
| 9424f829eb | |||
| 7b561be159 | |||
| c60464de07 |
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -107,7 +107,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -134,7 +134,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -158,7 +158,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -182,7 +182,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -212,7 +212,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
@@ -241,7 +241,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -257,7 +257,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -361,7 +361,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -381,7 +381,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.90.10",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -392,7 +392,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -405,7 +405,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -447,7 +447,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -458,7 +458,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -196,7 +196,6 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
||||
: language.t("palette.search.placeholder"),
|
||||
autofocus: true,
|
||||
hideIcon: true,
|
||||
class: "pl-3 pr-2 !mb-0",
|
||||
}}
|
||||
emptyMessage={language.t("palette.empty")}
|
||||
loadingMessage={language.t("common.loading")}
|
||||
@@ -224,7 +223,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div class="w-full flex items-center justify-between gap-4 pl-1">
|
||||
<div class="w-full flex items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<span class="text-14-regular text-text-strong whitespace-nowrap">{item.title}</span>
|
||||
<Show when={item.description}>
|
||||
|
||||
@@ -187,7 +187,7 @@ export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
|
||||
<Kobalte.Portal>
|
||||
<Kobalte.Content
|
||||
ref={(el) => setStore("content", el)}
|
||||
class="w-72 h-80 flex flex-col rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden"
|
||||
class="w-72 h-80 flex flex-col p-2 rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden"
|
||||
onEscapeKeyDown={(event) => {
|
||||
setStore("dismiss", "escape")
|
||||
setStore("open", false)
|
||||
|
||||
@@ -130,7 +130,7 @@ export const SettingsGeneral: Component = () => {
|
||||
const soundOptions = [...SOUND_OPTIONS]
|
||||
|
||||
return (
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
||||
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
|
||||
<div class="flex flex-col gap-1 pt-6 pb-8">
|
||||
<h2 class="text-16-medium text-text-strong">{language.t("settings.tab.general")}</h2>
|
||||
@@ -406,8 +406,8 @@ interface SettingsRowProps {
|
||||
|
||||
const SettingsRow: Component<SettingsRowProps> = (props) => {
|
||||
return (
|
||||
<div class="flex items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-col gap-0.5 min-w-0">
|
||||
<span class="text-14-medium text-text-strong">{props.title}</span>
|
||||
<span class="text-12-regular text-text-weak">{props.description}</span>
|
||||
</div>
|
||||
|
||||
@@ -352,7 +352,7 @@ export const SettingsKeybinds: Component = () => {
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
||||
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
|
||||
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
|
||||
@@ -39,7 +39,7 @@ export const SettingsModels: Component = () => {
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
||||
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
|
||||
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
|
||||
<h2 class="text-16-medium text-text-strong">{language.t("settings.models.title")}</h2>
|
||||
@@ -99,7 +99,7 @@ export const SettingsModels: Component = () => {
|
||||
{(item) => {
|
||||
const key = { providerID: item.provider.id, modelID: item.id }
|
||||
return (
|
||||
<div class="flex items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="min-w-0">
|
||||
<span class="text-14-regular text-text-strong truncate block">{item.name}</span>
|
||||
</div>
|
||||
|
||||
@@ -176,13 +176,13 @@ export const SettingsPermissions: Component = () => {
|
||||
return (
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar">
|
||||
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
|
||||
<div class="flex flex-col gap-1 p-8 max-w-[720px]">
|
||||
<div class="flex flex-col gap-1 px-4 py-8 sm:p-8 max-w-[720px]">
|
||||
<h2 class="text-16-medium text-text-strong">{language.t("settings.permissions.title")}</h2>
|
||||
<p class="text-14-regular text-text-weak">{language.t("settings.permissions.description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-6 p-8 pt-6 max-w-[720px]">
|
||||
<div class="flex flex-col gap-6 px-4 py-6 sm:p-8 sm:pt-6 max-w-[720px]">
|
||||
<div class="flex flex-col gap-2">
|
||||
<h3 class="text-14-medium text-text-strong">{language.t("settings.permissions.section.tools")}</h3>
|
||||
<div class="border border-border-weak-base rounded-lg overflow-hidden">
|
||||
@@ -217,8 +217,8 @@ interface SettingsRowProps {
|
||||
|
||||
const SettingsRow: Component<SettingsRowProps> = (props) => {
|
||||
return (
|
||||
<div class="flex items-center justify-between gap-4 px-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4 px-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-col gap-0.5 min-w-0">
|
||||
<span class="text-14-medium text-text-strong">{props.title}</span>
|
||||
<span class="text-12-regular text-text-weak">{props.description}</span>
|
||||
</div>
|
||||
|
||||
@@ -115,7 +115,7 @@ export const SettingsProviders: Component = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-10 pb-10">
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
||||
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
|
||||
<div class="flex flex-col gap-1 pt-6 pb-8 max-w-[720px]">
|
||||
<h2 class="text-16-medium text-text-strong">{language.t("settings.providers.title")}</h2>
|
||||
@@ -136,7 +136,7 @@ export const SettingsProviders: Component = () => {
|
||||
>
|
||||
<For each={connected()}>
|
||||
{(item) => (
|
||||
<div class="group flex items-center justify-between gap-4 h-16 border-b border-border-weak-base last:border-none">
|
||||
<div class="group flex flex-wrap items-center justify-between gap-4 min-h-16 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
<ProviderIcon id={icon(item.id)} class="size-5 shrink-0 icon-strong-base" />
|
||||
<span class="text-14-medium text-text-strong truncate">{item.name}</span>
|
||||
@@ -166,7 +166,7 @@ export const SettingsProviders: Component = () => {
|
||||
<div class="bg-surface-raised-base px-4 rounded-lg">
|
||||
<For each={popular()}>
|
||||
{(item) => (
|
||||
<div class="flex items-center justify-between gap-4 h-16 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4 min-h-16 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-col min-w-0">
|
||||
<div class="flex items-center gap-x-3">
|
||||
<ProviderIcon id={icon(item.id)} class="size-5 shrink-0 icon-strong-base" />
|
||||
|
||||
+31
-28
@@ -37,12 +37,12 @@ export const dict = {
|
||||
"command.palette": "命令面板",
|
||||
|
||||
"command.theme.cycle": "切换主题",
|
||||
"command.theme.set": "使用主题: {{theme}}",
|
||||
"command.theme.set": "使用主题:{{theme}}",
|
||||
"command.theme.scheme.cycle": "切换配色方案",
|
||||
"command.theme.scheme.set": "使用配色方案: {{scheme}}",
|
||||
"command.theme.scheme.set": "使用配色方案:{{scheme}}",
|
||||
|
||||
"command.language.cycle": "切换语言",
|
||||
"command.language.set": "使用语言: {{language}}",
|
||||
"command.language.set": "使用语言:{{language}}",
|
||||
|
||||
"command.session.new": "新建会话",
|
||||
"command.file.open": "打开文件",
|
||||
@@ -98,6 +98,10 @@ export const dict = {
|
||||
"dialog.provider.anthropic.note": "使用 Claude Pro/Max 或 API 密钥连接",
|
||||
"dialog.provider.openai.note": "使用 ChatGPT Pro/Plus 或 API 密钥连接",
|
||||
"dialog.provider.copilot.note": "使用 Copilot 或 API 密钥连接",
|
||||
"dialog.provider.opencode.note": "使用 OpenCode Zen 或 API 密钥连接",
|
||||
"dialog.provider.google.note": "使用 Google 账号或 API 密钥连接",
|
||||
"dialog.provider.openrouter.note": "使用 OpenRouter 账号或 API 密钥连接",
|
||||
"dialog.provider.vercel.note": "使用 Vercel 账号或 API 密钥连接",
|
||||
|
||||
"dialog.model.select.title": "选择模型",
|
||||
"dialog.model.search.placeholder": "搜索模型",
|
||||
@@ -116,7 +120,7 @@ export const dict = {
|
||||
"provider.connect.method.apiKey": "API 密钥",
|
||||
"provider.connect.status.inProgress": "正在授权...",
|
||||
"provider.connect.status.waiting": "等待授权...",
|
||||
"provider.connect.status.failed": "授权失败: {{error}}",
|
||||
"provider.connect.status.failed": "授权失败:{{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"输入你的 {{provider}} API 密钥以连接帐户,并在 OpenCode 中使用 {{provider}} 模型。",
|
||||
"provider.connect.apiKey.label": "{{provider}} API 密钥",
|
||||
@@ -156,7 +160,7 @@ export const dict = {
|
||||
"model.input.audio": "音频",
|
||||
"model.input.video": "视频",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "支持: {{inputs}}",
|
||||
"model.tooltip.allows": "支持:{{inputs}}",
|
||||
"model.tooltip.reasoning.allowed": "支持推理",
|
||||
"model.tooltip.reasoning.none": "不支持推理",
|
||||
"model.tooltip.context": "上下文上限 {{limit}}",
|
||||
@@ -181,30 +185,30 @@ export const dict = {
|
||||
"prompt.mode.shell.exit": "按 esc 退出",
|
||||
|
||||
"prompt.example.1": "修复代码库中的一个 TODO",
|
||||
"prompt.example.2": "这个项目的技术栈是什么?",
|
||||
"prompt.example.2": "这个项目的技术栈是什么?",
|
||||
"prompt.example.3": "修复失败的测试",
|
||||
"prompt.example.4": "解释认证是如何工作的",
|
||||
"prompt.example.5": "查找并修复安全漏洞",
|
||||
"prompt.example.6": "为用户服务添加单元测试",
|
||||
"prompt.example.7": "重构这个函数,让它更易读",
|
||||
"prompt.example.8": "这个错误是什么意思?",
|
||||
"prompt.example.8": "这个错误是什么意思?",
|
||||
"prompt.example.9": "帮我调试这个问题",
|
||||
"prompt.example.10": "生成 API 文档",
|
||||
"prompt.example.11": "优化数据库查询",
|
||||
"prompt.example.12": "添加输入校验",
|
||||
"prompt.example.13": "创建一个新的组件用于...",
|
||||
"prompt.example.14": "我该如何部署这个项目?",
|
||||
"prompt.example.14": "我该如何部署这个项目?",
|
||||
"prompt.example.15": "审查我的代码并给出最佳实践建议",
|
||||
"prompt.example.16": "为这个函数添加错误处理",
|
||||
"prompt.example.17": "解释这个正则表达式",
|
||||
"prompt.example.18": "把它转换成 TypeScript",
|
||||
"prompt.example.19": "在整个代码库中添加日志",
|
||||
"prompt.example.20": "哪些依赖已经过期?",
|
||||
"prompt.example.20": "哪些依赖已经过期?",
|
||||
"prompt.example.21": "帮我写一个迁移脚本",
|
||||
"prompt.example.22": "为这个接口实现缓存",
|
||||
"prompt.example.23": "给这个列表添加分页",
|
||||
"prompt.example.24": "创建一个 CLI 命令用于...",
|
||||
"prompt.example.25": "这里的环境变量是怎么工作的?",
|
||||
"prompt.example.25": "这里的环境变量是怎么工作的?",
|
||||
|
||||
"prompt.popover.emptyResults": "没有匹配的结果",
|
||||
"prompt.popover.emptyCommands": "没有匹配的命令",
|
||||
@@ -378,31 +382,31 @@ export const dict = {
|
||||
"error.page.action.updateTo": "更新到 {{version}}",
|
||||
"error.page.report.prefix": "请将此错误报告给 OpenCode 团队",
|
||||
"error.page.report.discord": "在 Discord 上",
|
||||
"error.page.version": "版本: {{version}}",
|
||||
"error.page.version": "版本:{{version}}",
|
||||
|
||||
"error.dev.rootNotFound": "未找到根元素。你是不是忘了把它添加到 index.html? 或者 id 属性拼写错了?",
|
||||
"error.dev.rootNotFound": "未找到根元素。你是不是忘了把它添加到 index.html?或者 id 属性拼写错了?",
|
||||
|
||||
"error.globalSync.connectFailed": "无法连接到服务器。是否有服务器正在 `{{url}}` 运行?",
|
||||
"error.globalSync.connectFailed": "无法连接到服务器。是否有服务器正在 `{{url}}` 运行?",
|
||||
|
||||
"error.chain.unknown": "未知错误",
|
||||
"error.chain.causedBy": "原因:",
|
||||
"error.chain.causedBy": "原因:",
|
||||
"error.chain.apiError": "API 错误",
|
||||
"error.chain.status": "状态: {{status}}",
|
||||
"error.chain.retryable": "可重试: {{retryable}}",
|
||||
"error.chain.responseBody": "响应内容:\n{{body}}",
|
||||
"error.chain.didYouMean": "你是不是想输入: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "未找到模型: {{provider}}/{{model}}",
|
||||
"error.chain.status": "状态:{{status}}",
|
||||
"error.chain.retryable": "可重试:{{retryable}}",
|
||||
"error.chain.responseBody": "响应内容:\n{{body}}",
|
||||
"error.chain.didYouMean": "你是不是想输入:{{suggestions}}",
|
||||
"error.chain.modelNotFound": "未找到模型:{{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "请检查你的配置 (opencode.json) 中的 provider/model 名称",
|
||||
"error.chain.mcpFailed": 'MCP 服务器 "{{name}}" 启动失败。注意: OpenCode 暂不支持 MCP 认证。',
|
||||
"error.chain.providerAuthFailed": "提供商认证失败 ({{provider}}): {{message}}",
|
||||
"error.chain.providerAuthFailed": "提供商认证失败({{provider}}):{{message}}",
|
||||
"error.chain.providerInitFailed": '无法初始化提供商 "{{provider}}"。请检查凭据和配置。',
|
||||
"error.chain.configJsonInvalid": "配置文件 {{path}} 不是有效的 JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "配置文件 {{path}} 不是有效的 JSON(C): {{message}}",
|
||||
"error.chain.configJsonInvalidWithMessage": "配置文件 {{path}} 不是有效的 JSON(C):{{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'{{path}} 中的目录 "{{dir}}" 无效。请将目录重命名为 "{{suggestion}}" 或移除它。这是一个常见拼写错误。',
|
||||
"error.chain.configFrontmatterError": "无法解析 {{path}} 中的 frontmatter:\n{{message}}",
|
||||
"error.chain.configFrontmatterError": "无法解析 {{path}} 中的 frontmatter:\n{{message}}",
|
||||
"error.chain.configInvalid": "配置文件 {{path}} 无效",
|
||||
"error.chain.configInvalidWithMessage": "配置文件 {{path}} 无效: {{message}}",
|
||||
"error.chain.configInvalidWithMessage": "配置文件 {{path}} 无效:{{message}}",
|
||||
|
||||
"notification.permission.title": "需要权限",
|
||||
"notification.permission.description": "{{sessionTitle}}({{projectName}})需要权限",
|
||||
@@ -439,7 +443,7 @@ export const dict = {
|
||||
"session.context.addToContext": "将 {{selection}} 添加到上下文",
|
||||
|
||||
"session.new.worktree.main": "主分支",
|
||||
"session.new.worktree.mainWithBranch": "主分支 ({{branch}})",
|
||||
"session.new.worktree.mainWithBranch": "主分支({{branch}})",
|
||||
"session.new.worktree.create": "创建新的 worktree",
|
||||
"session.new.lastModified": "最后修改",
|
||||
|
||||
@@ -522,7 +526,6 @@ export const dict = {
|
||||
"settings.general.row.theme.description": "自定义 OpenCode 的主题。",
|
||||
"settings.general.row.font.title": "字体",
|
||||
"settings.general.row.font.description": "自定义代码块使用的等宽字体",
|
||||
|
||||
"settings.general.row.releaseNotes.title": "发行说明",
|
||||
"settings.general.row.releaseNotes.description": "更新后显示“新功能”弹窗",
|
||||
|
||||
@@ -686,7 +689,7 @@ export const dict = {
|
||||
|
||||
"session.delete.failed.title": "删除会话失败",
|
||||
"session.delete.title": "删除会话",
|
||||
"session.delete.confirm": '删除会话 "{{name}}"?',
|
||||
"session.delete.confirm": '删除会话 "{{name}}"?',
|
||||
"session.delete.button": "删除会话",
|
||||
|
||||
"workspace.new": "新建工作区",
|
||||
@@ -705,10 +708,10 @@ export const dict = {
|
||||
"workspace.status.clean": "未检测到未合并的更改。",
|
||||
"workspace.status.dirty": "检测到未合并的更改。",
|
||||
"workspace.delete.title": "删除工作区",
|
||||
"workspace.delete.confirm": '删除工作区 "{{name}}"?',
|
||||
"workspace.delete.confirm": '删除工作区 "{{name}}"?',
|
||||
"workspace.delete.button": "删除工作区",
|
||||
"workspace.reset.title": "重置工作区",
|
||||
"workspace.reset.confirm": '重置工作区 "{{name}}"?',
|
||||
"workspace.reset.confirm": '重置工作区 "{{name}}"?',
|
||||
"workspace.reset.button": "重置工作区",
|
||||
"workspace.reset.archived.none": "不会归档任何活跃会话。",
|
||||
"workspace.reset.archived.one": "将归档 1 个会话。",
|
||||
|
||||
@@ -2183,7 +2183,62 @@ export default function Page() {
|
||||
<ConstrainDragYAxis />
|
||||
<Tabs value={activeTab()} onChange={openTab}>
|
||||
<div class="sticky top-0 shrink-0 flex">
|
||||
<Tabs.List>
|
||||
<Tabs.List
|
||||
ref={(el: HTMLDivElement) => {
|
||||
let scrollTimeout: number | undefined
|
||||
let prevScrollWidth = el.scrollWidth
|
||||
let prevContextOpen = contextOpen()
|
||||
|
||||
const handler = () => {
|
||||
if (scrollTimeout !== undefined) clearTimeout(scrollTimeout)
|
||||
scrollTimeout = window.setTimeout(() => {
|
||||
const scrollWidth = el.scrollWidth
|
||||
const clientWidth = el.clientWidth
|
||||
const currentContextOpen = contextOpen()
|
||||
|
||||
// Only scroll when a tab is added (width increased), not on removal
|
||||
if (scrollWidth > prevScrollWidth) {
|
||||
if (!prevContextOpen && currentContextOpen) {
|
||||
// Context tab was opened, scroll to first
|
||||
el.scrollTo({
|
||||
left: 0,
|
||||
behavior: "smooth",
|
||||
})
|
||||
} else if (scrollWidth > clientWidth) {
|
||||
// File tab was added, scroll to rightmost
|
||||
el.scrollTo({
|
||||
left: scrollWidth - clientWidth,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
}
|
||||
// When width decreases (tab removed), don't scroll - let browser handle it naturally
|
||||
|
||||
prevScrollWidth = scrollWidth
|
||||
prevContextOpen = currentContextOpen
|
||||
}, 0)
|
||||
}
|
||||
|
||||
const wheelHandler = (e: WheelEvent) => {
|
||||
// Enable horizontal scrolling with mouse wheel
|
||||
if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
|
||||
el.scrollLeft += e.deltaY > 0 ? 50 : -50
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
el.addEventListener("wheel", wheelHandler, { passive: false })
|
||||
|
||||
const observer = new MutationObserver(handler)
|
||||
observer.observe(el, { childList: true })
|
||||
|
||||
onCleanup(() => {
|
||||
el.removeEventListener("wheel", wheelHandler)
|
||||
observer.disconnect()
|
||||
if (scrollTimeout !== undefined) clearTimeout(scrollTimeout)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Show when={contextOpen()}>
|
||||
<Tabs.Trigger
|
||||
value="context"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.1.40"
|
||||
version = "1.1.41"
|
||||
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.1.40/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.41/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.40/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.41/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.40/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.41/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.1.40/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.41/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.1.40/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.41/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -27,6 +27,10 @@ interface SessionStats {
|
||||
tokens: {
|
||||
input: number
|
||||
output: number
|
||||
cache: {
|
||||
read: number
|
||||
write: number
|
||||
}
|
||||
}
|
||||
cost: number
|
||||
}
|
||||
@@ -191,6 +195,10 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
||||
tokens: {
|
||||
input: number
|
||||
output: number
|
||||
cache: {
|
||||
read: number
|
||||
write: number
|
||||
}
|
||||
}
|
||||
cost: number
|
||||
}
|
||||
@@ -204,7 +212,7 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
||||
if (!sessionModelUsage[modelKey]) {
|
||||
sessionModelUsage[modelKey] = {
|
||||
messages: 0,
|
||||
tokens: { input: 0, output: 0 },
|
||||
tokens: { input: 0, output: 0, cache: { read: 0, write: 0 } },
|
||||
cost: 0,
|
||||
}
|
||||
}
|
||||
@@ -221,6 +229,8 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
||||
sessionModelUsage[modelKey].tokens.input += message.info.tokens.input || 0
|
||||
sessionModelUsage[modelKey].tokens.output +=
|
||||
(message.info.tokens.output || 0) + (message.info.tokens.reasoning || 0)
|
||||
sessionModelUsage[modelKey].tokens.cache.read += message.info.tokens.cache?.read || 0
|
||||
sessionModelUsage[modelKey].tokens.cache.write += message.info.tokens.cache?.write || 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +245,12 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
||||
messageCount: messages.length,
|
||||
sessionCost,
|
||||
sessionTokens,
|
||||
sessionTotalTokens: sessionTokens.input + sessionTokens.output + sessionTokens.reasoning,
|
||||
sessionTotalTokens:
|
||||
sessionTokens.input +
|
||||
sessionTokens.output +
|
||||
sessionTokens.reasoning +
|
||||
sessionTokens.cache.read +
|
||||
sessionTokens.cache.write,
|
||||
sessionToolUsage,
|
||||
sessionModelUsage,
|
||||
earliestTime: cutoffTime > 0 ? session.time.updated : session.time.created,
|
||||
@@ -266,13 +281,15 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
||||
if (!stats.modelUsage[model]) {
|
||||
stats.modelUsage[model] = {
|
||||
messages: 0,
|
||||
tokens: { input: 0, output: 0 },
|
||||
tokens: { input: 0, output: 0, cache: { read: 0, write: 0 } },
|
||||
cost: 0,
|
||||
}
|
||||
}
|
||||
stats.modelUsage[model].messages += usage.messages
|
||||
stats.modelUsage[model].tokens.input += usage.tokens.input
|
||||
stats.modelUsage[model].tokens.output += usage.tokens.output
|
||||
stats.modelUsage[model].tokens.cache.read += usage.tokens.cache.read
|
||||
stats.modelUsage[model].tokens.cache.write += usage.tokens.cache.write
|
||||
stats.modelUsage[model].cost += usage.cost
|
||||
}
|
||||
}
|
||||
@@ -286,7 +303,12 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
||||
}
|
||||
stats.days = effectiveDays
|
||||
stats.costPerDay = stats.totalCost / effectiveDays
|
||||
const totalTokens = stats.totalTokens.input + stats.totalTokens.output + stats.totalTokens.reasoning
|
||||
const totalTokens =
|
||||
stats.totalTokens.input +
|
||||
stats.totalTokens.output +
|
||||
stats.totalTokens.reasoning +
|
||||
stats.totalTokens.cache.read +
|
||||
stats.totalTokens.cache.write
|
||||
stats.tokensPerSession = filteredSessions.length > 0 ? totalTokens / filteredSessions.length : 0
|
||||
sessionTotalTokens.sort((a, b) => a - b)
|
||||
const mid = Math.floor(sessionTotalTokens.length / 2)
|
||||
@@ -353,6 +375,8 @@ export function displayStats(stats: SessionStats, toolLimit?: number, modelLimit
|
||||
console.log(renderRow(" Messages", usage.messages.toLocaleString()))
|
||||
console.log(renderRow(" Input Tokens", formatNumber(usage.tokens.input)))
|
||||
console.log(renderRow(" Output Tokens", formatNumber(usage.tokens.output)))
|
||||
console.log(renderRow(" Cache Read", formatNumber(usage.tokens.cache.read)))
|
||||
console.log(renderRow(" Cache Write", formatNumber(usage.tokens.cache.write)))
|
||||
console.log(renderRow(" Cost", `$${usage.cost.toFixed(4)}`))
|
||||
console.log("├────────────────────────────────────────────────────────┤")
|
||||
}
|
||||
|
||||
@@ -241,7 +241,11 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
||||
focusedTextColor={theme.textMuted}
|
||||
ref={(r) => {
|
||||
input = r
|
||||
setTimeout(() => input.focus(), 1)
|
||||
setTimeout(() => {
|
||||
if (!input) return
|
||||
if (input.isDestroyed) return
|
||||
input.focus()
|
||||
}, 1)
|
||||
}}
|
||||
placeholder={props.placeholder ?? "Search"}
|
||||
/>
|
||||
|
||||
@@ -14,7 +14,9 @@ export namespace ConfigMarkdown {
|
||||
return Array.from(template.matchAll(SHELL_REGEX))
|
||||
}
|
||||
|
||||
export function preprocessFrontmatter(content: string): string {
|
||||
// other coding agents like claude code allow invalid yaml in their
|
||||
// frontmatter, we need to fallback to a more permissive parser for those cases
|
||||
export function fallbackSanitization(content: string): string {
|
||||
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
|
||||
if (!match) return content
|
||||
|
||||
@@ -53,7 +55,7 @@ export namespace ConfigMarkdown {
|
||||
|
||||
// if value contains a colon, convert to block scalar
|
||||
if (value.includes(":")) {
|
||||
result.push(`${key}: |`)
|
||||
result.push(`${key}: |-`)
|
||||
result.push(` ${value}`)
|
||||
continue
|
||||
}
|
||||
@@ -66,20 +68,23 @@ export namespace ConfigMarkdown {
|
||||
}
|
||||
|
||||
export async function parse(filePath: string) {
|
||||
const raw = await Bun.file(filePath).text()
|
||||
const template = preprocessFrontmatter(raw)
|
||||
const template = await Bun.file(filePath).text()
|
||||
|
||||
try {
|
||||
const md = matter(template)
|
||||
return md
|
||||
} catch (err) {
|
||||
throw new FrontmatterError(
|
||||
{
|
||||
path: filePath,
|
||||
message: `${filePath}: Failed to parse YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`,
|
||||
},
|
||||
{ cause: err },
|
||||
)
|
||||
} catch {
|
||||
try {
|
||||
return matter(fallbackSanitization(template))
|
||||
} catch (err) {
|
||||
throw new FrontmatterError(
|
||||
{
|
||||
path: filePath,
|
||||
message: `${filePath}: Failed to parse YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`,
|
||||
},
|
||||
{ cause: err },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,10 @@ export namespace Ripgrep {
|
||||
hidden?: boolean
|
||||
follow?: boolean
|
||||
maxDepth?: number
|
||||
signal?: AbortSignal
|
||||
}) {
|
||||
input.signal?.throwIfAborted()
|
||||
|
||||
const args = [await filepath(), "--files", "--glob=!.git/*"]
|
||||
if (input.follow !== false) args.push("--follow")
|
||||
if (input.hidden !== false) args.push("--hidden")
|
||||
@@ -235,6 +238,7 @@ export namespace Ripgrep {
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
maxBuffer: 1024 * 1024 * 20,
|
||||
signal: input.signal,
|
||||
})
|
||||
|
||||
const reader = proc.stdout.getReader()
|
||||
@@ -243,6 +247,8 @@ export namespace Ripgrep {
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
input.signal?.throwIfAborted()
|
||||
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
@@ -261,11 +267,13 @@ export namespace Ripgrep {
|
||||
reader.releaseLock()
|
||||
await proc.exited
|
||||
}
|
||||
|
||||
input.signal?.throwIfAborted()
|
||||
}
|
||||
|
||||
export async function tree(input: { cwd: string; limit?: number }) {
|
||||
export async function tree(input: { cwd: string; limit?: number; signal?: AbortSignal }) {
|
||||
log.info("tree", input)
|
||||
const files = await Array.fromAsync(Ripgrep.files({ cwd: input.cwd }))
|
||||
const files = await Array.fromAsync(Ripgrep.files({ cwd: input.cwd, signal: input.signal }))
|
||||
interface Node {
|
||||
path: string[]
|
||||
children: Node[]
|
||||
|
||||
@@ -38,7 +38,6 @@ export namespace Flag {
|
||||
export const OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT = truthy("OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT")
|
||||
export const OPENCODE_ENABLE_EXA =
|
||||
truthy("OPENCODE_ENABLE_EXA") || OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_EXA")
|
||||
export const OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH = number("OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH")
|
||||
export const OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS = number("OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS")
|
||||
export const OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX = number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX")
|
||||
export const OPENCODE_EXPERIMENTAL_OXFMT = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_OXFMT")
|
||||
|
||||
@@ -977,7 +977,7 @@ export namespace Provider {
|
||||
...model.headers,
|
||||
}
|
||||
|
||||
const key = Bun.hash.xxHash32(JSON.stringify({ npm: model.api.npm, options }))
|
||||
const key = Bun.hash.xxHash32(JSON.stringify({ providerID: model.providerID, npm: model.api.npm, options }))
|
||||
const existing = s.sdk.get(key)
|
||||
if (existing) return existing
|
||||
|
||||
|
||||
@@ -428,13 +428,13 @@ export namespace ProviderTransform {
|
||||
high: {
|
||||
thinking: {
|
||||
type: "enabled",
|
||||
budgetTokens: 16000,
|
||||
budgetTokens: Math.min(16_000, Math.floor(model.limit.output / 2 - 1)),
|
||||
},
|
||||
},
|
||||
max: {
|
||||
thinking: {
|
||||
type: "enabled",
|
||||
budgetTokens: 31999,
|
||||
budgetTokens: Math.min(31_999, model.limit.output - 1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -539,7 +539,7 @@ export namespace Server {
|
||||
})
|
||||
response.headers.set(
|
||||
"Content-Security-Policy",
|
||||
"default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' data:",
|
||||
"default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:",
|
||||
)
|
||||
return response
|
||||
}) as unknown as Hono,
|
||||
|
||||
@@ -150,20 +150,14 @@ export namespace LLM {
|
||||
},
|
||||
)
|
||||
|
||||
const maxOutputTokens = isCodex ? undefined : undefined
|
||||
log.info("max_output_tokens", {
|
||||
tokens: ProviderTransform.maxOutputTokens(
|
||||
input.model.api.npm,
|
||||
params.options,
|
||||
input.model.limit.output,
|
||||
OUTPUT_TOKEN_MAX,
|
||||
),
|
||||
modelOptions: params.options,
|
||||
outputLimit: input.model.limit.output,
|
||||
})
|
||||
// tokens = 32000
|
||||
// outputLimit = 64000
|
||||
// modelOptions={"reasoningEffort":"minimal"}
|
||||
const maxOutputTokens = isCodex
|
||||
? undefined
|
||||
: ProviderTransform.maxOutputTokens(
|
||||
input.model.api.npm,
|
||||
params.options,
|
||||
input.model.limit.output,
|
||||
OUTPUT_TOKEN_MAX,
|
||||
)
|
||||
|
||||
const tools = await resolveTools(input)
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ export const GlobTool = Tool.define("glob", {
|
||||
for await (const file of Ripgrep.files({
|
||||
cwd: search,
|
||||
glob: [params.pattern],
|
||||
signal: ctx.abort,
|
||||
})) {
|
||||
if (files.length >= limit) {
|
||||
truncated = true
|
||||
|
||||
@@ -54,6 +54,7 @@ export const GrepTool = Tool.define("grep", {
|
||||
const proc = Bun.spawn([rgPath, ...args], {
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
signal: ctx.abort,
|
||||
})
|
||||
|
||||
const output = await new Response(proc.stdout).text()
|
||||
|
||||
@@ -56,7 +56,7 @@ export const ListTool = Tool.define("list", {
|
||||
|
||||
const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(params.ignore?.map((p) => `!${p}`) || [])
|
||||
const files = []
|
||||
for await (const file of Ripgrep.files({ cwd: searchPath, glob: ignoreGlobs })) {
|
||||
for await (const file of Ripgrep.files({ cwd: searchPath, glob: ignoreGlobs, signal: ctx.abort })) {
|
||||
files.push(file)
|
||||
if (files.length >= LIMIT) break
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Response Formatting Requirements
|
||||
|
||||
Always structure your responses using clear markdown formatting:
|
||||
|
||||
- By default don't put information into tables for questions (but do put information into tables when creating or updating files)
|
||||
- Use headings (##, ###) to organise sections, always
|
||||
- Use bullet points or numbered lists for multiple items
|
||||
- Use code blocks with language tags for any code
|
||||
- Use **bold** for key terms and emphasis
|
||||
- Use tables when comparing options or listing structured data
|
||||
- Break long responses into logical sections with headings
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
description: General coding and planning agent
|
||||
mode: subagent
|
||||
model: synthetic/hf:zai-org/GLM-4.7
|
||||
tools:
|
||||
write: true
|
||||
read: true
|
||||
edit: true
|
||||
stuff: >
|
||||
This is some stuff
|
||||
---
|
||||
|
||||
Strictly follow da rules
|
||||
@@ -104,7 +104,7 @@ describe("ConfigMarkdown: frontmatter parsing", async () => {
|
||||
})
|
||||
|
||||
test("should extract occupation field with colon in value", () => {
|
||||
expect(parsed.data.occupation).toBe("This man has the following occupation: Software Engineer\n")
|
||||
expect(parsed.data.occupation).toBe("This man has the following occupation: Software Engineer")
|
||||
})
|
||||
|
||||
test("should extract title field with single quotes", () => {
|
||||
@@ -128,15 +128,15 @@ describe("ConfigMarkdown: frontmatter parsing", async () => {
|
||||
})
|
||||
|
||||
test("should extract URL with port", () => {
|
||||
expect(parsed.data.url).toBe("https://example.com:8080/path?query=value\n")
|
||||
expect(parsed.data.url).toBe("https://example.com:8080/path?query=value")
|
||||
})
|
||||
|
||||
test("should extract time with colons", () => {
|
||||
expect(parsed.data.time).toBe("The time is 12:30:00 PM\n")
|
||||
expect(parsed.data.time).toBe("The time is 12:30:00 PM")
|
||||
})
|
||||
|
||||
test("should extract value with multiple colons", () => {
|
||||
expect(parsed.data.nested).toBe("First: Second: Third: Fourth\n")
|
||||
expect(parsed.data.nested).toBe("First: Second: Third: Fourth")
|
||||
})
|
||||
|
||||
test("should preserve already double-quoted values with colons", () => {
|
||||
@@ -148,7 +148,7 @@ describe("ConfigMarkdown: frontmatter parsing", async () => {
|
||||
})
|
||||
|
||||
test("should extract value with quotes and colons mixed", () => {
|
||||
expect(parsed.data.mixed).toBe('He said "hello: world" and then left\n')
|
||||
expect(parsed.data.mixed).toBe('He said "hello: world" and then left')
|
||||
})
|
||||
|
||||
test("should handle empty values", () => {
|
||||
@@ -190,3 +190,39 @@ describe("ConfigMarkdown: frontmatter parsing w/ no frontmatter", async () => {
|
||||
expect(result.content.trim()).toBe("Content")
|
||||
})
|
||||
})
|
||||
|
||||
describe("ConfigMarkdown: frontmatter parsing w/ Markdown header", async () => {
|
||||
const result = await ConfigMarkdown.parse(import.meta.dir + "/fixtures/markdown-header.md")
|
||||
|
||||
test("should parse and match", () => {
|
||||
expect(result).toBeDefined()
|
||||
expect(result.data).toEqual({})
|
||||
expect(result.content.trim()).toBe(`# Response Formatting Requirements
|
||||
|
||||
Always structure your responses using clear markdown formatting:
|
||||
|
||||
- By default don't put information into tables for questions (but do put information into tables when creating or updating files)
|
||||
- Use headings (##, ###) to organise sections, always
|
||||
- Use bullet points or numbered lists for multiple items
|
||||
- Use code blocks with language tags for any code
|
||||
- Use **bold** for key terms and emphasis
|
||||
- Use tables when comparing options or listing structured data
|
||||
- Break long responses into logical sections with headings`)
|
||||
})
|
||||
})
|
||||
|
||||
describe("ConfigMarkdown: frontmatter has weird model id", async () => {
|
||||
const result = await ConfigMarkdown.parse(import.meta.dir + "/fixtures/weird-model-id.md")
|
||||
|
||||
test("should parse and match", () => {
|
||||
expect(result).toBeDefined()
|
||||
expect(result.data["description"]).toEqual("General coding and planning agent")
|
||||
expect(result.data["mode"]).toEqual("subagent")
|
||||
expect(result.data["model"]).toEqual("synthetic/hf:zai-org/GLM-4.7")
|
||||
expect(result.data["tools"]["write"]).toBeTrue()
|
||||
expect(result.data["tools"]["read"]).toBeTrue()
|
||||
expect(result.data["stuff"]).toBe("This is some stuff\n")
|
||||
|
||||
expect(result.content.trim()).toBe("Strictly follow da rules")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1056,8 +1056,8 @@ describe("ProviderTransform.variants", () => {
|
||||
cache: { read: 0.0001, write: 0.0002 },
|
||||
},
|
||||
limit: {
|
||||
context: 128000,
|
||||
output: 8192,
|
||||
context: 200_000,
|
||||
output: 64_000,
|
||||
},
|
||||
status: "active",
|
||||
options: {},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -25,4 +25,4 @@
|
||||
"typescript": "catalog:",
|
||||
"@typescript/native-preview": "catalog:"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -30,4 +30,4 @@
|
||||
"publishConfig": {
|
||||
"directory": "dist"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
||||
@@ -60,7 +60,9 @@ export function Dialog(props: DialogProps) {
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={props.description}>
|
||||
<Kobalte.Description data-slot="dialog-description">{props.description}</Kobalte.Description>
|
||||
<Kobalte.Description data-slot="dialog-description" style={{ "margin-left": "-4px" }}>
|
||||
{props.description}
|
||||
</Kobalte.Description>
|
||||
</Show>
|
||||
<div data-slot="dialog-body">{props.children}</div>
|
||||
</Kobalte.Content>
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
[data-slot="list-header"] {
|
||||
display: flex;
|
||||
z-index: 10;
|
||||
padding: 8px 12px 8px 12px;
|
||||
padding: 8px 12px 8px 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
|
||||
@@ -390,12 +390,14 @@ export function SessionTurn(
|
||||
const interval = Interval.fromDateTimes(from, to)
|
||||
const unit: DurationUnit[] = interval.length("seconds") > 60 ? ["minutes", "seconds"] : ["seconds"]
|
||||
|
||||
return interval.toDuration(unit).normalize().reconfigure({ locale: i18n.locale() }).toHuman({
|
||||
const locale = i18n.locale()
|
||||
const human = interval.toDuration(unit).normalize().reconfigure({ locale }).toHuman({
|
||||
notation: "compact",
|
||||
unitDisplay: "narrow",
|
||||
compactDisplay: "short",
|
||||
showZeros: false,
|
||||
})
|
||||
return locale.startsWith("zh") ? human.replaceAll("、", "") : human
|
||||
}
|
||||
|
||||
const autoScroll = createAutoScroll({
|
||||
|
||||
@@ -375,8 +375,13 @@
|
||||
|
||||
&[data-variant="settings"] {
|
||||
[data-slot="tabs-list"] {
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
width: 150px;
|
||||
min-width: 150px;
|
||||
|
||||
@media (min-width: 640px) {
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
}
|
||||
padding: 12px;
|
||||
gap: 0;
|
||||
background-color: var(--background-base);
|
||||
|
||||
@@ -475,6 +475,7 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext(
|
||||
},
|
||||
markedKatex({
|
||||
throwOnError: false,
|
||||
nonStandard: true,
|
||||
}),
|
||||
markedShiki({
|
||||
async highlight(code, lang) {
|
||||
|
||||
@@ -20,7 +20,7 @@ export const dict = {
|
||||
"ui.sessionTurn.steps.show": "显示步骤",
|
||||
"ui.sessionTurn.steps.hide": "隐藏步骤",
|
||||
"ui.sessionTurn.summary.response": "回复",
|
||||
"ui.sessionTurn.diff.showMore": "显示更多更改 ({{count}})",
|
||||
"ui.sessionTurn.diff.showMore": "显示更多更改({{count}})",
|
||||
|
||||
"ui.sessionTurn.retry.retrying": "重试中",
|
||||
"ui.sessionTurn.retry.inSeconds": "{{seconds}} 秒后",
|
||||
@@ -33,7 +33,7 @@ export const dict = {
|
||||
"ui.sessionTurn.status.makingEdits": "正在修改",
|
||||
"ui.sessionTurn.status.runningCommands": "正在运行命令",
|
||||
"ui.sessionTurn.status.thinking": "思考中",
|
||||
"ui.sessionTurn.status.thinkingWithTopic": "思考 - {{topic}}",
|
||||
"ui.sessionTurn.status.thinkingWithTopic": "思考:{{topic}}",
|
||||
"ui.sessionTurn.status.gatheringThoughts": "正在整理思路",
|
||||
"ui.sessionTurn.status.consideringNextSteps": "正在考虑下一步",
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -585,9 +585,13 @@ These environment variables enable experimental features that may change or be r
|
||||
| `OPENCODE_EXPERIMENTAL` | boolean | Enable all experimental features |
|
||||
| `OPENCODE_EXPERIMENTAL_ICON_DISCOVERY` | boolean | Enable icon discovery |
|
||||
| `OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT` | boolean | Disable copy on select in TUI |
|
||||
| `OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH` | number | Max output length for bash commands |
|
||||
| `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS` | number | Default timeout for bash commands in ms |
|
||||
| `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | number | Max output tokens for LLM responses |
|
||||
| `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | Enable file watcher for entire dir |
|
||||
| `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | Enable oxfmt formatter |
|
||||
| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | Enable experimental LSP tool |
|
||||
| `OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER` | boolean | Disable file watcher |
|
||||
| `OPENCODE_EXPERIMENTAL_EXA` | boolean | Enable experimental Exa features |
|
||||
| `OPENCODE_EXPERIMENTAL_LSP_TY` | boolean | Enable experimental LSP type checking |
|
||||
| `OPENCODE_EXPERIMENTAL_MARKDOWN` | boolean | Enable experimental markdown features |
|
||||
| `OPENCODE_EXPERIMENTAL_PLAN_MODE` | boolean | Enable plan mode |
|
||||
|
||||
@@ -15,6 +15,7 @@ export const team = [
|
||||
"adamdotdevin",
|
||||
"iamdavidhill",
|
||||
"opencode-agent[bot]",
|
||||
"R44VC0RP",
|
||||
]
|
||||
|
||||
export async function getLatestRelease() {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { buildNotes, getLatestRelease } from "./changelog"
|
||||
|
||||
const highlightsTemplate = `## Highlights
|
||||
|
||||
<!--
|
||||
<!--
|
||||
Add highlights before publishing. Delete this section if no highlights.
|
||||
|
||||
- For multiple highlights, use multiple <highlight> tags
|
||||
@@ -40,7 +40,7 @@ console.log("=== publishing ===\n")
|
||||
if (!Script.preview) {
|
||||
const previous = await getLatestRelease()
|
||||
notes = await buildNotes(previous, "HEAD")
|
||||
notes.unshift(highlightsTemplate)
|
||||
// notes.unshift(highlightsTemplate)
|
||||
}
|
||||
|
||||
const pkgjsons = await Array.fromAsync(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.1.40",
|
||||
"version": "1.1.41",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user