Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03de51bd3c | ||
|
|
8f9742d988 | ||
|
|
f6e7aefa72 | ||
|
|
e269788a8f | ||
|
|
66780195dc | ||
|
|
ad2087094d | ||
|
|
5bdf1c4b96 | ||
|
|
135f8ffb2a | ||
|
|
bf5a01edd9 | ||
|
|
81ca2df6ad | ||
|
|
aea68c386a | ||
|
|
8eea53a41e | ||
|
|
3befd0c6c5 | ||
|
|
8577eb8ec9 | ||
|
|
c856f875a1 | ||
|
|
264dd213f9 | ||
|
|
125727d09c | ||
|
|
8c7b35ad05 | ||
|
|
e2a33f75e1 | ||
|
|
006d673ed2 | ||
|
|
6b4d617df0 | ||
|
|
e3471526f4 | ||
|
|
6b30e0b752 | ||
|
|
fbabce1125 | ||
|
|
8f56ed5b85 | ||
|
|
81b5a6a08b | ||
|
|
94cb6390aa |
@@ -8,6 +8,8 @@
|
||||
# - Denounce with minus prefix: -username or -platform:username.
|
||||
# - Optional details after a space following the handle.
|
||||
adamdotdevin
|
||||
ariane-emory
|
||||
-florianleibert
|
||||
fwang
|
||||
iamdavidhill
|
||||
jayair
|
||||
|
||||
@@ -64,10 +64,13 @@ jobs:
|
||||
|
||||
Requirements:
|
||||
1. Update all relevant locale docs under packages/web/src/content/docs/<locale>/ so they reflect these English page changes.
|
||||
2. Preserve frontmatter keys, internal links, code blocks, and existing locale-specific metadata unless the English change requires an update.
|
||||
3. Keep locale docs structure aligned with their corresponding English pages.
|
||||
4. Do not modify English source docs in packages/web/src/content/docs/*.mdx.
|
||||
5. If no locale updates are needed, make no changes.
|
||||
2. You MUST use the Task tool for translation work and launch subagents with subagent_type `translator` (defined in .opencode/agent/translator.md).
|
||||
3. Do not translate directly in the primary agent. Use translator subagent output as the source for locale text updates.
|
||||
4. Run translator subagent Task calls in parallel whenever file/locale translation work is independent.
|
||||
5. Preserve frontmatter keys, internal links, code blocks, and existing locale-specific metadata unless the English change requires an update.
|
||||
6. Keep locale docs structure aligned with their corresponding English pages.
|
||||
7. Do not modify English source docs in packages/web/src/content/docs/*.mdx.
|
||||
8. If no locale updates are needed, make no changes.
|
||||
|
||||
- name: Commit and push locale docs updates
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
|
||||
@@ -32,6 +32,9 @@ description: Use this when you are working on file operations like reading, writ
|
||||
- Decode tool stderr with `Bun.readableStreamToText`.
|
||||
- For large writes, use `Bun.write(Bun.file(path), text)`.
|
||||
|
||||
NOTE: Bun.file(...).exists() will return `false` if the value is a directory.
|
||||
Use Filesystem.exists(...) instead if path can be file or directory
|
||||
|
||||
## Quick checklist
|
||||
|
||||
- Use Bun APIs first.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"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.59",
|
||||
"version": "1.1.60",
|
||||
"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.59",
|
||||
"version": "1.1.60",
|
||||
"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.59",
|
||||
"version": "1.1.60",
|
||||
"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.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -215,7 +215,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
@@ -244,7 +244,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -260,7 +260,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -301,8 +301,8 @@
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "1.5.4",
|
||||
"@opentui/core": "0.1.77",
|
||||
"@opentui/solid": "0.1.77",
|
||||
"@opentui/core": "0.1.79",
|
||||
"@opentui/solid": "0.1.79",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@pierre/diffs": "catalog:",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
@@ -366,7 +366,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -386,7 +386,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.90.10",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -397,7 +397,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -410,7 +410,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -452,7 +452,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -463,7 +463,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -522,7 +522,7 @@
|
||||
"@tailwindcss/vite": "4.1.11",
|
||||
"@tsconfig/bun": "1.0.9",
|
||||
"@tsconfig/node22": "22.0.2",
|
||||
"@types/bun": "1.3.8",
|
||||
"@types/bun": "1.3.9",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/node": "22.13.9",
|
||||
"@types/semver": "7.7.1",
|
||||
@@ -1279,21 +1279,21 @@
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||
|
||||
"@opentui/core": ["@opentui/core@0.1.77", "", { "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.77", "@opentui/core-darwin-x64": "0.1.77", "@opentui/core-linux-arm64": "0.1.77", "@opentui/core-linux-x64": "0.1.77", "@opentui/core-win32-arm64": "0.1.77", "@opentui/core-win32-x64": "0.1.77", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-lE3kabm6jdqK3AuBq+O0zZrXdxt6ulmibTc57sf+AsPny6cmwYHnWI4wD6hcreFiYoQVNVvdiJchVgPtowMlEg=="],
|
||||
"@opentui/core": ["@opentui/core@0.1.79", "", { "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.79", "@opentui/core-darwin-x64": "0.1.79", "@opentui/core-linux-arm64": "0.1.79", "@opentui/core-linux-x64": "0.1.79", "@opentui/core-win32-arm64": "0.1.79", "@opentui/core-win32-x64": "0.1.79", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-job/t09w8A/aHb/WuaVbimu5fIffyN+PCuVO5cYhXEg/NkOkC/WdFi80B8bwncR/DBPyLAh6oJ3EG86grOVo5g=="],
|
||||
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.77", "", { "os": "darwin", "cpu": "arm64" }, "sha512-SNqmygCMEsPCW7xWjzCZ5caBf36xaprwVdAnFijGDOuIzLA4iaDa6um8cj3TJh7awenN3NTRsuRc7OuH42UH+g=="],
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.79", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kgsGniV+DM5G1P3GideyJhvfnthNKcVCAm2mPTIr9InQ3L0gS/Feh7zgwOS/jxDvdlQbOWGKMk2Z3JApeC1MLw=="],
|
||||
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.77", "", { "os": "darwin", "cpu": "x64" }, "sha512-/8fsa03swEHTQt/9NrGm98kemlU+VuTURI/OFZiH53vPDRrOYIYoa4Jyga/H7ZMcG+iFhkq97zIe+0Kw95LGmA=="],
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.79", "", { "os": "darwin", "cpu": "x64" }, "sha512-OpyAmFqAAKQ2CeFmf/oLWcNksmP6Ryx/3R5dbKXThOudMCeQvfvInJTRbc2jTn9VFpf+Qj4BgHkJg1h90tf/EA=="],
|
||||
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.77", "", { "os": "linux", "cpu": "arm64" }, "sha512-QfUXZJPc69OvqoMu+AlLgjqXrwu4IeqcBuUWYMuH8nPTeLsVUc3CBbXdV2lv9UDxWzxzrxdS4ALPaxvmEv9lsQ=="],
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.79", "", { "os": "linux", "cpu": "arm64" }, "sha512-DCa5YaknS4bWhFt8TMEGH+qmTinyzuY8hoZbO4crtWXAxofPP7Pas76Cwxlvis/PyLffA+pPxAl1l5sUZpsvqw=="],
|
||||
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.77", "", { "os": "linux", "cpu": "x64" }, "sha512-Kmfx0yUKnPj67AoXYIgL7qQo0QVsUG5Iw8aRtv6XFzXqa5SzBPhaKkKZ9yHPjOmTalZquUs+9zcCRNKpYYuL7A=="],
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.79", "", { "os": "linux", "cpu": "x64" }, "sha512-V6xjvFfHh3NGvsuuDae1KHPRZXHMEE8XL0A/GM6v4I4OCC23kDmkK60Vn6OptQwAzwwbz0X0IX+Ut/GQU9qGgA=="],
|
||||
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.77", "", { "os": "win32", "cpu": "arm64" }, "sha512-HGTscPXc7gdd23Nh1DbzUNjog1I+5IZp95XPtLftGTpjrWs60VcetXcyJqK2rQcXNxewJK5yDyaa5QyMjfEhCQ=="],
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.79", "", { "os": "win32", "cpu": "arm64" }, "sha512-sPRKnVzOdT5szI59tte7pxwwkYA+07EQN+6miFAvkFuiLmRUngONUD8HVjL7nCnxcPFqxaU4Rvl1y40ST86g8g=="],
|
||||
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.77", "", { "os": "win32", "cpu": "x64" }, "sha512-c7GijsbvVgnlzd2murIbwuwrGbcv76KdUw6WlVv7a0vex50z6xJCpv1keGzpe0QfxrZ/6fFEFX7JnwGLno0wjA=="],
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.79", "", { "os": "win32", "cpu": "x64" }, "sha512-vmQcFTvKf9fqajnDtgU6/uAsiTGwx8//khqHVBmiTEXUsiT792Ki9l8sgNughbuldqG5iZOiF6IaAWU1H67UpA=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/solid@0.1.77", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.77", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-JY+hUbXVV+XCk6bC8dvcwawWCEmC3Gid6GDs23AJWBgHZ3TU2kRKrgwTdltm45DOq2cZXrYCt690/yE8bP+Gxg=="],
|
||||
"@opentui/solid": ["@opentui/solid@0.1.79", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.79", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-c5+0jexKxb8GwRDDkQ/U6isZZqClAzHccXmYiLYmSnqdoQQp2lIGHLartL+K8lfIQrsKClzP2ZHumN6nexRfRg=="],
|
||||
|
||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||
|
||||
@@ -1853,7 +1853,7 @@
|
||||
|
||||
"@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
|
||||
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
||||
|
||||
"@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
|
||||
|
||||
@@ -2181,7 +2181,7 @@
|
||||
|
||||
"bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
|
||||
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
||||
|
||||
"bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"keep": {
|
||||
"days": true,
|
||||
"amount": 14
|
||||
},
|
||||
"auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json",
|
||||
"files": [
|
||||
{
|
||||
"date": 1759827172859,
|
||||
"name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log",
|
||||
"hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16"
|
||||
}
|
||||
],
|
||||
"hashType": "sha256"
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:52.879"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:52.880"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:56.191"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:56.192"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:59.267"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:59.268"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:20.276"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:20.277"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:30.838"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:30.839"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:42.452"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:42.452"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:46.499"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:46.500"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:02.295"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:02.295"}
|
||||
{"arguments":{"url":"https://google.com"},"level":"debug","message":"Tool call received","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:37.150","tool":"puppeteer_navigate"}
|
||||
{"0":"n","1":"p","2":"x","level":"info","message":"Launching browser with config:","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:37.150"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:55:08.488"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:55:08.489"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:11.815"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:11.816"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:21.934"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:21.935"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:32.544"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:32.544"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:41.154"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:41.155"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:55.426"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:55.427"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:15.715"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:15.716"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:25.063"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:25.064"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:48.567"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:48.568"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:25:08.937"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:25:08.938"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:37.120"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:37.121"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:52.490"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:52.491"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:39:25.524"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:39:25.525"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:40:57.126"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:40:57.127"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:42:24.175"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:42:24.176"}
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-cvRBvHRuunNjF07c4GVHl5rRgoTn1qfI/HdJWtOV63M=",
|
||||
"aarch64-linux": "sha256-DJUI4pMZ7wQTnyOiuDHALmZz7FZtrTbzRzCuNOShmWE=",
|
||||
"aarch64-darwin": "sha256-JnkqDwuC7lNsjafV+jOGfvs8K1xC8rk5CTOW+spjiCA=",
|
||||
"x86_64-darwin": "sha256-GBeTqq2vDn/mXplYNglrAT2xajjFVzB4ATHnMS0j7z4="
|
||||
"x86_64-linux": "sha256-pp2gb4nxiIT3VltB6Xli2wZPH32JfnMsI+BbihyU1+E=",
|
||||
"aarch64-linux": "sha256-hJwxhBICZz/pbIxQsF/sIpZTlFIgLpcAyF44O8wxMdU=",
|
||||
"aarch64-darwin": "sha256-DPONXP52XOg/ApdSnLp32a+K5XCOnDGhbTUto2Rme0g=",
|
||||
"x86_64-darwin": "sha256-KX1h5LRJSgthpbOPqWlbM/sPf8cvQrdRJvxtrz/FzBQ="
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
"description": "AI-powered development tool",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.3.8",
|
||||
"packageManager": "bun@1.3.9",
|
||||
"scripts": {
|
||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
||||
"dev:desktop": "bun --cwd packages/desktop tauri dev",
|
||||
@@ -23,7 +23,7 @@
|
||||
"packages/slack"
|
||||
],
|
||||
"catalog": {
|
||||
"@types/bun": "1.3.8",
|
||||
"@types/bun": "1.3.9",
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"ulid": "3.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { onCleanup, onMount } from "solid-js"
|
||||
import { showToast } from "@opencode-ai/ui/toast"
|
||||
import { usePrompt, type ContentPart, type ImageAttachmentPart } from "@/context/prompt"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { uuid } from "@/utils/uuid"
|
||||
import { getCursorPosition } from "./editor-dom"
|
||||
|
||||
export const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"]
|
||||
@@ -31,7 +32,7 @@ export function createPromptAttachments(input: PromptAttachmentsInput) {
|
||||
const dataUrl = reader.result as string
|
||||
const attachment: ImageAttachmentPart = {
|
||||
type: "image",
|
||||
id: crypto.randomUUID?.() ?? Math.random().toString(16).slice(2),
|
||||
id: uuid(),
|
||||
filename: file.name,
|
||||
mime: file.type,
|
||||
dataUrl,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import { createScopedCache } from "@/utils/scoped-cache"
|
||||
import { uuid } from "@/utils/uuid"
|
||||
import type { SelectedLineRange } from "@/context/file"
|
||||
|
||||
export type LineComment = {
|
||||
@@ -53,7 +54,7 @@ function createCommentSessionState(store: Store<CommentStore>, setStore: SetStor
|
||||
|
||||
const add = (input: Omit<LineComment, "id" | "time">) => {
|
||||
const next: LineComment = {
|
||||
id: crypto.randomUUID?.() ?? Math.random().toString(16).slice(2),
|
||||
id: uuid(),
|
||||
time: Date.now(),
|
||||
...input,
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { useGlobalSync } from "./global-sync"
|
||||
import { useGlobalSDK } from "./global-sdk"
|
||||
import { useServer } from "./server"
|
||||
import { usePlatform } from "./platform"
|
||||
import { Project } from "@opencode-ai/sdk/v2"
|
||||
import { Persist, persisted, removePersisted } from "@/utils/persist"
|
||||
import { same } from "@/utils/same"
|
||||
@@ -90,6 +91,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
||||
const globalSdk = useGlobalSDK()
|
||||
const globalSync = useGlobalSync()
|
||||
const server = useServer()
|
||||
const platform = usePlatform()
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
@@ -200,10 +202,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
||||
|
||||
for (const entry of SESSION_STATE_KEYS) {
|
||||
const target = session ? Persist.session(dir, session, entry.key) : Persist.workspace(dir, entry.key)
|
||||
void removePersisted(target)
|
||||
void removePersisted(target, platform)
|
||||
|
||||
const legacyKey = `${dir}/${entry.legacy}${session ? "/" + session : ""}.${entry.version}`
|
||||
void removePersisted({ key: legacyKey })
|
||||
void removePersisted({ key: legacyKey }, platform)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { batch, createEffect, createMemo, createRoot, onCleanup } from "solid-js"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { useSDK } from "./sdk"
|
||||
import type { Platform } from "./platform"
|
||||
import { Persist, persisted, removePersisted } from "@/utils/persist"
|
||||
|
||||
export type LocalPTY = {
|
||||
@@ -37,14 +38,14 @@ type TerminalCacheEntry = {
|
||||
|
||||
const caches = new Set<Map<string, TerminalCacheEntry>>()
|
||||
|
||||
export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[]) {
|
||||
export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[], platform?: Platform) {
|
||||
const key = getWorkspaceTerminalCacheKey(dir)
|
||||
for (const cache of caches) {
|
||||
const entry = cache.get(key)
|
||||
entry?.value.clear()
|
||||
}
|
||||
|
||||
removePersisted(Persist.workspace(dir, "terminal"))
|
||||
removePersisted(Persist.workspace(dir, "terminal"), platform)
|
||||
|
||||
const legacy = new Set(getLegacyTerminalStorageKeys(dir))
|
||||
for (const id of sessionIDs ?? []) {
|
||||
@@ -53,7 +54,7 @@ export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[]) {
|
||||
}
|
||||
}
|
||||
for (const key of legacy) {
|
||||
removePersisted({ key })
|
||||
removePersisted({ key }, platform)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-19
@@ -110,25 +110,30 @@ export const dict = {
|
||||
"provider.connect.status.inProgress": "جارٍ التفويض...",
|
||||
"provider.connect.status.waiting": "في انتظار التفويض...",
|
||||
"provider.connect.status.failed": "فشل التفويض: {{error}}",
|
||||
"provider.connect.apiKey.description": "أدخل مفتاح واجهة برمجة تطبيقات {{provider}} الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.apiKey.description":
|
||||
"أدخل مفتاح واجهة برمجة تطبيقات {{provider}} الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.apiKey.label": "مفتاح واجهة برمجة تطبيقات {{provider}}",
|
||||
"provider.connect.apiKey.placeholder": "مفتاح API",
|
||||
"provider.connect.apiKey.required": "مفتاح API مطلوب",
|
||||
"provider.connect.opencodeZen.line1": "يمنحك OpenCode Zen الوصول إلى مجموعة مختارة من النماذج الموثوقة والمحسنة لوكلاء البرمجة.",
|
||||
"provider.connect.opencodeZen.line2": "باستخدام مفتاح API واحد، ستحصل على إمكانية الوصول إلى نماذج مثل Claude و GPT و Gemini و GLM والمزيد.",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"يمنحك OpenCode Zen الوصول إلى مجموعة مختارة من النماذج الموثوقة والمحسنة لوكلاء البرمجة.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"باستخدام مفتاح API واحد، ستحصل على إمكانية الوصول إلى نماذج مثل Claude و GPT و Gemini و GLM والمزيد.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "قم بزيارة ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": " للحصول على مفتاح API الخاص بك.",
|
||||
"provider.connect.oauth.code.visit.prefix": "قم بزيارة ",
|
||||
"provider.connect.oauth.code.visit.link": "هذا الرابط",
|
||||
"provider.connect.oauth.code.visit.suffix": " للحصول على رمز التفويض الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" للحصول على رمز التفويض الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.oauth.code.label": "رمز تفويض {{method}}",
|
||||
"provider.connect.oauth.code.placeholder": "رمز التفويض",
|
||||
"provider.connect.oauth.code.required": "رمز التفويض مطلوب",
|
||||
"provider.connect.oauth.code.invalid": "رمز التفويض غير صالح",
|
||||
"provider.connect.oauth.auto.visit.prefix": "قم بزيارة ",
|
||||
"provider.connect.oauth.auto.visit.link": "هذا الرابط",
|
||||
"provider.connect.oauth.auto.visit.suffix": " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "رمز التأكيد",
|
||||
"provider.connect.toast.connected.title": "تم توصيل {{provider}}",
|
||||
"provider.connect.toast.connected.description": "نماذج {{provider}} متاحة الآن للاستخدام.",
|
||||
@@ -200,7 +205,7 @@ export const dict = {
|
||||
"common.default": "افتراضي",
|
||||
"common.attachment": "مرفق",
|
||||
"prompt.placeholder.shell": "أدخل أمر shell...",
|
||||
"prompt.placeholder.normal": "اسأل أي شيء... \"{{example}}\"",
|
||||
"prompt.placeholder.normal": 'اسأل أي شيء... "{{example}}"',
|
||||
"prompt.placeholder.summarizeComments": "لخّص التعليقات…",
|
||||
"prompt.placeholder.summarizeComment": "لخّص التعليق…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
@@ -278,7 +283,8 @@ export const dict = {
|
||||
"dialog.server.add.checking": "جارٍ التحقق...",
|
||||
"dialog.server.add.button": "إضافة خادم",
|
||||
"dialog.server.default.title": "الخادم الافتراضي",
|
||||
"dialog.server.default.description": "الاتصال بهذا الخادم عند بدء تشغيل التطبيق بدلاً من بدء خادم محلي. يتطلب إعادة التشغيل.",
|
||||
"dialog.server.default.description":
|
||||
"الاتصال بهذا الخادم عند بدء تشغيل التطبيق بدلاً من بدء خادم محلي. يتطلب إعادة التشغيل.",
|
||||
"dialog.server.default.none": "لم يتم تحديد خادم",
|
||||
"dialog.server.default.set": "تعيين الخادم الحالي كافتراضي",
|
||||
"dialog.server.default.clear": "مسح",
|
||||
@@ -301,7 +307,7 @@ export const dict = {
|
||||
"dialog.project.edit.worktree.startup.description": "يتم تشغيله بعد إنشاء مساحة عمل جديدة (شجرة عمل).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "مثال: bun install",
|
||||
"context.breakdown.title": "تفصيل السياق",
|
||||
"context.breakdown.note": "تفصيل تقريبي لرموز الإدخال. يشمل \"أخرى\" تعريفات الأدوات والنفقات العامة.",
|
||||
"context.breakdown.note": 'تفصيل تقريبي لرموز الإدخال. يشمل "أخرى" تعريفات الأدوات والنفقات العامة.',
|
||||
"context.breakdown.system": "النظام",
|
||||
"context.breakdown.user": "المستخدم",
|
||||
"context.breakdown.assistant": "المساعد",
|
||||
@@ -388,7 +394,8 @@ export const dict = {
|
||||
"error.page.report.prefix": "يرجى الإبلاغ عن هذا الخطأ لفريق OpenCode",
|
||||
"error.page.report.discord": "على Discord",
|
||||
"error.page.version": "الإصدار: {{version}}",
|
||||
"error.dev.rootNotFound": "لم يتم العثور على العنصر الجذري. هل نسيت إضافته إلى index.html؟ أو ربما تمت كتابة سمة id بشكل خاطئ؟",
|
||||
"error.dev.rootNotFound":
|
||||
"لم يتم العثور على العنصر الجذري. هل نسيت إضافته إلى index.html؟ أو ربما تمت كتابة سمة id بشكل خاطئ؟",
|
||||
"error.globalSync.connectFailed": "تعذر الاتصال بالخادم. هل هناك خادم يعمل في `{{url}}`؟",
|
||||
"directory.error.invalidUrl": "دليل غير صالح في عنوان URL.",
|
||||
"error.chain.unknown": "خطأ غير معروف",
|
||||
@@ -400,12 +407,13 @@ export const dict = {
|
||||
"error.chain.didYouMean": "هل كنت تعني: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "النموذج غير موجود: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "تحقق من أسماء الموفر/النموذج في التكوين (opencode.json)",
|
||||
"error.chain.mcpFailed": "فشل خادم MCP \"{{name}}\". لاحظ أن OpenCode لا يدعم مصادقة MCP بعد.",
|
||||
"error.chain.mcpFailed": 'فشل خادم MCP "{{name}}". لاحظ أن OpenCode لا يدعم مصادقة MCP بعد.',
|
||||
"error.chain.providerAuthFailed": "فشلت مصادقة الموفر ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed": "فشل تهيئة الموفر \"{{provider}}\". تحقق من بيانات الاعتماد والتكوين.",
|
||||
"error.chain.providerInitFailed": 'فشل تهيئة الموفر "{{provider}}". تحقق من بيانات الاعتماد والتكوين.',
|
||||
"error.chain.configJsonInvalid": "ملف التكوين في {{path}} ليس JSON(C) صالحًا",
|
||||
"error.chain.configJsonInvalidWithMessage": "ملف التكوين في {{path}} ليس JSON(C) صالحًا: {{message}}",
|
||||
"error.chain.configDirectoryTypo": "الدليل \"{{dir}}\" في {{path}} غير صالح. أعد تسمية الدليل إلى \"{{suggestion}}\" أو قم بإزالته. هذا خطأ مطبعي شائع.",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'الدليل "{{dir}}" في {{path}} غير صالح. أعد تسمية الدليل إلى "{{suggestion}}" أو قم بإزالته. هذا خطأ مطبعي شائع.',
|
||||
"error.chain.configFrontmatterError": "فشل تحليل frontmatter في {{path}}:\n{{message}}",
|
||||
"error.chain.configInvalid": "ملف التكوين في {{path}} غير صالح",
|
||||
"error.chain.configInvalidWithMessage": "ملف التكوين في {{path}} غير صالح: {{message}}",
|
||||
@@ -524,9 +532,10 @@ export const dict = {
|
||||
"settings.general.row.font.description": "تخصيص الخط الأحادي المستخدم في كتل التعليمات البرمجية",
|
||||
"settings.general.row.wayland.title": "استخدام Wayland الأصلي",
|
||||
"settings.general.row.wayland.description": "تعطيل التراجع إلى X11 على Wayland. يتطلب إعادة التشغيل.",
|
||||
"settings.general.row.wayland.tooltip": "على Linux مع شاشات بمعدلات تحديث مختلطة، يمكن أن يكون Wayland الأصلي أكثر استقرارًا.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"على Linux مع شاشات بمعدلات تحديث مختلطة، يمكن أن يكون Wayland الأصلي أكثر استقرارًا.",
|
||||
"settings.general.row.releaseNotes.title": "ملاحظات الإصدار",
|
||||
"settings.general.row.releaseNotes.description": "عرض نوافذ \"ما الجديد\" المنبثقة بعد التحديثات",
|
||||
"settings.general.row.releaseNotes.description": 'عرض نوافذ "ما الجديد" المنبثقة بعد التحديثات',
|
||||
"settings.updates.row.startup.title": "التحقق من التحديثات عند بدء التشغيل",
|
||||
"settings.updates.row.startup.description": "التحقق تلقائيًا من التحديثات عند تشغيل OpenCode",
|
||||
"settings.updates.row.check.title": "التحقق من التحديثات",
|
||||
@@ -647,7 +656,8 @@ export const dict = {
|
||||
"settings.permissions.tool.read.title": "قراءة",
|
||||
"settings.permissions.tool.read.description": "قراءة ملف (يطابق مسار الملف)",
|
||||
"settings.permissions.tool.edit.title": "تحرير",
|
||||
"settings.permissions.tool.edit.description": "تعديل الملفات، بما في ذلك التحرير والكتابة والتصحيحات والتحرير المتعدد",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"تعديل الملفات، بما في ذلك التحرير والكتابة والتصحيحات والتحرير المتعدد",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "مطابقة الملفات باستخدام أنماط glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
@@ -678,7 +688,7 @@ export const dict = {
|
||||
"settings.permissions.tool.doom_loop.description": "اكتشاف استدعاءات الأدوات المتكررة بمدخلات متطابقة",
|
||||
"session.delete.failed.title": "فشل حذف الجلسة",
|
||||
"session.delete.title": "حذف الجلسة",
|
||||
"session.delete.confirm": "حذف الجلسة \"{{name}}\"؟",
|
||||
"session.delete.confirm": 'حذف الجلسة "{{name}}"؟',
|
||||
"session.delete.button": "حذف الجلسة",
|
||||
"workspace.new": "مساحة عمل جديدة",
|
||||
"workspace.type.local": "محلي",
|
||||
@@ -696,14 +706,13 @@ 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": "ستتم أرشفة جلسة واحدة.",
|
||||
"workspace.reset.archived.many": "ستتم أرشفة {{count}} جلسات.",
|
||||
"workspace.reset.note": "سيؤدي هذا إلى إعادة تعيين مساحة العمل لتتطابق مع الفرع الافتراضي.",
|
||||
}
|
||||
|
||||
|
||||
+42
-25
@@ -110,25 +110,30 @@ export const dict = {
|
||||
"provider.connect.status.inProgress": "Autorização em andamento...",
|
||||
"provider.connect.status.waiting": "Aguardando autorização...",
|
||||
"provider.connect.status.failed": "Autorização falhou: {{error}}",
|
||||
"provider.connect.apiKey.description": "Digite sua chave de API do {{provider}} para conectar sua conta e usar modelos do {{provider}} no OpenCode.",
|
||||
"provider.connect.apiKey.description":
|
||||
"Digite sua chave de API do {{provider}} para conectar sua conta e usar modelos do {{provider}} no OpenCode.",
|
||||
"provider.connect.apiKey.label": "Chave de API do {{provider}}",
|
||||
"provider.connect.apiKey.placeholder": "Chave de API",
|
||||
"provider.connect.apiKey.required": "A chave de API é obrigatória",
|
||||
"provider.connect.opencodeZen.line1": "OpenCode Zen oferece acesso a um conjunto selecionado de modelos confiáveis otimizados para agentes de código.",
|
||||
"provider.connect.opencodeZen.line2": "Com uma única chave de API você terá acesso a modelos como Claude, GPT, Gemini, GLM e mais.",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen oferece acesso a um conjunto selecionado de modelos confiáveis otimizados para agentes de código.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"Com uma única chave de API você terá acesso a modelos como Claude, GPT, Gemini, GLM e mais.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "Visite ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": " para obter sua chave de API.",
|
||||
"provider.connect.oauth.code.visit.prefix": "Visite ",
|
||||
"provider.connect.oauth.code.visit.link": "este link",
|
||||
"provider.connect.oauth.code.visit.suffix": " para obter seu código de autorização e conectar sua conta para usar modelos do {{provider}} no OpenCode.",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" para obter seu código de autorização e conectar sua conta para usar modelos do {{provider}} no OpenCode.",
|
||||
"provider.connect.oauth.code.label": "Código de autorização {{method}}",
|
||||
"provider.connect.oauth.code.placeholder": "Código de autorização",
|
||||
"provider.connect.oauth.code.required": "O código de autorização é obrigatório",
|
||||
"provider.connect.oauth.code.invalid": "Código de autorização inválido",
|
||||
"provider.connect.oauth.auto.visit.prefix": "Visite ",
|
||||
"provider.connect.oauth.auto.visit.link": "este link",
|
||||
"provider.connect.oauth.auto.visit.suffix": " e digite o código abaixo para conectar sua conta e usar modelos do {{provider}} no OpenCode.",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" e digite o código abaixo para conectar sua conta e usar modelos do {{provider}} no OpenCode.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Código de confirmação",
|
||||
"provider.connect.toast.connected.title": "{{provider}} conectado",
|
||||
"provider.connect.toast.connected.description": "Modelos do {{provider}} agora estão disponíveis para uso.",
|
||||
@@ -200,7 +205,7 @@ export const dict = {
|
||||
"common.default": "Padrão",
|
||||
"common.attachment": "anexo",
|
||||
"prompt.placeholder.shell": "Digite comando do shell...",
|
||||
"prompt.placeholder.normal": "Pergunte qualquer coisa... \"{{example}}\"",
|
||||
"prompt.placeholder.normal": 'Pergunte qualquer coisa... "{{example}}"',
|
||||
"prompt.placeholder.summarizeComments": "Resumir comentários…",
|
||||
"prompt.placeholder.summarizeComment": "Resumir comentário…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
@@ -278,7 +283,8 @@ export const dict = {
|
||||
"dialog.server.add.checking": "Verificando...",
|
||||
"dialog.server.add.button": "Adicionar",
|
||||
"dialog.server.default.title": "Servidor padrão",
|
||||
"dialog.server.default.description": "Conectar a este servidor na inicialização do aplicativo ao invés de iniciar um servidor local. Requer reinicialização.",
|
||||
"dialog.server.default.description":
|
||||
"Conectar a este servidor na inicialização do aplicativo ao invés de iniciar um servidor local. Requer reinicialização.",
|
||||
"dialog.server.default.none": "Nenhum servidor selecionado",
|
||||
"dialog.server.default.set": "Definir servidor atual como padrão",
|
||||
"dialog.server.default.clear": "Limpar",
|
||||
@@ -301,7 +307,8 @@ export const dict = {
|
||||
"dialog.project.edit.worktree.startup.description": "Executa após criar um novo espaço de trabalho (worktree).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "ex: bun install",
|
||||
"context.breakdown.title": "Detalhamento do Contexto",
|
||||
"context.breakdown.note": "Detalhamento aproximado dos tokens de entrada. \"Outros\" inclui definições de ferramentas e overhead.",
|
||||
"context.breakdown.note":
|
||||
'Detalhamento aproximado dos tokens de entrada. "Outros" inclui definições de ferramentas e overhead.',
|
||||
"context.breakdown.system": "Sistema",
|
||||
"context.breakdown.user": "Usuário",
|
||||
"context.breakdown.assistant": "Assistente",
|
||||
@@ -388,7 +395,8 @@ export const dict = {
|
||||
"error.page.report.prefix": "Por favor, reporte este erro para a equipe do OpenCode",
|
||||
"error.page.report.discord": "no Discord",
|
||||
"error.page.version": "Versão: {{version}}",
|
||||
"error.dev.rootNotFound": "Elemento raiz não encontrado. Você esqueceu de adicioná-lo ao seu index.html? Ou talvez o atributo id foi escrito incorretamente?",
|
||||
"error.dev.rootNotFound":
|
||||
"Elemento raiz não encontrado. Você esqueceu de adicioná-lo ao seu index.html? Ou talvez o atributo id foi escrito incorretamente?",
|
||||
"error.globalSync.connectFailed": "Não foi possível conectar ao servidor. Há um servidor executando em `{{url}}`?",
|
||||
"directory.error.invalidUrl": "Diretório inválido na URL.",
|
||||
"error.chain.unknown": "Erro desconhecido",
|
||||
@@ -400,12 +408,15 @@ export const dict = {
|
||||
"error.chain.didYouMean": "Você quis dizer: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Modelo não encontrado: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Verifique os nomes de provedor/modelo na sua configuração (opencode.json)",
|
||||
"error.chain.mcpFailed": "Servidor MCP \"{{name}}\" falhou. Nota: OpenCode ainda não suporta autenticação MCP.",
|
||||
"error.chain.mcpFailed": 'Servidor MCP "{{name}}" falhou. Nota: OpenCode ainda não suporta autenticação MCP.',
|
||||
"error.chain.providerAuthFailed": "Autenticação do provedor falhou ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed": "Falha ao inicializar provedor \"{{provider}}\". Verifique credenciais e configuração.",
|
||||
"error.chain.providerInitFailed":
|
||||
'Falha ao inicializar provedor "{{provider}}". Verifique credenciais e configuração.',
|
||||
"error.chain.configJsonInvalid": "Arquivo de configuração em {{path}} não é um JSON(C) válido",
|
||||
"error.chain.configJsonInvalidWithMessage": "Arquivo de configuração em {{path}} não é um JSON(C) válido: {{message}}",
|
||||
"error.chain.configDirectoryTypo": "Diretório \"{{dir}}\" em {{path}} não é válido. Renomeie o diretório para \"{{suggestion}}\" ou remova-o. Este é um erro de digitação comum.",
|
||||
"error.chain.configJsonInvalidWithMessage":
|
||||
"Arquivo de configuração em {{path}} não é um JSON(C) válido: {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'Diretório "{{dir}}" em {{path}} não é válido. Renomeie o diretório para "{{suggestion}}" ou remova-o. Este é um erro de digitação comum.',
|
||||
"error.chain.configFrontmatterError": "Falha ao analisar frontmatter em {{path}}:\n{{message}}",
|
||||
"error.chain.configInvalid": "Arquivo de configuração em {{path}} é inválido",
|
||||
"error.chain.configInvalidWithMessage": "Arquivo de configuração em {{path}} é inválido: {{message}}",
|
||||
@@ -458,8 +469,10 @@ export const dict = {
|
||||
"status.popover.tab.plugins": "Plugins",
|
||||
"status.popover.action.manageServers": "Gerenciar servidores",
|
||||
"session.share.popover.title": "Publicar na web",
|
||||
"session.share.popover.description.shared": "Esta sessão é pública na web. Está acessível para qualquer pessoa com o link.",
|
||||
"session.share.popover.description.unshared": "Compartilhar sessão publicamente na web. Estará acessível para qualquer pessoa com o link.",
|
||||
"session.share.popover.description.shared":
|
||||
"Esta sessão é pública na web. Está acessível para qualquer pessoa com o link.",
|
||||
"session.share.popover.description.unshared":
|
||||
"Compartilhar sessão publicamente na web. Estará acessível para qualquer pessoa com o link.",
|
||||
"session.share.action.share": "Compartilhar",
|
||||
"session.share.action.publish": "Publicar",
|
||||
"session.share.action.publishing": "Publicando...",
|
||||
@@ -476,7 +489,8 @@ export const dict = {
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Fechar terminal",
|
||||
"terminal.connectionLost.title": "Conexão Perdida",
|
||||
"terminal.connectionLost.description": "A conexão do terminal foi interrompida. Isso pode acontecer quando o servidor reinicia.",
|
||||
"terminal.connectionLost.description":
|
||||
"A conexão do terminal foi interrompida. Isso pode acontecer quando o servidor reinicia.",
|
||||
"common.closeTab": "Fechar aba",
|
||||
"common.dismiss": "Descartar",
|
||||
"common.requestFailed": "Requisição falhou",
|
||||
@@ -524,9 +538,10 @@ export const dict = {
|
||||
"settings.general.row.font.description": "Personalize a fonte monoespaçada usada em blocos de código",
|
||||
"settings.general.row.wayland.title": "Usar Wayland nativo",
|
||||
"settings.general.row.wayland.description": "Desabilitar fallback X11 no Wayland. Requer reinicialização.",
|
||||
"settings.general.row.wayland.tooltip": "No Linux com monitores de taxas de atualização mistas, Wayland nativo pode ser mais estável.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"No Linux com monitores de taxas de atualização mistas, Wayland nativo pode ser mais estável.",
|
||||
"settings.general.row.releaseNotes.title": "Notas da versão",
|
||||
"settings.general.row.releaseNotes.description": "Mostrar pop-ups de \"Novidades\" após atualizações",
|
||||
"settings.general.row.releaseNotes.description": 'Mostrar pop-ups de "Novidades" após atualizações',
|
||||
"settings.updates.row.startup.title": "Verificar atualizações ao iniciar",
|
||||
"settings.updates.row.startup.description": "Verificar atualizações automaticamente quando o OpenCode iniciar",
|
||||
"settings.updates.row.check.title": "Verificar atualizações",
|
||||
@@ -593,9 +608,11 @@ export const dict = {
|
||||
"sound.option.yup05": "Sim 05",
|
||||
"sound.option.yup06": "Sim 06",
|
||||
"settings.general.notifications.agent.title": "Agente",
|
||||
"settings.general.notifications.agent.description": "Mostrar notificação do sistema quando o agente estiver completo ou precisar de atenção",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Mostrar notificação do sistema quando o agente estiver completo ou precisar de atenção",
|
||||
"settings.general.notifications.permissions.title": "Permissões",
|
||||
"settings.general.notifications.permissions.description": "Mostrar notificação do sistema quando uma permissão for necessária",
|
||||
"settings.general.notifications.permissions.description":
|
||||
"Mostrar notificação do sistema quando uma permissão for necessária",
|
||||
"settings.general.notifications.errors.title": "Erros",
|
||||
"settings.general.notifications.errors.description": "Mostrar notificação do sistema quando ocorrer um erro",
|
||||
"settings.general.sounds.agent.title": "Agente",
|
||||
@@ -647,7 +664,8 @@ export const dict = {
|
||||
"settings.permissions.tool.read.title": "Ler",
|
||||
"settings.permissions.tool.read.description": "Ler um arquivo (corresponde ao caminho do arquivo)",
|
||||
"settings.permissions.tool.edit.title": "Editar",
|
||||
"settings.permissions.tool.edit.description": "Modificar arquivos, incluindo edições, escritas, patches e multi-edições",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Modificar arquivos, incluindo edições, escritas, patches e multi-edições",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Corresponder arquivos usando padrões glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
@@ -678,7 +696,7 @@ export const dict = {
|
||||
"settings.permissions.tool.doom_loop.description": "Detectar chamadas de ferramentas repetidas com entrada idêntica",
|
||||
"session.delete.failed.title": "Falha ao excluir sessão",
|
||||
"session.delete.title": "Excluir sessão",
|
||||
"session.delete.confirm": "Excluir sessão \"{{name}}\"?",
|
||||
"session.delete.confirm": 'Excluir sessão "{{name}}"?',
|
||||
"session.delete.button": "Excluir sessão",
|
||||
"workspace.new": "Novo espaço de trabalho",
|
||||
"workspace.type.local": "local",
|
||||
@@ -696,14 +714,13 @@ export const dict = {
|
||||
"workspace.status.clean": "Nenhuma alteração não mesclada detectada.",
|
||||
"workspace.status.dirty": "Alterações não mescladas detectadas neste espaço de trabalho.",
|
||||
"workspace.delete.title": "Excluir espaço de trabalho",
|
||||
"workspace.delete.confirm": "Excluir espaço de trabalho \"{{name}}\"?",
|
||||
"workspace.delete.confirm": 'Excluir espaço de trabalho "{{name}}"?',
|
||||
"workspace.delete.button": "Excluir espaço de trabalho",
|
||||
"workspace.reset.title": "Redefinir espaço de trabalho",
|
||||
"workspace.reset.confirm": "Redefinir espaço de trabalho \"{{name}}\"?",
|
||||
"workspace.reset.confirm": 'Redefinir espaço de trabalho "{{name}}"?',
|
||||
"workspace.reset.button": "Redefinir espaço de trabalho",
|
||||
"workspace.reset.archived.none": "Nenhuma sessão ativa será arquivada.",
|
||||
"workspace.reset.archived.one": "1 sessão será arquivada.",
|
||||
"workspace.reset.archived.many": "{{count}} sessões serão arquivadas.",
|
||||
"workspace.reset.note": "Isso redefinirá o espaço de trabalho para corresponder ao branch padrão.",
|
||||
}
|
||||
|
||||
|
||||
+52
-29
@@ -114,25 +114,30 @@ export const dict = {
|
||||
"provider.connect.status.inProgress": "Autorisierung läuft...",
|
||||
"provider.connect.status.waiting": "Warten auf Autorisierung...",
|
||||
"provider.connect.status.failed": "Autorisierung fehlgeschlagen: {{error}}",
|
||||
"provider.connect.apiKey.description": "Geben Sie Ihren {{provider}} API-Schlüssel ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.apiKey.description":
|
||||
"Geben Sie Ihren {{provider}} API-Schlüssel ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API-Schlüssel",
|
||||
"provider.connect.apiKey.placeholder": "API-Schlüssel",
|
||||
"provider.connect.apiKey.required": "API-Schlüssel ist erforderlich",
|
||||
"provider.connect.opencodeZen.line1": "OpenCode Zen bietet Ihnen Zugriff auf eine kuratierte Auswahl zuverlässiger, optimierter Modelle für Coding-Agenten.",
|
||||
"provider.connect.opencodeZen.line2": "Mit einem einzigen API-Schlüssel erhalten Sie Zugriff auf Modelle wie Claude, GPT, Gemini, GLM und mehr.",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen bietet Ihnen Zugriff auf eine kuratierte Auswahl zuverlässiger, optimierter Modelle für Coding-Agenten.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"Mit einem einzigen API-Schlüssel erhalten Sie Zugriff auf Modelle wie Claude, GPT, Gemini, GLM und mehr.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "Besuchen Sie ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": ", um Ihren API-Schlüssel zu erhalten.",
|
||||
"provider.connect.oauth.code.visit.prefix": "Besuchen Sie ",
|
||||
"provider.connect.oauth.code.visit.link": "diesen Link",
|
||||
"provider.connect.oauth.code.visit.suffix": ", um Ihren Autorisierungscode zu erhalten, Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
", um Ihren Autorisierungscode zu erhalten, Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.oauth.code.label": "{{method}} Autorisierungscode",
|
||||
"provider.connect.oauth.code.placeholder": "Autorisierungscode",
|
||||
"provider.connect.oauth.code.required": "Autorisierungscode ist erforderlich",
|
||||
"provider.connect.oauth.code.invalid": "Ungültiger Autorisierungscode",
|
||||
"provider.connect.oauth.auto.visit.prefix": "Besuchen Sie ",
|
||||
"provider.connect.oauth.auto.visit.link": "diesen Link",
|
||||
"provider.connect.oauth.auto.visit.suffix": " und geben Sie den untenstehenden Code ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" und geben Sie den untenstehenden Code ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Bestätigungscode",
|
||||
"provider.connect.toast.connected.title": "{{provider}} verbunden",
|
||||
"provider.connect.toast.connected.description": "{{provider}} Modelle sind jetzt verfügbar.",
|
||||
@@ -149,7 +154,8 @@ export const dict = {
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "API-Schlüssel",
|
||||
"provider.custom.field.apiKey.placeholder": "API-Schlüssel",
|
||||
"provider.custom.field.apiKey.description": "Optional. Leer lassen, wenn Sie die Authentifizierung über Header verwalten.",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Optional. Leer lassen, wenn Sie die Authentifizierung über Header verwalten.",
|
||||
"provider.custom.models.label": "Modelle",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "model-id",
|
||||
@@ -204,7 +210,7 @@ export const dict = {
|
||||
"common.default": "Standard",
|
||||
"common.attachment": "Anhang",
|
||||
"prompt.placeholder.shell": "Shell-Befehl eingeben...",
|
||||
"prompt.placeholder.normal": "Fragen Sie alles... \"{{example}}\"",
|
||||
"prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"',
|
||||
"prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…",
|
||||
"prompt.placeholder.summarizeComment": "Kommentar zusammenfassen…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
@@ -252,7 +258,8 @@ export const dict = {
|
||||
"prompt.toast.pasteUnsupported.title": "Nicht unterstütztes Einfügen",
|
||||
"prompt.toast.pasteUnsupported.description": "Hier können nur Bilder oder PDFs eingefügt werden.",
|
||||
"prompt.toast.modelAgentRequired.title": "Wählen Sie einen Agenten und ein Modell",
|
||||
"prompt.toast.modelAgentRequired.description": "Wählen Sie einen Agenten und ein Modell, bevor Sie eine Eingabe senden.",
|
||||
"prompt.toast.modelAgentRequired.description":
|
||||
"Wählen Sie einen Agenten und ein Modell, bevor Sie eine Eingabe senden.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Worktree konnte nicht erstellt werden",
|
||||
"prompt.toast.sessionCreateFailed.title": "Sitzung konnte nicht erstellt werden",
|
||||
"prompt.toast.shellSendFailed.title": "Shell-Befehl konnte nicht gesendet werden",
|
||||
@@ -282,7 +289,8 @@ export const dict = {
|
||||
"dialog.server.add.checking": "Prüfen...",
|
||||
"dialog.server.add.button": "Server hinzufügen",
|
||||
"dialog.server.default.title": "Standardserver",
|
||||
"dialog.server.default.description": "Beim App-Start mit diesem Server verbinden, anstatt einen lokalen Server zu starten. Erfordert Neustart.",
|
||||
"dialog.server.default.description":
|
||||
"Beim App-Start mit diesem Server verbinden, anstatt einen lokalen Server zu starten. Erfordert Neustart.",
|
||||
"dialog.server.default.none": "Kein Server ausgewählt",
|
||||
"dialog.server.default.set": "Aktuellen Server als Standard setzen",
|
||||
"dialog.server.default.clear": "Löschen",
|
||||
@@ -302,10 +310,12 @@ export const dict = {
|
||||
"dialog.project.edit.color": "Farbe",
|
||||
"dialog.project.edit.color.select": "{{color}}-Farbe auswählen",
|
||||
"dialog.project.edit.worktree.startup": "Startup-Skript für Arbeitsbereich",
|
||||
"dialog.project.edit.worktree.startup.description": "Wird nach dem Erstellen eines neuen Arbeitsbereichs (Worktree) ausgeführt.",
|
||||
"dialog.project.edit.worktree.startup.description":
|
||||
"Wird nach dem Erstellen eines neuen Arbeitsbereichs (Worktree) ausgeführt.",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "z. B. bun install",
|
||||
"context.breakdown.title": "Kontext-Aufschlüsselung",
|
||||
"context.breakdown.note": "Ungefähre Aufschlüsselung der Eingabe-Token. \"Andere\" beinhaltet Werkzeugdefinitionen und Overhead.",
|
||||
"context.breakdown.note":
|
||||
'Ungefähre Aufschlüsselung der Eingabe-Token. "Andere" beinhaltet Werkzeugdefinitionen und Overhead.',
|
||||
"context.breakdown.system": "System",
|
||||
"context.breakdown.user": "Benutzer",
|
||||
"context.breakdown.assistant": "Assistent",
|
||||
@@ -392,7 +402,8 @@ export const dict = {
|
||||
"error.page.report.prefix": "Bitte melden Sie diesen Fehler dem OpenCode-Team",
|
||||
"error.page.report.discord": "auf Discord",
|
||||
"error.page.version": "Version: {{version}}",
|
||||
"error.dev.rootNotFound": "Wurzelelement nicht gefunden. Haben Sie vergessen, es in Ihre index.html aufzunehmen? Oder wurde das id-Attribut falsch geschrieben?",
|
||||
"error.dev.rootNotFound":
|
||||
"Wurzelelement nicht gefunden. Haben Sie vergessen, es in Ihre index.html aufzunehmen? Oder wurde das id-Attribut falsch geschrieben?",
|
||||
"error.globalSync.connectFailed": "Verbindung zum Server fehlgeschlagen. Läuft ein Server unter `{{url}}`?",
|
||||
"directory.error.invalidUrl": "Ungültiges Verzeichnis in der URL.",
|
||||
"error.chain.unknown": "Unbekannter Fehler",
|
||||
@@ -404,12 +415,16 @@ export const dict = {
|
||||
"error.chain.didYouMean": "Meinten Sie: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Modell nicht gefunden: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Überprüfen Sie Ihre Konfiguration (opencode.json) auf Anbieter-/Modellnamen",
|
||||
"error.chain.mcpFailed": "MCP-Server \"{{name}}\" fehlgeschlagen. Hinweis: OpenCode unterstützt noch keine MCP-Authentifizierung.",
|
||||
"error.chain.mcpFailed":
|
||||
'MCP-Server "{{name}}" fehlgeschlagen. Hinweis: OpenCode unterstützt noch keine MCP-Authentifizierung.',
|
||||
"error.chain.providerAuthFailed": "Anbieter-Authentifizierung fehlgeschlagen ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed": "Anbieter \"{{provider}}\" konnte nicht initialisiert werden. Überprüfen Sie Anmeldeinformationen und Konfiguration.",
|
||||
"error.chain.providerInitFailed":
|
||||
'Anbieter "{{provider}}" konnte nicht initialisiert werden. Überprüfen Sie Anmeldeinformationen und Konfiguration.',
|
||||
"error.chain.configJsonInvalid": "Konfigurationsdatei unter {{path}} ist kein gültiges JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "Konfigurationsdatei unter {{path}} ist kein gültiges JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo": "Verzeichnis \"{{dir}}\" in {{path}} ist ungültig. Benennen Sie das Verzeichnis in \"{{suggestion}}\" um oder entfernen Sie es. Dies ist ein häufiger Tippfehler.",
|
||||
"error.chain.configJsonInvalidWithMessage":
|
||||
"Konfigurationsdatei unter {{path}} ist kein gültiges JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'Verzeichnis "{{dir}}" in {{path}} ist ungültig. Benennen Sie das Verzeichnis in "{{suggestion}}" um oder entfernen Sie es. Dies ist ein häufiger Tippfehler.',
|
||||
"error.chain.configFrontmatterError": "Frontmatter in {{path}} konnte nicht geparst werden:\n{{message}}",
|
||||
"error.chain.configInvalid": "Konfigurationsdatei unter {{path}} ist ungültig",
|
||||
"error.chain.configInvalidWithMessage": "Konfigurationsdatei unter {{path}} ist ungültig: {{message}}",
|
||||
@@ -462,8 +477,10 @@ export const dict = {
|
||||
"status.popover.tab.plugins": "Plugins",
|
||||
"status.popover.action.manageServers": "Server verwalten",
|
||||
"session.share.popover.title": "Im Web veröffentlichen",
|
||||
"session.share.popover.description.shared": "Diese Sitzung ist öffentlich im Web. Sie ist für jeden mit dem Link zugänglich.",
|
||||
"session.share.popover.description.unshared": "Sitzung öffentlich im Web teilen. Sie wird für jeden mit dem Link zugänglich sein.",
|
||||
"session.share.popover.description.shared":
|
||||
"Diese Sitzung ist öffentlich im Web. Sie ist für jeden mit dem Link zugänglich.",
|
||||
"session.share.popover.description.unshared":
|
||||
"Sitzung öffentlich im Web teilen. Sie wird für jeden mit dem Link zugänglich sein.",
|
||||
"session.share.action.share": "Teilen",
|
||||
"session.share.action.publish": "Veröffentlichen",
|
||||
"session.share.action.publishing": "Veröffentliche...",
|
||||
@@ -480,7 +497,8 @@ export const dict = {
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Terminal schließen",
|
||||
"terminal.connectionLost.title": "Verbindung verloren",
|
||||
"terminal.connectionLost.description": "Die Terminalverbindung wurde unterbrochen. Das kann passieren, wenn der Server neu startet.",
|
||||
"terminal.connectionLost.description":
|
||||
"Die Terminalverbindung wurde unterbrochen. Das kann passieren, wenn der Server neu startet.",
|
||||
"common.closeTab": "Tab schließen",
|
||||
"common.dismiss": "Verwerfen",
|
||||
"common.requestFailed": "Anfrage fehlgeschlagen",
|
||||
@@ -502,7 +520,8 @@ export const dict = {
|
||||
"sidebar.workspaces.disable": "Arbeitsbereiche deaktivieren",
|
||||
"sidebar.gettingStarted.title": "Erste Schritte",
|
||||
"sidebar.gettingStarted.line1": "OpenCode enthält kostenlose Modelle, damit Sie sofort loslegen können.",
|
||||
"sidebar.gettingStarted.line2": "Verbinden Sie einen beliebigen Anbieter, um Modelle wie Claude, GPT, Gemini usw. zu nutzen.",
|
||||
"sidebar.gettingStarted.line2":
|
||||
"Verbinden Sie einen beliebigen Anbieter, um Modelle wie Claude, GPT, Gemini usw. zu nutzen.",
|
||||
"sidebar.project.recentSessions": "Letzte Sitzungen",
|
||||
"sidebar.project.viewAllSessions": "Alle Sitzungen anzeigen",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
@@ -528,9 +547,10 @@ export const dict = {
|
||||
"settings.general.row.font.description": "Die in Codeblöcken verwendete Monospace-Schriftart anpassen",
|
||||
"settings.general.row.wayland.title": "Natives Wayland verwenden",
|
||||
"settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Erfordert Neustart.",
|
||||
"settings.general.row.wayland.tooltip": "Unter Linux mit Monitoren unterschiedlicher Bildwiederholraten kann natives Wayland stabiler sein.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Unter Linux mit Monitoren unterschiedlicher Bildwiederholraten kann natives Wayland stabiler sein.",
|
||||
"settings.general.row.releaseNotes.title": "Versionshinweise",
|
||||
"settings.general.row.releaseNotes.description": "\"Neuigkeiten\"-Pop-ups nach Updates anzeigen",
|
||||
"settings.general.row.releaseNotes.description": '"Neuigkeiten"-Pop-ups nach Updates anzeigen',
|
||||
"settings.updates.row.startup.title": "Beim Start nach Updates suchen",
|
||||
"settings.updates.row.startup.description": "Beim Start von OpenCode automatisch nach Updates suchen",
|
||||
"settings.updates.row.check.title": "Nach Updates suchen",
|
||||
@@ -597,9 +617,11 @@ export const dict = {
|
||||
"sound.option.yup05": "Ja 05",
|
||||
"sound.option.yup06": "Ja 06",
|
||||
"settings.general.notifications.agent.title": "Agent",
|
||||
"settings.general.notifications.agent.description": "Systembenachrichtigung anzeigen, wenn der Agent fertig ist oder Aufmerksamkeit benötigt",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Systembenachrichtigung anzeigen, wenn der Agent fertig ist oder Aufmerksamkeit benötigt",
|
||||
"settings.general.notifications.permissions.title": "Berechtigungen",
|
||||
"settings.general.notifications.permissions.description": "Systembenachrichtigung anzeigen, wenn eine Berechtigung erforderlich ist",
|
||||
"settings.general.notifications.permissions.description":
|
||||
"Systembenachrichtigung anzeigen, wenn eine Berechtigung erforderlich ist",
|
||||
"settings.general.notifications.errors.title": "Fehler",
|
||||
"settings.general.notifications.errors.description": "Systembenachrichtigung anzeigen, wenn ein Fehler auftritt",
|
||||
"settings.general.sounds.agent.title": "Agent",
|
||||
@@ -651,7 +673,8 @@ export const dict = {
|
||||
"settings.permissions.tool.read.title": "Lesen",
|
||||
"settings.permissions.tool.read.description": "Lesen einer Datei (stimmt mit dem Dateipfad überein)",
|
||||
"settings.permissions.tool.edit.title": "Bearbeiten",
|
||||
"settings.permissions.tool.edit.description": "Dateien ändern, einschließlich Bearbeitungen, Schreibvorgängen, Patches und Mehrfachbearbeitungen",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Dateien ändern, einschließlich Bearbeitungen, Schreibvorgängen, Patches und Mehrfachbearbeitungen",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Dateien mithilfe von Glob-Mustern abgleichen",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
@@ -682,7 +705,7 @@ export const dict = {
|
||||
"settings.permissions.tool.doom_loop.description": "Wiederholte Tool-Aufrufe mit identischer Eingabe erkennen",
|
||||
"session.delete.failed.title": "Sitzung konnte nicht gelöscht werden",
|
||||
"session.delete.title": "Sitzung löschen",
|
||||
"session.delete.confirm": "Sitzung \"{{name}}\" löschen?",
|
||||
"session.delete.confirm": 'Sitzung "{{name}}" löschen?',
|
||||
"session.delete.button": "Sitzung löschen",
|
||||
"workspace.new": "Neuer Arbeitsbereich",
|
||||
"workspace.type.local": "lokal",
|
||||
@@ -700,13 +723,13 @@ export const dict = {
|
||||
"workspace.status.clean": "Keine nicht zusammengeführten Änderungen erkannt.",
|
||||
"workspace.status.dirty": "Nicht zusammengeführte Änderungen in diesem Arbeitsbereich erkannt.",
|
||||
"workspace.delete.title": "Arbeitsbereich löschen",
|
||||
"workspace.delete.confirm": "Arbeitsbereich \"{{name}}\" löschen?",
|
||||
"workspace.delete.confirm": 'Arbeitsbereich "{{name}}" löschen?',
|
||||
"workspace.delete.button": "Arbeitsbereich löschen",
|
||||
"workspace.reset.title": "Arbeitsbereich zurücksetzen",
|
||||
"workspace.reset.confirm": "Arbeitsbereich \"{{name}}\" zurücksetzen?",
|
||||
"workspace.reset.confirm": 'Arbeitsbereich "{{name}}" zurücksetzen?',
|
||||
"workspace.reset.button": "Arbeitsbereich zurücksetzen",
|
||||
"workspace.reset.archived.none": "Keine aktiven Sitzungen werden archiviert.",
|
||||
"workspace.reset.archived.one": "1 Sitzung wird archiviert.",
|
||||
"workspace.reset.archived.many": "{{count}} Sitzungen werden archiviert.",
|
||||
"workspace.reset.note": "Dadurch wird der Arbeitsbereich auf den Standard-Branch zurückgesetzt."
|
||||
"workspace.reset.note": "Dadurch wird der Arbeitsbereich auf den Standard-Branch zurückgesetzt.",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
+32
-16
@@ -110,25 +110,30 @@ export const dict = {
|
||||
"provider.connect.status.inProgress": "Autoryzacja w toku...",
|
||||
"provider.connect.status.waiting": "Oczekiwanie na autoryzację...",
|
||||
"provider.connect.status.failed": "Autoryzacja nie powiodła się: {{error}}",
|
||||
"provider.connect.apiKey.description": "Wprowadź swój klucz API {{provider}}, aby połączyć konto i używać modeli {{provider}} w OpenCode.",
|
||||
"provider.connect.apiKey.description":
|
||||
"Wprowadź swój klucz API {{provider}}, aby połączyć konto i używać modeli {{provider}} w OpenCode.",
|
||||
"provider.connect.apiKey.label": "Klucz API {{provider}}",
|
||||
"provider.connect.apiKey.placeholder": "Klucz API",
|
||||
"provider.connect.apiKey.required": "Klucz API jest wymagany",
|
||||
"provider.connect.opencodeZen.line1": "OpenCode Zen daje dostęp do wybranego zestawu niezawodnych, zoptymalizowanych modeli dla agentów kodujących.",
|
||||
"provider.connect.opencodeZen.line2": "Z jednym kluczem API uzyskasz dostęp do modeli takich jak Claude, GPT, Gemini, GLM i więcej.",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen daje dostęp do wybranego zestawu niezawodnych, zoptymalizowanych modeli dla agentów kodujących.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"Z jednym kluczem API uzyskasz dostęp do modeli takich jak Claude, GPT, Gemini, GLM i więcej.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "Odwiedź ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": ", aby odebrać swój klucz API.",
|
||||
"provider.connect.oauth.code.visit.prefix": "Odwiedź ",
|
||||
"provider.connect.oauth.code.visit.link": "ten link",
|
||||
"provider.connect.oauth.code.visit.suffix": ", aby odebrać kod autoryzacyjny, połączyć konto i używać modeli {{provider}} w OpenCode.",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
", aby odebrać kod autoryzacyjny, połączyć konto i używać modeli {{provider}} w OpenCode.",
|
||||
"provider.connect.oauth.code.label": "Kod autoryzacyjny {{method}}",
|
||||
"provider.connect.oauth.code.placeholder": "Kod autoryzacyjny",
|
||||
"provider.connect.oauth.code.required": "Kod autoryzacyjny jest wymagany",
|
||||
"provider.connect.oauth.code.invalid": "Nieprawidłowy kod autoryzacyjny",
|
||||
"provider.connect.oauth.auto.visit.prefix": "Odwiedź ",
|
||||
"provider.connect.oauth.auto.visit.link": "ten link",
|
||||
"provider.connect.oauth.auto.visit.suffix": " i wprowadź poniższy kod, aby połączyć konto i używać modeli {{provider}} w OpenCode.",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" i wprowadź poniższy kod, aby połączyć konto i używać modeli {{provider}} w OpenCode.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Kod potwierdzający",
|
||||
"provider.connect.toast.connected.title": "Połączono {{provider}}",
|
||||
"provider.connect.toast.connected.description": "Modele {{provider}} są teraz dostępne do użycia.",
|
||||
@@ -145,7 +150,8 @@ export const dict = {
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.mojdostawca.com/v1",
|
||||
"provider.custom.field.apiKey.label": "Klucz API",
|
||||
"provider.custom.field.apiKey.placeholder": "Klucz API",
|
||||
"provider.custom.field.apiKey.description": "Opcjonalne. Pozostaw puste, jeśli zarządzasz autoryzacją przez nagłówki.",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Opcjonalne. Pozostaw puste, jeśli zarządzasz autoryzacją przez nagłówki.",
|
||||
"provider.custom.models.label": "Modele",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "model-id",
|
||||
@@ -278,7 +284,8 @@ export const dict = {
|
||||
"dialog.server.add.checking": "Sprawdzanie...",
|
||||
"dialog.server.add.button": "Dodaj serwer",
|
||||
"dialog.server.default.title": "Domyślny serwer",
|
||||
"dialog.server.default.description": "Połącz z tym serwerem przy uruchomieniu aplikacji zamiast uruchamiać lokalny serwer. Wymaga restartu.",
|
||||
"dialog.server.default.description":
|
||||
"Połącz z tym serwerem przy uruchomieniu aplikacji zamiast uruchamiać lokalny serwer. Wymaga restartu.",
|
||||
"dialog.server.default.none": "Nie wybrano serwera",
|
||||
"dialog.server.default.set": "Ustaw bieżący serwer jako domyślny",
|
||||
"dialog.server.default.clear": "Wyczyść",
|
||||
@@ -388,7 +395,8 @@ export const dict = {
|
||||
"error.page.report.prefix": "Proszę zgłosić ten błąd do zespołu OpenCode",
|
||||
"error.page.report.discord": "na Discordzie",
|
||||
"error.page.version": "Wersja: {{version}}",
|
||||
"error.dev.rootNotFound": "Nie znaleziono elementu głównego. Czy zapomniałeś dodać go do swojego index.html? A może atrybut id został błędnie wpisany?",
|
||||
"error.dev.rootNotFound":
|
||||
"Nie znaleziono elementu głównego. Czy zapomniałeś dodać go do swojego index.html? A może atrybut id został błędnie wpisany?",
|
||||
"error.globalSync.connectFailed": "Nie można połączyć się z serwerem. Czy serwer działa pod adresem `{{url}}`?",
|
||||
"directory.error.invalidUrl": "Nieprawidłowy katalog w URL.",
|
||||
"error.chain.unknown": "Nieznany błąd",
|
||||
@@ -402,10 +410,12 @@ export const dict = {
|
||||
"error.chain.checkConfig": "Sprawdź swoją konfigurację (opencode.json) nazwy dostawców/modeli",
|
||||
"error.chain.mcpFailed": 'MCP server "{{name}}" failed. Note, OpenCode does not support MCP authentication yet.',
|
||||
"error.chain.providerAuthFailed": "Uwierzytelnianie dostawcy nie powiodło się ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed": 'Nie udało się zainicjować dostawcy "{{provider}}". Sprawdź poświadczenia i konfigurację.',
|
||||
"error.chain.providerInitFailed":
|
||||
'Nie udało się zainicjować dostawcy "{{provider}}". Sprawdź poświadczenia i konfigurację.',
|
||||
"error.chain.configJsonInvalid": "Plik konfiguracyjny w {{path}} nie jest poprawnym JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "Plik konfiguracyjny w {{path}} nie jest poprawnym JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo": 'Katalog "{{dir}}" w {{path}} jest nieprawidłowy. Zmień nazwę katalogu na "{{suggestion}}" lub usuń go. To częsta literówka.',
|
||||
"error.chain.configDirectoryTypo":
|
||||
'Katalog "{{dir}}" w {{path}} jest nieprawidłowy. Zmień nazwę katalogu na "{{suggestion}}" lub usuń go. To częsta literówka.',
|
||||
"error.chain.configFrontmatterError": "Nie udało się przetworzyć frontmatter w {{path}}:\n{{message}}",
|
||||
"error.chain.configInvalid": "Plik konfiguracyjny w {{path}} jest nieprawidłowy",
|
||||
"error.chain.configInvalidWithMessage": "Plik konfiguracyjny w {{path}} jest nieprawidłowy: {{message}}",
|
||||
@@ -458,8 +468,10 @@ export const dict = {
|
||||
"status.popover.tab.plugins": "Wtyczki",
|
||||
"status.popover.action.manageServers": "Zarządzaj serwerami",
|
||||
"session.share.popover.title": "Opublikuj w sieci",
|
||||
"session.share.popover.description.shared": "Ta sesja jest publiczna w sieci. Jest dostępna dla każdego, kto posiada link.",
|
||||
"session.share.popover.description.unshared": "Udostępnij sesję publicznie w sieci. Będzie dostępna dla każdego, kto posiada link.",
|
||||
"session.share.popover.description.shared":
|
||||
"Ta sesja jest publiczna w sieci. Jest dostępna dla każdego, kto posiada link.",
|
||||
"session.share.popover.description.unshared":
|
||||
"Udostępnij sesję publicznie w sieci. Będzie dostępna dla każdego, kto posiada link.",
|
||||
"session.share.action.share": "Udostępnij",
|
||||
"session.share.action.publish": "Opublikuj",
|
||||
"session.share.action.publishing": "Publikowanie...",
|
||||
@@ -476,7 +488,8 @@ export const dict = {
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Zamknij terminal",
|
||||
"terminal.connectionLost.title": "Utracono połączenie",
|
||||
"terminal.connectionLost.description": "Połączenie z terminalem zostało przerwane. Może się to zdarzyć przy restarcie serwera.",
|
||||
"terminal.connectionLost.description":
|
||||
"Połączenie z terminalem zostało przerwane. Może się to zdarzyć przy restarcie serwera.",
|
||||
"common.closeTab": "Zamknij kartę",
|
||||
"common.dismiss": "Odrzuć",
|
||||
"common.requestFailed": "Żądanie nie powiodło się",
|
||||
@@ -524,7 +537,8 @@ export const dict = {
|
||||
"settings.general.row.font.description": "Dostosuj czcionkę mono używaną w blokach kodu",
|
||||
"settings.general.row.wayland.title": "Użyj natywnego Wayland",
|
||||
"settings.general.row.wayland.description": "Wyłącz fallback X11 na Wayland. Wymaga restartu.",
|
||||
"settings.general.row.wayland.tooltip": "Na Linuxie z monitorami o różnym odświeżaniu, natywny Wayland może być bardziej stabilny.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Na Linuxie z monitorami o różnym odświeżaniu, natywny Wayland może być bardziej stabilny.",
|
||||
"settings.general.row.releaseNotes.title": "Informacje o wydaniu",
|
||||
"settings.general.row.releaseNotes.description": 'Pokazuj wyskakujące okna "Co nowego" po aktualizacjach',
|
||||
"settings.updates.row.startup.title": "Sprawdzaj aktualizacje przy uruchomieniu",
|
||||
@@ -593,9 +607,11 @@ export const dict = {
|
||||
"sound.option.yup05": "Yup 05",
|
||||
"sound.option.yup06": "Yup 06",
|
||||
"settings.general.notifications.agent.title": "Agent",
|
||||
"settings.general.notifications.agent.description": "Pokaż powiadomienie systemowe, gdy agent zakończy pracę lub wymaga uwagi",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Pokaż powiadomienie systemowe, gdy agent zakończy pracę lub wymaga uwagi",
|
||||
"settings.general.notifications.permissions.title": "Uprawnienia",
|
||||
"settings.general.notifications.permissions.description": "Pokaż powiadomienie systemowe, gdy wymagane jest uprawnienie",
|
||||
"settings.general.notifications.permissions.description":
|
||||
"Pokaż powiadomienie systemowe, gdy wymagane jest uprawnienie",
|
||||
"settings.general.notifications.errors.title": "Błędy",
|
||||
"settings.general.notifications.errors.description": "Pokaż powiadomienie systemowe, gdy wystąpi błąd",
|
||||
"settings.general.sounds.agent.title": "Agent",
|
||||
|
||||
@@ -147,7 +147,8 @@ export const dict = {
|
||||
"provider.connect.status.inProgress": "正在授权...",
|
||||
"provider.connect.status.waiting": "等待授权...",
|
||||
"provider.connect.status.failed": "授权失败:{{error}}",
|
||||
"provider.connect.apiKey.description": "输入你的 {{provider}} API 密钥以连接帐户,并在 OpenCode 中使用 {{provider}} 模型。",
|
||||
"provider.connect.apiKey.description":
|
||||
"输入你的 {{provider}} API 密钥以连接帐户,并在 OpenCode 中使用 {{provider}} 模型。",
|
||||
"provider.connect.apiKey.label": "{{provider}} API 密钥",
|
||||
"provider.connect.apiKey.placeholder": "API 密钥",
|
||||
"provider.connect.apiKey.required": "API 密钥为必填项",
|
||||
@@ -242,7 +243,7 @@ export const dict = {
|
||||
"common.attachment": "附件",
|
||||
|
||||
"prompt.placeholder.shell": "输入 shell 命令...",
|
||||
"prompt.placeholder.normal": "随便问点什么... \"{{example}}\"",
|
||||
"prompt.placeholder.normal": '随便问点什么... "{{example}}"',
|
||||
"prompt.placeholder.summarizeComments": "总结评论…",
|
||||
"prompt.placeholder.summarizeComment": "总结该评论…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
@@ -456,12 +457,13 @@ export const dict = {
|
||||
"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.mcpFailed": 'MCP 服务器 "{{name}}" 启动失败。注意: OpenCode 暂不支持 MCP 认证。',
|
||||
"error.chain.providerAuthFailed": "提供商认证失败({{provider}}):{{message}}",
|
||||
"error.chain.providerInitFailed": "无法初始化提供商 \"{{provider}}\"。请检查凭据和配置。",
|
||||
"error.chain.providerInitFailed": '无法初始化提供商 "{{provider}}"。请检查凭据和配置。',
|
||||
"error.chain.configJsonInvalid": "配置文件 {{path}} 不是有效的 JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "配置文件 {{path}} 不是有效的 JSON(C):{{message}}",
|
||||
"error.chain.configDirectoryTypo": "{{path}} 中的目录 \"{{dir}}\" 无效。请将目录重命名为 \"{{suggestion}}\" 或移除它。这是一个常见拼写错误。",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'{{path}} 中的目录 "{{dir}}" 无效。请将目录重命名为 "{{suggestion}}" 或移除它。这是一个常见拼写错误。',
|
||||
"error.chain.configFrontmatterError": "无法解析 {{path}} 中的 frontmatter:\n{{message}}",
|
||||
"error.chain.configInvalid": "配置文件 {{path}} 无效",
|
||||
"error.chain.configInvalidWithMessage": "配置文件 {{path}} 无效:{{message}}",
|
||||
@@ -761,7 +763,7 @@ export const dict = {
|
||||
|
||||
"session.delete.failed.title": "删除会话失败",
|
||||
"session.delete.title": "删除会话",
|
||||
"session.delete.confirm": "删除会话 \"{{name}}\"?",
|
||||
"session.delete.confirm": '删除会话 "{{name}}"?',
|
||||
"session.delete.button": "删除会话",
|
||||
|
||||
"workspace.new": "新建工作区",
|
||||
@@ -780,10 +782,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 个会话。",
|
||||
|
||||
@@ -1203,6 +1203,16 @@ export default function Layout(props: ParentProps) {
|
||||
|
||||
if (!result) return
|
||||
|
||||
globalSync.set(
|
||||
"project",
|
||||
produce((draft) => {
|
||||
const project = draft.find((item) => item.worktree === root)
|
||||
if (!project) return
|
||||
project.sandboxes = (project.sandboxes ?? []).filter((sandbox) => sandbox !== directory)
|
||||
}),
|
||||
)
|
||||
setStore("workspaceOrder", root, (order) => (order ?? []).filter((workspace) => workspace !== directory))
|
||||
|
||||
layout.projects.close(directory)
|
||||
layout.projects.open(root)
|
||||
|
||||
@@ -1230,6 +1240,7 @@ export default function Layout(props: ParentProps) {
|
||||
clearWorkspaceTerminals(
|
||||
directory,
|
||||
sessions.map((s) => s.id),
|
||||
platform,
|
||||
)
|
||||
await globalSDK.client.instance.dispose({ directory }).catch(() => undefined)
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { uuid } from "@/utils/uuid"
|
||||
|
||||
type Nav = {
|
||||
id: string
|
||||
dir?: string
|
||||
@@ -16,8 +18,6 @@ const key = (dir: string | undefined, to: string) => `${dir ?? ""}:${to}`
|
||||
|
||||
const now = () => performance.now()
|
||||
|
||||
const uid = () => crypto.randomUUID?.() ?? Math.random().toString(16).slice(2)
|
||||
|
||||
const navs = new Map<string, Nav>()
|
||||
const pending = new Map<string, string>()
|
||||
const active = new Map<string, string>()
|
||||
@@ -94,7 +94,7 @@ function ensure(id: string, data: Omit<Nav, "marks" | "logged" | "timer">) {
|
||||
export function navStart(input: { dir?: string; from?: string; to: string; trigger?: string }) {
|
||||
if (!dev) return
|
||||
|
||||
const id = uid()
|
||||
const id = uuid()
|
||||
const start = now()
|
||||
const nav = ensure(id, { ...input, id, start })
|
||||
nav.marks["navigate:start"] = start
|
||||
@@ -109,7 +109,7 @@ export function navParams(input: { dir?: string; from?: string; to: string }) {
|
||||
const k = key(input.dir, input.to)
|
||||
const pendingId = pending.get(k)
|
||||
if (pendingId) pending.delete(k)
|
||||
const id = pendingId ?? uid()
|
||||
const id = pendingId ?? uuid()
|
||||
|
||||
const start = now()
|
||||
const nav = ensure(id, { ...input, id, start, trigger: pendingId ? "key" : "route" })
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { Platform, usePlatform } from "@/context/platform"
|
||||
import { makePersisted, type AsyncStorage, type SyncStorage } from "@solid-primitives/storage"
|
||||
import { checksum } from "@opencode-ai/util/encode"
|
||||
import { createResource, type Accessor } from "solid-js"
|
||||
@@ -318,9 +318,8 @@ export const Persist = {
|
||||
},
|
||||
}
|
||||
|
||||
export function removePersisted(target: { storage?: string; key: string }) {
|
||||
const platform = usePlatform()
|
||||
const isDesktop = platform.platform === "desktop" && !!platform.storage
|
||||
export function removePersisted(target: { storage?: string; key: string }, platform?: Platform) {
|
||||
const isDesktop = platform?.platform === "desktop" && !!platform.storage
|
||||
|
||||
if (isDesktop) {
|
||||
return platform.storage?.(target.storage)?.removeItem(target.key)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import { uuid } from "./uuid"
|
||||
|
||||
const cryptoDescriptor = Object.getOwnPropertyDescriptor(globalThis, "crypto")
|
||||
const secureDescriptor = Object.getOwnPropertyDescriptor(globalThis, "isSecureContext")
|
||||
const randomDescriptor = Object.getOwnPropertyDescriptor(Math, "random")
|
||||
|
||||
const setCrypto = (value: Partial<Crypto>) => {
|
||||
Object.defineProperty(globalThis, "crypto", {
|
||||
configurable: true,
|
||||
value: value as Crypto,
|
||||
})
|
||||
}
|
||||
|
||||
const setSecure = (value: boolean) => {
|
||||
Object.defineProperty(globalThis, "isSecureContext", {
|
||||
configurable: true,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
const setRandom = (value: () => number) => {
|
||||
Object.defineProperty(Math, "random", {
|
||||
configurable: true,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
if (cryptoDescriptor) {
|
||||
Object.defineProperty(globalThis, "crypto", cryptoDescriptor)
|
||||
}
|
||||
|
||||
if (secureDescriptor) {
|
||||
Object.defineProperty(globalThis, "isSecureContext", secureDescriptor)
|
||||
}
|
||||
|
||||
if (!secureDescriptor) {
|
||||
delete (globalThis as { isSecureContext?: boolean }).isSecureContext
|
||||
}
|
||||
|
||||
if (randomDescriptor) {
|
||||
Object.defineProperty(Math, "random", randomDescriptor)
|
||||
}
|
||||
})
|
||||
|
||||
describe("uuid", () => {
|
||||
test("uses randomUUID in secure contexts", () => {
|
||||
setCrypto({ randomUUID: () => "00000000-0000-0000-0000-000000000000" })
|
||||
setSecure(true)
|
||||
expect(uuid()).toBe("00000000-0000-0000-0000-000000000000")
|
||||
})
|
||||
|
||||
test("falls back in insecure contexts", () => {
|
||||
setCrypto({ randomUUID: () => "00000000-0000-0000-0000-000000000000" })
|
||||
setSecure(false)
|
||||
setRandom(() => 0.5)
|
||||
expect(uuid()).toBe("8")
|
||||
})
|
||||
|
||||
test("falls back when randomUUID throws", () => {
|
||||
setCrypto({
|
||||
randomUUID: () => {
|
||||
throw new DOMException("Failed", "OperationError")
|
||||
},
|
||||
})
|
||||
setSecure(true)
|
||||
setRandom(() => 0.5)
|
||||
expect(uuid()).toBe("8")
|
||||
})
|
||||
|
||||
test("falls back when randomUUID is unavailable", () => {
|
||||
setCrypto({})
|
||||
setSecure(true)
|
||||
setRandom(() => 0.5)
|
||||
expect(uuid()).toBe("8")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
const fallback = () => Math.random().toString(16).slice(2)
|
||||
|
||||
export function uuid() {
|
||||
const c = globalThis.crypto
|
||||
if (!c || typeof c.randomUUID !== "function") return fallback()
|
||||
if (typeof globalThis.isSecureContext === "boolean" && !globalThis.isSecureContext) return fallback()
|
||||
try {
|
||||
return c.randomUUID()
|
||||
} catch {
|
||||
return fallback()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 697 B |
@@ -0,0 +1,18 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(30, 0)">
|
||||
<g clip-path="url(#clip0_1401_86283)">
|
||||
<mask id="mask0_1401_86283" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="240" height="300">
|
||||
<path d="M240 0H0V300H240V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1401_86283)">
|
||||
<path d="M180 240H60V120H180V240Z" fill="#4B4646"/>
|
||||
<path d="M180 60H60V240H180V60ZM240 300H0V0H240V300Z" fill="#F1ECEC"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1401_86283">
|
||||
<rect width="240" height="300" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 631 B |
Binary file not shown.
|
After Width: | Height: | Size: 697 B |
@@ -0,0 +1,18 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(30, 0)">
|
||||
<g clip-path="url(#clip0_1401_86274)">
|
||||
<mask id="mask0_1401_86274" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="240" height="300">
|
||||
<path d="M240 0H0V300H240V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1401_86274)">
|
||||
<path d="M180 240H60V120H180V240Z" fill="#CFCECD"/>
|
||||
<path d="M180 60H60V240H180V60ZM240 300H0V0H240V300Z" fill="#211E1E"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1401_86274">
|
||||
<rect width="240" height="300" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 631 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -7,18 +7,24 @@ import { useI18n } from "~/context/i18n"
|
||||
import { LocaleLinks } from "~/component/locale-links"
|
||||
import previewLogoLight from "../../asset/brand/preview-opencode-logo-light.png"
|
||||
import previewLogoDark from "../../asset/brand/preview-opencode-logo-dark.png"
|
||||
import previewLogoLightSquare from "../../asset/brand/preview-opencode-logo-light-square.png"
|
||||
import previewLogoDarkSquare from "../../asset/brand/preview-opencode-logo-dark-square.png"
|
||||
import previewWordmarkLight from "../../asset/brand/preview-opencode-wordmark-light.png"
|
||||
import previewWordmarkDark from "../../asset/brand/preview-opencode-wordmark-dark.png"
|
||||
import previewWordmarkSimpleLight from "../../asset/brand/preview-opencode-wordmark-simple-light.png"
|
||||
import previewWordmarkSimpleDark from "../../asset/brand/preview-opencode-wordmark-simple-dark.png"
|
||||
import logoLightPng from "../../asset/brand/opencode-logo-light.png"
|
||||
import logoDarkPng from "../../asset/brand/opencode-logo-dark.png"
|
||||
import logoLightSquarePng from "../../asset/brand/opencode-logo-light-square.png"
|
||||
import logoDarkSquarePng from "../../asset/brand/opencode-logo-dark-square.png"
|
||||
import wordmarkLightPng from "../../asset/brand/opencode-wordmark-light.png"
|
||||
import wordmarkDarkPng from "../../asset/brand/opencode-wordmark-dark.png"
|
||||
import wordmarkSimpleLightPng from "../../asset/brand/opencode-wordmark-simple-light.png"
|
||||
import wordmarkSimpleDarkPng from "../../asset/brand/opencode-wordmark-simple-dark.png"
|
||||
import logoLightSvg from "../../asset/brand/opencode-logo-light.svg"
|
||||
import logoDarkSvg from "../../asset/brand/opencode-logo-dark.svg"
|
||||
import logoLightSquareSvg from "../../asset/brand/opencode-logo-light-square.svg"
|
||||
import logoDarkSquareSvg from "../../asset/brand/opencode-logo-dark-square.svg"
|
||||
import wordmarkLightSvg from "../../asset/brand/opencode-wordmark-light.svg"
|
||||
import wordmarkDarkSvg from "../../asset/brand/opencode-wordmark-dark.svg"
|
||||
import wordmarkSimpleLightSvg from "../../asset/brand/opencode-wordmark-simple-light.svg"
|
||||
@@ -135,6 +141,60 @@ export default function Brand() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img src={previewLogoLightSquare} alt="OpenCode brand guidelines" />
|
||||
<div data-component="actions">
|
||||
<button onClick={() => downloadFile(logoLightSquarePng, "opencode-logo-light-square.png")}>
|
||||
PNG
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="square"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button onClick={() => downloadFile(logoLightSquareSvg, "opencode-logo-light-square.svg")}>
|
||||
SVG
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="square"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img src={previewLogoDarkSquare} alt="OpenCode brand guidelines" />
|
||||
<div data-component="actions">
|
||||
<button onClick={() => downloadFile(logoDarkSquarePng, "opencode-logo-dark-square.png")}>
|
||||
PNG
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="square"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button onClick={() => downloadFile(logoDarkSquareSvg, "opencode-logo-dark-square.svg")}>
|
||||
SVG
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="square"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img src={previewWordmarkLight} alt="OpenCode brand guidelines" />
|
||||
<div data-component="actions">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"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.59",
|
||||
"version": "1.1.60",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.1.59"
|
||||
version = "1.1.60"
|
||||
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.59/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.60/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.59/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.60/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.59/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.60/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.59/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.60/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.59/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.60/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"$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.59",
|
||||
"version": "1.1.60",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
@@ -84,8 +84,8 @@
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "1.5.4",
|
||||
"@opentui/core": "0.1.77",
|
||||
"@opentui/solid": "0.1.77",
|
||||
"@opentui/core": "0.1.79",
|
||||
"@opentui/solid": "0.1.79",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@pierre/diffs": "catalog:",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Clipboard } from "@tui/util/clipboard"
|
||||
import { TextAttributes } from "@opentui/core"
|
||||
import { RouteProvider, useRoute } from "@tui/context/route"
|
||||
import { Switch, Match, createEffect, untrack, ErrorBoundary, createSignal, onMount, batch, Show, on } from "solid-js"
|
||||
import { win32DisableProcessedInput, win32FlushInputBuffer, win32InstallCtrlCGuard } from "./win32"
|
||||
import { Installation } from "@/installation"
|
||||
import { Flag } from "@/flag/flag"
|
||||
import { DialogProvider, useDialog } from "@tui/ui/dialog"
|
||||
@@ -110,8 +111,17 @@ export function tui(input: {
|
||||
}) {
|
||||
// promise to prevent immediate exit
|
||||
return new Promise<void>(async (resolve) => {
|
||||
const unguard = win32InstallCtrlCGuard()
|
||||
win32DisableProcessedInput()
|
||||
|
||||
const mode = await getTerminalBackgroundColor()
|
||||
|
||||
// Re-clear after getTerminalBackgroundColor() — setRawMode(false) restores
|
||||
// the original console mode which re-enables ENABLE_PROCESSED_INPUT.
|
||||
win32DisableProcessedInput()
|
||||
|
||||
const onExit = async () => {
|
||||
unguard?.()
|
||||
await input.onExit?.()
|
||||
resolve()
|
||||
}
|
||||
@@ -730,7 +740,8 @@ function ErrorComponent(props: {
|
||||
const handleExit = async () => {
|
||||
renderer.setTerminalTitle("")
|
||||
renderer.destroy()
|
||||
props.onExit()
|
||||
win32FlushInputBuffer()
|
||||
await props.onExit()
|
||||
}
|
||||
|
||||
useKeyboard((evt) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cmd } from "../cmd"
|
||||
import { tui } from "./app"
|
||||
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
|
||||
|
||||
export const AttachCommand = cmd({
|
||||
command: "attach <url>",
|
||||
@@ -26,27 +27,34 @@ export const AttachCommand = cmd({
|
||||
describe: "basic auth password (defaults to OPENCODE_SERVER_PASSWORD)",
|
||||
}),
|
||||
handler: async (args) => {
|
||||
const directory = (() => {
|
||||
if (!args.dir) return undefined
|
||||
try {
|
||||
process.chdir(args.dir)
|
||||
return process.cwd()
|
||||
} catch {
|
||||
// If the directory doesn't exist locally (remote attach), pass it through.
|
||||
return args.dir
|
||||
}
|
||||
})()
|
||||
const headers = (() => {
|
||||
const password = args.password ?? process.env.OPENCODE_SERVER_PASSWORD
|
||||
if (!password) return undefined
|
||||
const auth = `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`
|
||||
return { Authorization: auth }
|
||||
})()
|
||||
await tui({
|
||||
url: args.url,
|
||||
args: { sessionID: args.session },
|
||||
directory,
|
||||
headers,
|
||||
})
|
||||
const unguard = win32InstallCtrlCGuard()
|
||||
try {
|
||||
win32DisableProcessedInput()
|
||||
|
||||
const directory = (() => {
|
||||
if (!args.dir) return undefined
|
||||
try {
|
||||
process.chdir(args.dir)
|
||||
return process.cwd()
|
||||
} catch {
|
||||
// If the directory doesn't exist locally (remote attach), pass it through.
|
||||
return args.dir
|
||||
}
|
||||
})()
|
||||
const headers = (() => {
|
||||
const password = args.password ?? process.env.OPENCODE_SERVER_PASSWORD
|
||||
if (!password) return undefined
|
||||
const auth = `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`
|
||||
return { Authorization: auth }
|
||||
})()
|
||||
await tui({
|
||||
url: args.url,
|
||||
args: { sessionID: args.session },
|
||||
directory,
|
||||
headers,
|
||||
})
|
||||
} finally {
|
||||
unguard?.()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRenderer } from "@opentui/solid"
|
||||
import { createSimpleContext } from "./helper"
|
||||
import { FormatError, FormatUnknownError } from "@/cli/error"
|
||||
import { win32FlushInputBuffer } from "../win32"
|
||||
type Exit = ((reason?: unknown) => Promise<void>) & {
|
||||
message: {
|
||||
set: (value?: string) => () => void
|
||||
@@ -32,6 +33,7 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({
|
||||
// Reset window title before destroying renderer
|
||||
renderer.setTerminalTitle("")
|
||||
renderer.destroy()
|
||||
win32FlushInputBuffer()
|
||||
await input.onExit?.()
|
||||
if (reason) {
|
||||
const formatted = FormatError(reason) ?? FormatUnknownError(reason)
|
||||
|
||||
@@ -149,6 +149,7 @@ export function Session() {
|
||||
const [showDetails, setShowDetails] = kv.signal("tool_details_visibility", true)
|
||||
const [showAssistantMetadata, setShowAssistantMetadata] = kv.signal("assistant_metadata_visibility", true)
|
||||
const [showScrollbar, setShowScrollbar] = kv.signal("scrollbar_visible", false)
|
||||
const [showHeader, setShowHeader] = kv.signal("header_visible", true)
|
||||
const [diffWrapMode] = kv.signal<"word" | "none">("diff_wrap_mode", "word")
|
||||
const [animationsEnabled, setAnimationsEnabled] = kv.signal("animations_enabled", true)
|
||||
|
||||
@@ -582,6 +583,15 @@ export function Session() {
|
||||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: showHeader() ? "Hide header" : "Show header",
|
||||
value: "session.toggle.header",
|
||||
category: "Session",
|
||||
onSelect: (dialog) => {
|
||||
setShowHeader((prev) => !prev)
|
||||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Page up",
|
||||
value: "session.page.up",
|
||||
@@ -963,7 +973,7 @@ export function Session() {
|
||||
<box flexDirection="row">
|
||||
<box flexGrow={1} paddingBottom={1} paddingTop={1} paddingLeft={2} paddingRight={2} gap={1}>
|
||||
<Show when={session()}>
|
||||
<Show when={!sidebarVisible() || !wide()}>
|
||||
<Show when={showHeader() && (!sidebarVisible() || !wide())}>
|
||||
<Header />
|
||||
</Show>
|
||||
<scrollbox
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Log } from "@/util/log"
|
||||
import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network"
|
||||
import type { Event } from "@opencode-ai/sdk/v2"
|
||||
import type { EventSource } from "./context/sdk"
|
||||
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
|
||||
|
||||
declare global {
|
||||
const OPENCODE_WORKER_PATH: string
|
||||
@@ -77,99 +78,111 @@ export const TuiThreadCommand = cmd({
|
||||
describe: "agent to use",
|
||||
}),
|
||||
handler: async (args) => {
|
||||
if (args.fork && !args.continue && !args.session) {
|
||||
UI.error("--fork requires --continue or --session")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Resolve relative paths against PWD to preserve behavior when using --cwd flag
|
||||
const baseCwd = process.env.PWD ?? process.cwd()
|
||||
const cwd = args.project ? path.resolve(baseCwd, args.project) : process.cwd()
|
||||
const localWorker = new URL("./worker.ts", import.meta.url)
|
||||
const distWorker = new URL("./cli/cmd/tui/worker.js", import.meta.url)
|
||||
const workerPath = await iife(async () => {
|
||||
if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH
|
||||
if (await Bun.file(distWorker).exists()) return distWorker
|
||||
return localWorker
|
||||
})
|
||||
// Keep ENABLE_PROCESSED_INPUT cleared even if other code flips it.
|
||||
// (Important when running under `bun run` wrappers on Windows.)
|
||||
const unguard = win32InstallCtrlCGuard()
|
||||
try {
|
||||
process.chdir(cwd)
|
||||
} catch (e) {
|
||||
UI.error("Failed to change directory to " + cwd)
|
||||
return
|
||||
// Must be the very first thing — disables CTRL_C_EVENT before any Worker
|
||||
// spawn or async work so the OS cannot kill the process group.
|
||||
win32DisableProcessedInput()
|
||||
|
||||
if (args.fork && !args.continue && !args.session) {
|
||||
UI.error("--fork requires --continue or --session")
|
||||
process.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
// Resolve relative paths against PWD to preserve behavior when using --cwd flag
|
||||
const baseCwd = process.env.PWD ?? process.cwd()
|
||||
const cwd = args.project ? path.resolve(baseCwd, args.project) : process.cwd()
|
||||
const localWorker = new URL("./worker.ts", import.meta.url)
|
||||
const distWorker = new URL("./cli/cmd/tui/worker.js", import.meta.url)
|
||||
const workerPath = await iife(async () => {
|
||||
if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH
|
||||
if (await Bun.file(distWorker).exists()) return distWorker
|
||||
return localWorker
|
||||
})
|
||||
try {
|
||||
process.chdir(cwd)
|
||||
} catch (e) {
|
||||
UI.error("Failed to change directory to " + cwd)
|
||||
return
|
||||
}
|
||||
|
||||
const worker = new Worker(workerPath, {
|
||||
env: Object.fromEntries(
|
||||
Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined),
|
||||
),
|
||||
})
|
||||
worker.onerror = (e) => {
|
||||
Log.Default.error(e)
|
||||
}
|
||||
const client = Rpc.client<typeof rpc>(worker)
|
||||
process.on("uncaughtException", (e) => {
|
||||
Log.Default.error(e)
|
||||
})
|
||||
process.on("unhandledRejection", (e) => {
|
||||
Log.Default.error(e)
|
||||
})
|
||||
process.on("SIGUSR2", async () => {
|
||||
await client.call("reload", undefined)
|
||||
})
|
||||
|
||||
const prompt = await iife(async () => {
|
||||
const piped = !process.stdin.isTTY ? await Bun.stdin.text() : undefined
|
||||
if (!args.prompt) return piped
|
||||
return piped ? piped + "\n" + args.prompt : args.prompt
|
||||
})
|
||||
|
||||
// Check if server should be started (port or hostname explicitly set in CLI or config)
|
||||
const networkOpts = await resolveNetworkOptions(args)
|
||||
const shouldStartServer =
|
||||
process.argv.includes("--port") ||
|
||||
process.argv.includes("--hostname") ||
|
||||
process.argv.includes("--mdns") ||
|
||||
networkOpts.mdns ||
|
||||
networkOpts.port !== 0 ||
|
||||
networkOpts.hostname !== "127.0.0.1"
|
||||
|
||||
let url: string
|
||||
let customFetch: typeof fetch | undefined
|
||||
let events: EventSource | undefined
|
||||
|
||||
if (shouldStartServer) {
|
||||
// Start HTTP server for external access
|
||||
const server = await client.call("server", networkOpts)
|
||||
url = server.url
|
||||
} else {
|
||||
// Use direct RPC communication (no HTTP)
|
||||
url = "http://opencode.internal"
|
||||
customFetch = createWorkerFetch(client)
|
||||
events = createEventSource(client)
|
||||
}
|
||||
|
||||
const tuiPromise = tui({
|
||||
url,
|
||||
fetch: customFetch,
|
||||
events,
|
||||
args: {
|
||||
continue: args.continue,
|
||||
sessionID: args.session,
|
||||
agent: args.agent,
|
||||
model: args.model,
|
||||
prompt,
|
||||
fork: args.fork,
|
||||
},
|
||||
onExit: async () => {
|
||||
await client.call("shutdown", undefined)
|
||||
},
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
client.call("checkUpgrade", { directory: cwd }).catch(() => {})
|
||||
}, 1000)
|
||||
|
||||
await tuiPromise
|
||||
} finally {
|
||||
unguard?.()
|
||||
}
|
||||
|
||||
const worker = new Worker(workerPath, {
|
||||
env: Object.fromEntries(
|
||||
Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined),
|
||||
),
|
||||
})
|
||||
worker.onerror = (e) => {
|
||||
Log.Default.error(e)
|
||||
}
|
||||
const client = Rpc.client<typeof rpc>(worker)
|
||||
process.on("uncaughtException", (e) => {
|
||||
Log.Default.error(e)
|
||||
})
|
||||
process.on("unhandledRejection", (e) => {
|
||||
Log.Default.error(e)
|
||||
})
|
||||
process.on("SIGUSR2", async () => {
|
||||
await client.call("reload", undefined)
|
||||
})
|
||||
|
||||
const prompt = await iife(async () => {
|
||||
const piped = !process.stdin.isTTY ? await Bun.stdin.text() : undefined
|
||||
if (!args.prompt) return piped
|
||||
return piped ? piped + "\n" + args.prompt : args.prompt
|
||||
})
|
||||
|
||||
// Check if server should be started (port or hostname explicitly set in CLI or config)
|
||||
const networkOpts = await resolveNetworkOptions(args)
|
||||
const shouldStartServer =
|
||||
process.argv.includes("--port") ||
|
||||
process.argv.includes("--hostname") ||
|
||||
process.argv.includes("--mdns") ||
|
||||
networkOpts.mdns ||
|
||||
networkOpts.port !== 0 ||
|
||||
networkOpts.hostname !== "127.0.0.1"
|
||||
|
||||
let url: string
|
||||
let customFetch: typeof fetch | undefined
|
||||
let events: EventSource | undefined
|
||||
|
||||
if (shouldStartServer) {
|
||||
// Start HTTP server for external access
|
||||
const server = await client.call("server", networkOpts)
|
||||
url = server.url
|
||||
} else {
|
||||
// Use direct RPC communication (no HTTP)
|
||||
url = "http://opencode.internal"
|
||||
customFetch = createWorkerFetch(client)
|
||||
events = createEventSource(client)
|
||||
}
|
||||
|
||||
const tuiPromise = tui({
|
||||
url,
|
||||
fetch: customFetch,
|
||||
events,
|
||||
args: {
|
||||
continue: args.continue,
|
||||
sessionID: args.session,
|
||||
agent: args.agent,
|
||||
model: args.model,
|
||||
prompt,
|
||||
fork: args.fork,
|
||||
},
|
||||
onExit: async () => {
|
||||
await client.call("shutdown", undefined)
|
||||
},
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
client.call("checkUpgrade", { directory: cwd }).catch(() => {})
|
||||
}, 1000)
|
||||
|
||||
await tuiPromise
|
||||
},
|
||||
})
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import { dlopen, ptr } from "bun:ffi"
|
||||
|
||||
const STD_INPUT_HANDLE = -10
|
||||
const ENABLE_PROCESSED_INPUT = 0x0001
|
||||
|
||||
const kernel = () =>
|
||||
dlopen("kernel32.dll", {
|
||||
GetStdHandle: { args: ["i32"], returns: "ptr" },
|
||||
GetConsoleMode: { args: ["ptr", "ptr"], returns: "i32" },
|
||||
SetConsoleMode: { args: ["ptr", "u32"], returns: "i32" },
|
||||
FlushConsoleInputBuffer: { args: ["ptr"], returns: "i32" },
|
||||
})
|
||||
|
||||
let k32: ReturnType<typeof kernel> | undefined
|
||||
|
||||
function load() {
|
||||
if (process.platform !== "win32") return false
|
||||
try {
|
||||
k32 ??= kernel()
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear ENABLE_PROCESSED_INPUT on the console stdin handle.
|
||||
*/
|
||||
export function win32DisableProcessedInput() {
|
||||
if (process.platform !== "win32") return
|
||||
if (!process.stdin.isTTY) return
|
||||
if (!load()) return
|
||||
|
||||
const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE)
|
||||
const buf = new Uint32Array(1)
|
||||
if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return
|
||||
|
||||
const mode = buf[0]!
|
||||
if ((mode & ENABLE_PROCESSED_INPUT) === 0) return
|
||||
k32!.symbols.SetConsoleMode(handle, mode & ~ENABLE_PROCESSED_INPUT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard any queued console input (mouse events, key presses, etc.).
|
||||
*/
|
||||
export function win32FlushInputBuffer() {
|
||||
if (process.platform !== "win32") return
|
||||
if (!process.stdin.isTTY) return
|
||||
if (!load()) return
|
||||
|
||||
const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE)
|
||||
k32!.symbols.FlushConsoleInputBuffer(handle)
|
||||
}
|
||||
|
||||
let unhook: (() => void) | undefined
|
||||
|
||||
/**
|
||||
* Keep ENABLE_PROCESSED_INPUT disabled.
|
||||
*
|
||||
* On Windows, Ctrl+C becomes a CTRL_C_EVENT (instead of stdin input) when
|
||||
* ENABLE_PROCESSED_INPUT is set. Various runtimes can re-apply console modes
|
||||
* (sometimes on a later tick), and the flag is console-global, not per-process.
|
||||
*
|
||||
* We combine:
|
||||
* - A `setRawMode(...)` hook to re-clear after known raw-mode toggles.
|
||||
* - A low-frequency poll as a backstop for native/external mode changes.
|
||||
*/
|
||||
export function win32InstallCtrlCGuard() {
|
||||
if (process.platform !== "win32") return
|
||||
if (!process.stdin.isTTY) return
|
||||
if (!load()) return
|
||||
if (unhook) return unhook
|
||||
|
||||
const stdin = process.stdin as any
|
||||
const original = stdin.setRawMode
|
||||
|
||||
const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE)
|
||||
const buf = new Uint32Array(1)
|
||||
|
||||
if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return
|
||||
const initial = buf[0]!
|
||||
|
||||
const enforce = () => {
|
||||
if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return
|
||||
const mode = buf[0]!
|
||||
if ((mode & ENABLE_PROCESSED_INPUT) === 0) return
|
||||
k32!.symbols.SetConsoleMode(handle, mode & ~ENABLE_PROCESSED_INPUT)
|
||||
}
|
||||
|
||||
// Some runtimes can re-apply console modes on the next tick; enforce twice.
|
||||
const later = () => {
|
||||
enforce()
|
||||
setImmediate(enforce)
|
||||
}
|
||||
|
||||
let wrapped: ((mode: boolean) => unknown) | undefined
|
||||
|
||||
if (typeof original === "function") {
|
||||
wrapped = (mode: boolean) => {
|
||||
const result = original.call(stdin, mode)
|
||||
later()
|
||||
return result
|
||||
}
|
||||
|
||||
stdin.setRawMode = wrapped
|
||||
}
|
||||
|
||||
// Ensure it's cleared immediately too (covers any earlier mode changes).
|
||||
later()
|
||||
|
||||
const interval = setInterval(enforce, 100)
|
||||
interval.unref()
|
||||
|
||||
let done = false
|
||||
unhook = () => {
|
||||
if (done) return
|
||||
done = true
|
||||
|
||||
clearInterval(interval)
|
||||
if (wrapped && stdin.setRawMode === wrapped) {
|
||||
stdin.setRawMode = original
|
||||
}
|
||||
|
||||
k32!.symbols.SetConsoleMode(handle, initial)
|
||||
unhook = undefined
|
||||
}
|
||||
|
||||
return unhook
|
||||
}
|
||||
@@ -571,25 +571,28 @@ export namespace MCP {
|
||||
const clientsSnapshot = await clients()
|
||||
const defaultTimeout = cfg.experimental?.mcp_timeout
|
||||
|
||||
for (const [clientName, client] of Object.entries(clientsSnapshot)) {
|
||||
// Only include tools from connected MCPs (skip disabled ones)
|
||||
if (s.status[clientName]?.status !== "connected") {
|
||||
continue
|
||||
}
|
||||
const connectedClients = Object.entries(clientsSnapshot).filter(
|
||||
([clientName]) => s.status[clientName]?.status === "connected",
|
||||
)
|
||||
|
||||
const toolsResult = await client.listTools().catch((e) => {
|
||||
log.error("failed to get tools", { clientName, error: e.message })
|
||||
const failedStatus = {
|
||||
status: "failed" as const,
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
}
|
||||
s.status[clientName] = failedStatus
|
||||
delete s.clients[clientName]
|
||||
return undefined
|
||||
})
|
||||
if (!toolsResult) {
|
||||
continue
|
||||
}
|
||||
const toolsResults = await Promise.all(
|
||||
connectedClients.map(async ([clientName, client]) => {
|
||||
const toolsResult = await client.listTools().catch((e) => {
|
||||
log.error("failed to get tools", { clientName, error: e.message })
|
||||
const failedStatus = {
|
||||
status: "failed" as const,
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
}
|
||||
s.status[clientName] = failedStatus
|
||||
delete s.clients[clientName]
|
||||
return undefined
|
||||
})
|
||||
return { clientName, client, toolsResult }
|
||||
}),
|
||||
)
|
||||
|
||||
for (const { clientName, client, toolsResult } of toolsResults) {
|
||||
if (!toolsResult) continue
|
||||
const mcpConfig = config[clientName]
|
||||
const entry = isMcpConfigured(mcpConfig) ? mcpConfig : undefined
|
||||
const timeout = entry?.timeout ?? defaultTimeout
|
||||
|
||||
@@ -64,7 +64,7 @@ export namespace ModelsDev {
|
||||
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
|
||||
options: z.record(z.string(), z.any()),
|
||||
headers: z.record(z.string(), z.string()).optional(),
|
||||
provider: z.object({ npm: z.string() }).optional(),
|
||||
provider: z.object({ npm: z.string(), api: z.string() }).optional(),
|
||||
variants: z.record(z.string(), z.record(z.string(), z.any())).optional(),
|
||||
})
|
||||
export type Model = z.infer<typeof Model>
|
||||
|
||||
@@ -629,7 +629,7 @@ export namespace Provider {
|
||||
family: model.family,
|
||||
api: {
|
||||
id: model.id,
|
||||
url: provider.api!,
|
||||
url: model.provider?.api ?? provider.api!,
|
||||
npm: model.provider?.npm ?? provider.npm ?? "@ai-sdk/openai-compatible",
|
||||
},
|
||||
status: model.status ?? "active",
|
||||
@@ -781,7 +781,7 @@ export namespace Provider {
|
||||
existingModel?.api.npm ??
|
||||
modelsDev[providerID]?.npm ??
|
||||
"@ai-sdk/openai-compatible",
|
||||
url: provider?.api ?? existingModel?.api.url ?? modelsDev[providerID]?.api,
|
||||
url: model.provider?.api ?? provider?.api ?? existingModel?.api.url ?? modelsDev[providerID]?.api,
|
||||
},
|
||||
status: model.status ?? existingModel?.status ?? "active",
|
||||
name,
|
||||
|
||||
@@ -401,6 +401,8 @@ export namespace ProviderTransform {
|
||||
// https://v5.ai-sdk.dev/providers/ai-sdk-providers/xai
|
||||
case "@ai-sdk/deepinfra":
|
||||
// https://v5.ai-sdk.dev/providers/ai-sdk-providers/deepinfra
|
||||
case "venice-ai-sdk-provider":
|
||||
// https://docs.venice.ai/overview/guides/reasoning-models#reasoning-effort
|
||||
case "@ai-sdk/openai-compatible":
|
||||
return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
|
||||
|
||||
|
||||
@@ -41,7 +41,9 @@ export namespace SessionCompaction {
|
||||
|
||||
const reserved =
|
||||
config.compaction?.reserved ?? Math.min(COMPACTION_BUFFER, ProviderTransform.maxOutputTokens(input.model))
|
||||
const usable = input.model.limit.input ? input.model.limit.input - reserved : context - reserved
|
||||
const usable = input.model.limit.input
|
||||
? input.model.limit.input - reserved
|
||||
: context - ProviderTransform.maxOutputTokens(input.model)
|
||||
return count >= usable
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ export namespace LLM {
|
||||
small?: boolean
|
||||
tools: Record<string, Tool>
|
||||
retries?: number
|
||||
toolChoice?: "auto" | "required" | "none"
|
||||
}
|
||||
|
||||
export type StreamOutput = StreamTextResult<ToolSet, unknown>
|
||||
@@ -205,6 +206,7 @@ export namespace LLM {
|
||||
providerOptions: ProviderTransform.providerOptions(input.model, params.options),
|
||||
activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
|
||||
tools,
|
||||
toolChoice: input.toolChoice,
|
||||
maxOutputTokens,
|
||||
abortSignal: input.abort,
|
||||
headers: {
|
||||
|
||||
@@ -15,6 +15,13 @@ import type { Provider } from "@/provider/provider"
|
||||
export namespace MessageV2 {
|
||||
export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({}))
|
||||
export const AbortedError = NamedError.create("MessageAbortedError", z.object({ message: z.string() }))
|
||||
export const StructuredOutputError = NamedError.create(
|
||||
"StructuredOutputError",
|
||||
z.object({
|
||||
message: z.string(),
|
||||
retries: z.number(),
|
||||
}),
|
||||
)
|
||||
export const AuthError = NamedError.create(
|
||||
"ProviderAuthError",
|
||||
z.object({
|
||||
@@ -39,6 +46,29 @@ export namespace MessageV2 {
|
||||
z.object({ message: z.string(), responseBody: z.string().optional() }),
|
||||
)
|
||||
|
||||
export const OutputFormatText = z
|
||||
.object({
|
||||
type: z.literal("text"),
|
||||
})
|
||||
.meta({
|
||||
ref: "OutputFormatText",
|
||||
})
|
||||
|
||||
export const OutputFormatJsonSchema = z
|
||||
.object({
|
||||
type: z.literal("json_schema"),
|
||||
schema: z.record(z.string(), z.any()).meta({ ref: "JSONSchema" }),
|
||||
retryCount: z.number().int().min(0).default(2),
|
||||
})
|
||||
.meta({
|
||||
ref: "OutputFormatJsonSchema",
|
||||
})
|
||||
|
||||
export const Format = z.discriminatedUnion("type", [OutputFormatText, OutputFormatJsonSchema]).meta({
|
||||
ref: "OutputFormat",
|
||||
})
|
||||
export type OutputFormat = z.infer<typeof Format>
|
||||
|
||||
const PartBase = z.object({
|
||||
id: z.string(),
|
||||
sessionID: z.string(),
|
||||
@@ -313,6 +343,7 @@ export namespace MessageV2 {
|
||||
time: z.object({
|
||||
created: z.number(),
|
||||
}),
|
||||
format: Format.optional(),
|
||||
summary: z
|
||||
.object({
|
||||
title: z.string().optional(),
|
||||
@@ -365,6 +396,7 @@ export namespace MessageV2 {
|
||||
NamedError.Unknown.Schema,
|
||||
OutputLengthError.Schema,
|
||||
AbortedError.Schema,
|
||||
StructuredOutputError.Schema,
|
||||
ContextOverflowError.Schema,
|
||||
APIError.Schema,
|
||||
])
|
||||
@@ -393,6 +425,7 @@ export namespace MessageV2 {
|
||||
write: z.number(),
|
||||
}),
|
||||
}),
|
||||
structured: z.any().optional(),
|
||||
variant: z.string().optional(),
|
||||
finish: z.string().optional(),
|
||||
}).meta({
|
||||
|
||||
@@ -50,6 +50,16 @@ import { Truncate } from "@/tool/truncation"
|
||||
// @ts-ignore
|
||||
globalThis.AI_SDK_LOG_WARNINGS = false
|
||||
|
||||
const STRUCTURED_OUTPUT_DESCRIPTION = `Use this tool to return your final response in the requested structured format.
|
||||
|
||||
IMPORTANT:
|
||||
- You MUST call this tool exactly once at the end of your response
|
||||
- The input must be valid JSON matching the required schema
|
||||
- Complete all necessary research and tool calls BEFORE calling this tool
|
||||
- This tool provides your final answer - no further actions are taken after calling it`
|
||||
|
||||
const STRUCTURED_OUTPUT_SYSTEM_PROMPT = `IMPORTANT: The user has requested structured output. You MUST use the StructuredOutput tool to provide your final response. Do NOT respond with plain text - you MUST call the StructuredOutput tool with your answer formatted according to the schema.`
|
||||
|
||||
export namespace SessionPrompt {
|
||||
const log = Log.create({ service: "session.prompt" })
|
||||
|
||||
@@ -96,6 +106,7 @@ export namespace SessionPrompt {
|
||||
.describe(
|
||||
"@deprecated tools and permissions have been merged, you can set permissions on the session itself now",
|
||||
),
|
||||
format: MessageV2.Format.optional(),
|
||||
system: z.string().optional(),
|
||||
variant: z.string().optional(),
|
||||
parts: z.array(
|
||||
@@ -276,6 +287,11 @@ export namespace SessionPrompt {
|
||||
|
||||
using _ = defer(() => cancel(sessionID))
|
||||
|
||||
// Structured output state
|
||||
// Note: On session resumption, state is reset but outputFormat is preserved
|
||||
// on the user message and will be retrieved from lastUser below
|
||||
let structuredOutput: unknown | undefined
|
||||
|
||||
let step = 0
|
||||
const session = await Session.get(sessionID)
|
||||
while (true) {
|
||||
@@ -589,6 +605,16 @@ export namespace SessionPrompt {
|
||||
messages: msgs,
|
||||
})
|
||||
|
||||
// Inject StructuredOutput tool if JSON schema mode enabled
|
||||
if (lastUser.format?.type === "json_schema") {
|
||||
tools["StructuredOutput"] = createStructuredOutputTool({
|
||||
schema: lastUser.format.schema,
|
||||
onSuccess(output) {
|
||||
structuredOutput = output
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (step === 1) {
|
||||
SessionSummary.summarize({
|
||||
sessionID: sessionID,
|
||||
@@ -619,12 +645,19 @@ export namespace SessionPrompt {
|
||||
|
||||
await Plugin.trigger("experimental.chat.messages.transform", {}, { messages: sessionMessages })
|
||||
|
||||
// Build system prompt, adding structured output instruction if needed
|
||||
const system = [...(await SystemPrompt.environment(model)), ...(await InstructionPrompt.system())]
|
||||
const format = lastUser.format ?? { type: "text" }
|
||||
if (format.type === "json_schema") {
|
||||
system.push(STRUCTURED_OUTPUT_SYSTEM_PROMPT)
|
||||
}
|
||||
|
||||
const result = await processor.process({
|
||||
user: lastUser,
|
||||
agent,
|
||||
abort,
|
||||
sessionID,
|
||||
system: [...(await SystemPrompt.environment(model)), ...(await InstructionPrompt.system())],
|
||||
system,
|
||||
messages: [
|
||||
...MessageV2.toModelMessages(sessionMessages, model),
|
||||
...(isLastStep
|
||||
@@ -638,7 +671,33 @@ export namespace SessionPrompt {
|
||||
],
|
||||
tools,
|
||||
model,
|
||||
toolChoice: format.type === "json_schema" ? "required" : undefined,
|
||||
})
|
||||
|
||||
// If structured output was captured, save it and exit immediately
|
||||
// This takes priority because the StructuredOutput tool was called successfully
|
||||
if (structuredOutput !== undefined) {
|
||||
processor.message.structured = structuredOutput
|
||||
processor.message.finish = processor.message.finish ?? "stop"
|
||||
await Session.updateMessage(processor.message)
|
||||
break
|
||||
}
|
||||
|
||||
// Check if model finished (finish reason is not "tool-calls" or "unknown")
|
||||
const modelFinished = processor.message.finish && !["tool-calls", "unknown"].includes(processor.message.finish)
|
||||
|
||||
if (modelFinished && !processor.message.error) {
|
||||
if (format.type === "json_schema") {
|
||||
// Model stopped without calling StructuredOutput tool
|
||||
processor.message.error = new MessageV2.StructuredOutputError({
|
||||
message: "Model did not produce structured output",
|
||||
retries: 0,
|
||||
}).toObject()
|
||||
await Session.updateMessage(processor.message)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (result === "stop") break
|
||||
if (result === "compact") {
|
||||
await SessionCompaction.create({
|
||||
@@ -669,7 +728,8 @@ export namespace SessionPrompt {
|
||||
return Provider.defaultModel()
|
||||
}
|
||||
|
||||
async function resolveTools(input: {
|
||||
/** @internal Exported for testing */
|
||||
export async function resolveTools(input: {
|
||||
agent: Agent.Info
|
||||
model: Provider.Model
|
||||
session: Session.Info
|
||||
@@ -849,6 +909,36 @@ export namespace SessionPrompt {
|
||||
return tools
|
||||
}
|
||||
|
||||
/** @internal Exported for testing */
|
||||
export function createStructuredOutputTool(input: {
|
||||
schema: Record<string, any>
|
||||
onSuccess: (output: unknown) => void
|
||||
}): AITool {
|
||||
// Remove $schema property if present (not needed for tool input)
|
||||
const { $schema, ...toolSchema } = input.schema
|
||||
|
||||
return tool({
|
||||
id: "StructuredOutput" as any,
|
||||
description: STRUCTURED_OUTPUT_DESCRIPTION,
|
||||
inputSchema: jsonSchema(toolSchema as any),
|
||||
async execute(args) {
|
||||
// AI SDK validates args against inputSchema before calling execute()
|
||||
input.onSuccess(args)
|
||||
return {
|
||||
output: "Structured output captured successfully.",
|
||||
title: "Structured Output",
|
||||
metadata: { valid: true },
|
||||
}
|
||||
},
|
||||
toModelOutput(result) {
|
||||
return {
|
||||
type: "text",
|
||||
value: result.output,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function createUserMessage(input: PromptInput) {
|
||||
const agent = await Agent.get(input.agent ?? (await Agent.defaultAgent()))
|
||||
|
||||
@@ -870,6 +960,7 @@ export namespace SessionPrompt {
|
||||
agent: agent.name,
|
||||
model,
|
||||
system: input.system,
|
||||
format: input.format,
|
||||
variant,
|
||||
}
|
||||
using _ = defer(() => InstructionPrompt.clear(info.id))
|
||||
@@ -1022,9 +1113,9 @@ export namespace SessionPrompt {
|
||||
}
|
||||
}
|
||||
}
|
||||
offset = Math.max(start - 1, 0)
|
||||
offset = Math.max(start, 1)
|
||||
if (end) {
|
||||
limit = end - offset
|
||||
limit = end - (offset - 1)
|
||||
}
|
||||
}
|
||||
const args = { filePath: filepath, offset, limit }
|
||||
|
||||
@@ -2,9 +2,9 @@ Performs exact string replacements in files.
|
||||
|
||||
Usage:
|
||||
- You must use your `Read` tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file.
|
||||
- When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the oldString or newString.
|
||||
- When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: line number + colon + space (e.g., `1: `). Everything after that space is the actual file content to match. Never include any part of the line number prefix in the oldString or newString.
|
||||
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
|
||||
- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.
|
||||
- The edit will FAIL if `oldString` is not found in the file with an error "oldString not found in content".
|
||||
- The edit will FAIL if `oldString` is found multiple times in the file with an error "oldString found multiple times and requires more code context to uniquely identify the intended match". Either provide a larger string with more surrounding context to make it unique or use `replaceAll` to change every instance of `oldString`.
|
||||
- The edit will FAIL if `oldString` is found multiple times in the file with an error "Found multiple matches for oldString. Provide more surrounding lines in oldString to identify the correct match." Either provide a larger string with more surrounding context to make it unique or use `replaceAll` to change every instance of `oldString`.
|
||||
- Use `replaceAll` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.
|
||||
|
||||
@@ -17,19 +17,26 @@ const MAX_BYTES = 50 * 1024
|
||||
export const ReadTool = Tool.define("read", {
|
||||
description: DESCRIPTION,
|
||||
parameters: z.object({
|
||||
filePath: z.string().describe("The path to the file to read"),
|
||||
offset: z.coerce.number().describe("The line number to start reading from (0-based)").optional(),
|
||||
limit: z.coerce.number().describe("The number of lines to read (defaults to 2000)").optional(),
|
||||
filePath: z.string().describe("The absolute path to the file or directory to read"),
|
||||
offset: z.coerce.number().describe("The line number to start reading from (1-indexed)").optional(),
|
||||
limit: z.coerce.number().describe("The maximum number of lines to read (defaults to 2000)").optional(),
|
||||
}),
|
||||
async execute(params, ctx) {
|
||||
if (params.offset !== undefined && params.offset < 1) {
|
||||
throw new Error("offset must be greater than or equal to 1")
|
||||
}
|
||||
let filepath = params.filePath
|
||||
if (!path.isAbsolute(filepath)) {
|
||||
filepath = path.resolve(Instance.directory, filepath)
|
||||
}
|
||||
const title = path.relative(Instance.worktree, filepath)
|
||||
|
||||
const file = Bun.file(filepath)
|
||||
const stat = await file.stat().catch(() => undefined)
|
||||
|
||||
await assertExternalDirectory(ctx, filepath, {
|
||||
bypass: Boolean(ctx.extra?.["bypassCwdCheck"]),
|
||||
kind: stat?.isDirectory() ? "directory" : "file",
|
||||
})
|
||||
|
||||
await ctx.ask({
|
||||
@@ -39,8 +46,7 @@ export const ReadTool = Tool.define("read", {
|
||||
metadata: {},
|
||||
})
|
||||
|
||||
const file = Bun.file(filepath)
|
||||
if (!(await file.exists())) {
|
||||
if (!stat) {
|
||||
const dir = path.dirname(filepath)
|
||||
const base = path.basename(filepath)
|
||||
|
||||
@@ -60,6 +66,48 @@ export const ReadTool = Tool.define("read", {
|
||||
throw new Error(`File not found: ${filepath}`)
|
||||
}
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const dirents = await fs.promises.readdir(filepath, { withFileTypes: true })
|
||||
const entries = await Promise.all(
|
||||
dirents.map(async (dirent) => {
|
||||
if (dirent.isDirectory()) return dirent.name + "/"
|
||||
if (dirent.isSymbolicLink()) {
|
||||
const target = await fs.promises.stat(path.join(filepath, dirent.name)).catch(() => undefined)
|
||||
if (target?.isDirectory()) return dirent.name + "/"
|
||||
}
|
||||
return dirent.name
|
||||
}),
|
||||
)
|
||||
entries.sort((a, b) => a.localeCompare(b))
|
||||
|
||||
const limit = params.limit ?? DEFAULT_READ_LIMIT
|
||||
const offset = params.offset ?? 1
|
||||
const start = offset - 1
|
||||
const sliced = entries.slice(start, start + limit)
|
||||
const truncated = start + sliced.length < entries.length
|
||||
|
||||
const output = [
|
||||
`<path>${filepath}</path>`,
|
||||
`<type>directory</type>`,
|
||||
`<entries>`,
|
||||
sliced.join("\n"),
|
||||
truncated
|
||||
? `\n(Showing ${sliced.length} of ${entries.length} entries. Use 'offset' parameter to read beyond entry ${offset + sliced.length})`
|
||||
: `\n(${entries.length} entries)`,
|
||||
`</entries>`,
|
||||
].join("\n")
|
||||
|
||||
return {
|
||||
title,
|
||||
output,
|
||||
metadata: {
|
||||
preview: sliced.slice(0, 20).join("\n"),
|
||||
truncated,
|
||||
loaded: [] as string[],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const instructions = await InstructionPrompt.resolve(ctx.messages, filepath, ctx.messageID)
|
||||
|
||||
// Exclude SVG (XML-based) and vnd.fastbidsheet (.fbs extension, commonly FlatBuffers schema files)
|
||||
@@ -75,7 +123,7 @@ export const ReadTool = Tool.define("read", {
|
||||
metadata: {
|
||||
preview: msg,
|
||||
truncated: false,
|
||||
...(instructions.length > 0 && { loaded: instructions.map((i) => i.filepath) }),
|
||||
loaded: instructions.map((i) => i.filepath),
|
||||
},
|
||||
attachments: [
|
||||
{
|
||||
@@ -94,13 +142,15 @@ export const ReadTool = Tool.define("read", {
|
||||
if (isBinary) throw new Error(`Cannot read binary file: ${filepath}`)
|
||||
|
||||
const limit = params.limit ?? DEFAULT_READ_LIMIT
|
||||
const offset = params.offset || 0
|
||||
const offset = params.offset ?? 1
|
||||
const start = offset - 1
|
||||
const lines = await file.text().then((text) => text.split("\n"))
|
||||
if (start >= lines.length) throw new Error(`Offset ${offset} is out of range for this file (${lines.length} lines)`)
|
||||
|
||||
const raw: string[] = []
|
||||
let bytes = 0
|
||||
let truncatedByBytes = false
|
||||
for (let i = offset; i < Math.min(lines.length, offset + limit); i++) {
|
||||
for (let i = start; i < Math.min(lines.length, start + limit); i++) {
|
||||
const line = lines[i].length > MAX_LINE_LENGTH ? lines[i].substring(0, MAX_LINE_LENGTH) + "..." : lines[i]
|
||||
const size = Buffer.byteLength(line, "utf-8") + (raw.length > 0 ? 1 : 0)
|
||||
if (bytes + size > MAX_BYTES) {
|
||||
@@ -112,15 +162,15 @@ export const ReadTool = Tool.define("read", {
|
||||
}
|
||||
|
||||
const content = raw.map((line, index) => {
|
||||
return `${(index + offset + 1).toString().padStart(5, "0")}| ${line}`
|
||||
return `${index + offset}: ${line}`
|
||||
})
|
||||
const preview = raw.slice(0, 20).join("\n")
|
||||
|
||||
let output = "<file>\n"
|
||||
let output = [`<path>${filepath}</path>`, `<type>file</type>`, "<content>"].join("\n")
|
||||
output += content.join("\n")
|
||||
|
||||
const totalLines = lines.length
|
||||
const lastReadLine = offset + raw.length
|
||||
const lastReadLine = offset + raw.length - 1
|
||||
const hasMoreLines = totalLines > lastReadLine
|
||||
const truncated = hasMoreLines || truncatedByBytes
|
||||
|
||||
@@ -131,7 +181,7 @@ export const ReadTool = Tool.define("read", {
|
||||
} else {
|
||||
output += `\n\n(End of file - total ${totalLines} lines)`
|
||||
}
|
||||
output += "\n</file>"
|
||||
output += "\n</content>"
|
||||
|
||||
// just warms the lsp client
|
||||
LSP.touchFile(filepath, false)
|
||||
@@ -147,7 +197,7 @@ export const ReadTool = Tool.define("read", {
|
||||
metadata: {
|
||||
preview,
|
||||
truncated,
|
||||
...(instructions.length > 0 && { loaded: instructions.map((i) => i.filepath) }),
|
||||
loaded: instructions.map((i) => i.filepath),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
Reads a file from the local filesystem. You can access any file directly by using this tool.
|
||||
Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
|
||||
Read a file or directory from the local filesystem. If the path does not exist, an error is returned.
|
||||
|
||||
Usage:
|
||||
- The filePath parameter must be an absolute path, not a relative path
|
||||
- By default, it reads up to 2000 lines starting from the beginning of the file
|
||||
- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
|
||||
- Any lines longer than 2000 characters will be truncated
|
||||
- Results are returned using cat -n format, with line numbers starting at 1
|
||||
- You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.
|
||||
- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.
|
||||
- You can read image files using this tool.
|
||||
- The filePath parameter should be an absolute path.
|
||||
- By default, this tool returns up to 2000 lines from the start of the file.
|
||||
- The offset parameter is the line number to start from (1-indexed).
|
||||
- To read later sections, call this tool again with a larger offset.
|
||||
- Use the grep tool to find specific content in large files or files with long lines.
|
||||
- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.
|
||||
- Contents are returned with each line prefixed by its line number as `<line>: <content>`. For example, if a file has contents "foo\n", you will receive "1: foo\n". For directories, entries are returned one per line (without line numbers) with a trailing `/` for subdirectories.
|
||||
- Any line longer than 2000 characters is truncated.
|
||||
- Call this tool in parallel when you know there are multiple files you want to read.
|
||||
- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.
|
||||
- This tool can read image files and PDFs and return them as file attachments.
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import path from "path"
|
||||
import { Session } from "../../src/session"
|
||||
import { SessionPrompt } from "../../src/session/prompt"
|
||||
import { Log } from "../../src/util/log"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { MessageV2 } from "../../src/session/message-v2"
|
||||
|
||||
const projectRoot = path.join(__dirname, "../..")
|
||||
Log.init({ print: false })
|
||||
|
||||
// Skip tests if no API key is available
|
||||
const hasApiKey = !!process.env.ANTHROPIC_API_KEY
|
||||
|
||||
// Helper to run test within Instance context
|
||||
async function withInstance<T>(fn: () => Promise<T>): Promise<T> {
|
||||
return Instance.provide({
|
||||
directory: projectRoot,
|
||||
fn,
|
||||
})
|
||||
}
|
||||
|
||||
describe("StructuredOutput Integration", () => {
|
||||
test.skipIf(!hasApiKey)(
|
||||
"produces structured output with simple schema",
|
||||
async () => {
|
||||
await withInstance(async () => {
|
||||
const session = await Session.create({ title: "Structured Output Test" })
|
||||
|
||||
const result = await SessionPrompt.prompt({
|
||||
sessionID: session.id,
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: "What is 2 + 2? Provide a simple answer.",
|
||||
},
|
||||
],
|
||||
format: {
|
||||
type: "json_schema",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
answer: { type: "number", description: "The numerical answer" },
|
||||
explanation: { type: "string", description: "Brief explanation" },
|
||||
},
|
||||
required: ["answer"],
|
||||
},
|
||||
retryCount: 0,
|
||||
},
|
||||
})
|
||||
|
||||
// Verify structured output was captured (only on assistant messages)
|
||||
expect(result.info.role).toBe("assistant")
|
||||
if (result.info.role === "assistant") {
|
||||
expect(result.info.structured).toBeDefined()
|
||||
expect(typeof result.info.structured).toBe("object")
|
||||
|
||||
const output = result.info.structured as any
|
||||
expect(output.answer).toBe(4)
|
||||
|
||||
// Verify no error was set
|
||||
expect(result.info.error).toBeUndefined()
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
||||
})
|
||||
},
|
||||
60000,
|
||||
)
|
||||
|
||||
test.skipIf(!hasApiKey)(
|
||||
"produces structured output with nested objects",
|
||||
async () => {
|
||||
await withInstance(async () => {
|
||||
const session = await Session.create({ title: "Nested Schema Test" })
|
||||
|
||||
const result = await SessionPrompt.prompt({
|
||||
sessionID: session.id,
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Tell me about Anthropic company in a structured format.",
|
||||
},
|
||||
],
|
||||
format: {
|
||||
type: "json_schema",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
company: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
founded: { type: "number" },
|
||||
},
|
||||
required: ["name", "founded"],
|
||||
},
|
||||
products: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
},
|
||||
},
|
||||
required: ["company"],
|
||||
},
|
||||
retryCount: 0,
|
||||
},
|
||||
})
|
||||
|
||||
// Verify structured output was captured (only on assistant messages)
|
||||
expect(result.info.role).toBe("assistant")
|
||||
if (result.info.role === "assistant") {
|
||||
expect(result.info.structured).toBeDefined()
|
||||
const output = result.info.structured as any
|
||||
|
||||
expect(output.company).toBeDefined()
|
||||
expect(output.company.name).toBe("Anthropic")
|
||||
expect(typeof output.company.founded).toBe("number")
|
||||
|
||||
if (output.products) {
|
||||
expect(Array.isArray(output.products)).toBe(true)
|
||||
}
|
||||
|
||||
// Verify no error was set
|
||||
expect(result.info.error).toBeUndefined()
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
||||
})
|
||||
},
|
||||
60000,
|
||||
)
|
||||
|
||||
test.skipIf(!hasApiKey)(
|
||||
"works with text outputFormat (default)",
|
||||
async () => {
|
||||
await withInstance(async () => {
|
||||
const session = await Session.create({ title: "Text Output Test" })
|
||||
|
||||
const result = await SessionPrompt.prompt({
|
||||
sessionID: session.id,
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Say hello.",
|
||||
},
|
||||
],
|
||||
format: {
|
||||
type: "text",
|
||||
},
|
||||
})
|
||||
|
||||
// Verify no structured output (text mode) and no error
|
||||
expect(result.info.role).toBe("assistant")
|
||||
if (result.info.role === "assistant") {
|
||||
expect(result.info.structured).toBeUndefined()
|
||||
expect(result.info.error).toBeUndefined()
|
||||
}
|
||||
|
||||
// Verify we got a response with parts
|
||||
expect(result.parts.length).toBeGreaterThan(0)
|
||||
|
||||
// Clean up
|
||||
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
||||
})
|
||||
},
|
||||
60000,
|
||||
)
|
||||
|
||||
test.skipIf(!hasApiKey)(
|
||||
"stores outputFormat on user message",
|
||||
async () => {
|
||||
await withInstance(async () => {
|
||||
const session = await Session.create({ title: "OutputFormat Storage Test" })
|
||||
|
||||
await SessionPrompt.prompt({
|
||||
sessionID: session.id,
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: "What is 1 + 1?",
|
||||
},
|
||||
],
|
||||
format: {
|
||||
type: "json_schema",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
result: { type: "number" },
|
||||
},
|
||||
required: ["result"],
|
||||
},
|
||||
retryCount: 3,
|
||||
},
|
||||
})
|
||||
|
||||
// Get all messages from session
|
||||
const messages = await Session.messages({ sessionID: session.id })
|
||||
const userMessage = messages.find((m) => m.info.role === "user")
|
||||
|
||||
// Verify outputFormat was stored on user message
|
||||
expect(userMessage).toBeDefined()
|
||||
if (userMessage?.info.role === "user") {
|
||||
expect(userMessage.info.format).toBeDefined()
|
||||
expect(userMessage.info.format?.type).toBe("json_schema")
|
||||
if (userMessage.info.format?.type === "json_schema") {
|
||||
expect(userMessage.info.format.retryCount).toBe(3)
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
||||
})
|
||||
},
|
||||
60000,
|
||||
)
|
||||
|
||||
test("unit test: StructuredOutputError is properly structured", () => {
|
||||
const error = new MessageV2.StructuredOutputError({
|
||||
message: "Failed to produce valid structured output after 3 attempts",
|
||||
retries: 3,
|
||||
})
|
||||
|
||||
expect(error.name).toBe("StructuredOutputError")
|
||||
expect(error.data.message).toContain("3 attempts")
|
||||
expect(error.data.retries).toBe(3)
|
||||
|
||||
const obj = error.toObject()
|
||||
expect(obj.name).toBe("StructuredOutputError")
|
||||
expect(obj.data.retries).toBe(3)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,385 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { MessageV2 } from "../../src/session/message-v2"
|
||||
import { SessionPrompt } from "../../src/session/prompt"
|
||||
|
||||
describe("structured-output.OutputFormat", () => {
|
||||
test("parses text format", () => {
|
||||
const result = MessageV2.Format.safeParse({ type: "text" })
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect(result.data.type).toBe("text")
|
||||
}
|
||||
})
|
||||
|
||||
test("parses json_schema format with defaults", () => {
|
||||
const result = MessageV2.Format.safeParse({
|
||||
type: "json_schema",
|
||||
schema: { type: "object", properties: { name: { type: "string" } } },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect(result.data.type).toBe("json_schema")
|
||||
if (result.data.type === "json_schema") {
|
||||
expect(result.data.retryCount).toBe(2) // default value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("parses json_schema format with custom retryCount", () => {
|
||||
const result = MessageV2.Format.safeParse({
|
||||
type: "json_schema",
|
||||
schema: { type: "object" },
|
||||
retryCount: 5,
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success && result.data.type === "json_schema") {
|
||||
expect(result.data.retryCount).toBe(5)
|
||||
}
|
||||
})
|
||||
|
||||
test("rejects invalid type", () => {
|
||||
const result = MessageV2.Format.safeParse({ type: "invalid" })
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects json_schema without schema", () => {
|
||||
const result = MessageV2.Format.safeParse({ type: "json_schema" })
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects negative retryCount", () => {
|
||||
const result = MessageV2.Format.safeParse({
|
||||
type: "json_schema",
|
||||
schema: { type: "object" },
|
||||
retryCount: -1,
|
||||
})
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("structured-output.StructuredOutputError", () => {
|
||||
test("creates error with message and retries", () => {
|
||||
const error = new MessageV2.StructuredOutputError({
|
||||
message: "Failed to validate",
|
||||
retries: 3,
|
||||
})
|
||||
|
||||
expect(error.name).toBe("StructuredOutputError")
|
||||
expect(error.data.message).toBe("Failed to validate")
|
||||
expect(error.data.retries).toBe(3)
|
||||
})
|
||||
|
||||
test("converts to object correctly", () => {
|
||||
const error = new MessageV2.StructuredOutputError({
|
||||
message: "Test error",
|
||||
retries: 2,
|
||||
})
|
||||
|
||||
const obj = error.toObject()
|
||||
expect(obj.name).toBe("StructuredOutputError")
|
||||
expect(obj.data.message).toBe("Test error")
|
||||
expect(obj.data.retries).toBe(2)
|
||||
})
|
||||
|
||||
test("isInstance correctly identifies error", () => {
|
||||
const error = new MessageV2.StructuredOutputError({
|
||||
message: "Test",
|
||||
retries: 1,
|
||||
})
|
||||
|
||||
expect(MessageV2.StructuredOutputError.isInstance(error)).toBe(true)
|
||||
expect(MessageV2.StructuredOutputError.isInstance({ name: "other" })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("structured-output.UserMessage", () => {
|
||||
test("user message accepts outputFormat", () => {
|
||||
const result = MessageV2.User.safeParse({
|
||||
id: "test-id",
|
||||
sessionID: "test-session",
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent: "default",
|
||||
model: { providerID: "anthropic", modelID: "claude-3" },
|
||||
outputFormat: {
|
||||
type: "json_schema",
|
||||
schema: { type: "object" },
|
||||
},
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("user message works without outputFormat (optional)", () => {
|
||||
const result = MessageV2.User.safeParse({
|
||||
id: "test-id",
|
||||
sessionID: "test-session",
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent: "default",
|
||||
model: { providerID: "anthropic", modelID: "claude-3" },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("structured-output.AssistantMessage", () => {
|
||||
const baseAssistantMessage = {
|
||||
id: "test-id",
|
||||
sessionID: "test-session",
|
||||
role: "assistant" as const,
|
||||
parentID: "parent-id",
|
||||
modelID: "claude-3",
|
||||
providerID: "anthropic",
|
||||
mode: "default",
|
||||
agent: "default",
|
||||
path: { cwd: "/test", root: "/test" },
|
||||
cost: 0.001,
|
||||
tokens: { input: 100, output: 50, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
time: { created: Date.now() },
|
||||
}
|
||||
|
||||
test("assistant message accepts structured", () => {
|
||||
const result = MessageV2.Assistant.safeParse({
|
||||
...baseAssistantMessage,
|
||||
structured: { company: "Anthropic", founded: 2021 },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect(result.data.structured).toEqual({ company: "Anthropic", founded: 2021 })
|
||||
}
|
||||
})
|
||||
|
||||
test("assistant message works without structured_output (optional)", () => {
|
||||
const result = MessageV2.Assistant.safeParse(baseAssistantMessage)
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("structured-output.createStructuredOutputTool", () => {
|
||||
test("creates tool with correct id", () => {
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: { type: "object", properties: { name: { type: "string" } } },
|
||||
onSuccess: () => {},
|
||||
})
|
||||
|
||||
// AI SDK tool type doesn't expose id, but we set it internally
|
||||
expect((tool as any).id).toBe("StructuredOutput")
|
||||
})
|
||||
|
||||
test("creates tool with description", () => {
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: { type: "object" },
|
||||
onSuccess: () => {},
|
||||
})
|
||||
|
||||
expect(tool.description).toContain("structured format")
|
||||
})
|
||||
|
||||
test("creates tool with schema as inputSchema", () => {
|
||||
const schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
company: { type: "string" },
|
||||
founded: { type: "number" },
|
||||
},
|
||||
required: ["company"],
|
||||
}
|
||||
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema,
|
||||
onSuccess: () => {},
|
||||
})
|
||||
|
||||
// AI SDK wraps schema in { jsonSchema: {...} }
|
||||
expect(tool.inputSchema).toBeDefined()
|
||||
const inputSchema = tool.inputSchema as any
|
||||
expect(inputSchema.jsonSchema?.properties?.company).toBeDefined()
|
||||
expect(inputSchema.jsonSchema?.properties?.founded).toBeDefined()
|
||||
})
|
||||
|
||||
test("strips $schema property from inputSchema", () => {
|
||||
const schema = {
|
||||
$schema: "http://json-schema.org/draft-07/schema#",
|
||||
type: "object",
|
||||
properties: { name: { type: "string" } },
|
||||
}
|
||||
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema,
|
||||
onSuccess: () => {},
|
||||
})
|
||||
|
||||
// AI SDK wraps schema in { jsonSchema: {...} }
|
||||
const inputSchema = tool.inputSchema as any
|
||||
expect(inputSchema.jsonSchema?.$schema).toBeUndefined()
|
||||
})
|
||||
|
||||
test("execute calls onSuccess with valid args", async () => {
|
||||
let capturedOutput: unknown
|
||||
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: { type: "object", properties: { name: { type: "string" } } },
|
||||
onSuccess: (output) => {
|
||||
capturedOutput = output
|
||||
},
|
||||
})
|
||||
|
||||
expect(tool.execute).toBeDefined()
|
||||
const testArgs = { name: "Test Company" }
|
||||
const result = await tool.execute!(testArgs, {
|
||||
toolCallId: "test-call-id",
|
||||
messages: [],
|
||||
abortSignal: undefined as any,
|
||||
})
|
||||
|
||||
expect(capturedOutput).toEqual(testArgs)
|
||||
expect(result.output).toBe("Structured output captured successfully.")
|
||||
expect(result.metadata.valid).toBe(true)
|
||||
})
|
||||
|
||||
test("AI SDK validates schema before execute - missing required field", async () => {
|
||||
// Note: The AI SDK validates the input against the schema BEFORE calling execute()
|
||||
// So invalid inputs never reach the tool's execute function
|
||||
// This test documents the expected schema behavior
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
age: { type: "number" },
|
||||
},
|
||||
required: ["name", "age"],
|
||||
},
|
||||
onSuccess: () => {},
|
||||
})
|
||||
|
||||
// The schema requires both 'name' and 'age'
|
||||
expect(tool.inputSchema).toBeDefined()
|
||||
const inputSchema = tool.inputSchema as any
|
||||
expect(inputSchema.jsonSchema?.required).toContain("name")
|
||||
expect(inputSchema.jsonSchema?.required).toContain("age")
|
||||
})
|
||||
|
||||
test("AI SDK validates schema types before execute - wrong type", async () => {
|
||||
// Note: The AI SDK validates the input against the schema BEFORE calling execute()
|
||||
// So invalid inputs never reach the tool's execute function
|
||||
// This test documents the expected schema behavior
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: { type: "number" },
|
||||
},
|
||||
required: ["count"],
|
||||
},
|
||||
onSuccess: () => {},
|
||||
})
|
||||
|
||||
// The schema defines 'count' as a number
|
||||
expect(tool.inputSchema).toBeDefined()
|
||||
const inputSchema = tool.inputSchema as any
|
||||
expect(inputSchema.jsonSchema?.properties?.count?.type).toBe("number")
|
||||
})
|
||||
|
||||
test("execute handles nested objects", async () => {
|
||||
let capturedOutput: unknown
|
||||
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
user: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
email: { type: "string" },
|
||||
},
|
||||
required: ["name"],
|
||||
},
|
||||
},
|
||||
required: ["user"],
|
||||
},
|
||||
onSuccess: (output) => {
|
||||
capturedOutput = output
|
||||
},
|
||||
})
|
||||
|
||||
// Valid nested object - AI SDK validates before calling execute()
|
||||
const validResult = await tool.execute!(
|
||||
{ user: { name: "John", email: "john@test.com" } },
|
||||
{
|
||||
toolCallId: "test-call-id",
|
||||
messages: [],
|
||||
abortSignal: undefined as any,
|
||||
},
|
||||
)
|
||||
|
||||
expect(capturedOutput).toEqual({ user: { name: "John", email: "john@test.com" } })
|
||||
expect(validResult.metadata.valid).toBe(true)
|
||||
|
||||
// Verify schema has correct nested structure
|
||||
const inputSchema = tool.inputSchema as any
|
||||
expect(inputSchema.jsonSchema?.properties?.user?.type).toBe("object")
|
||||
expect(inputSchema.jsonSchema?.properties?.user?.properties?.name?.type).toBe("string")
|
||||
expect(inputSchema.jsonSchema?.properties?.user?.required).toContain("name")
|
||||
})
|
||||
|
||||
test("execute handles arrays", async () => {
|
||||
let capturedOutput: unknown
|
||||
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
tags: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
},
|
||||
},
|
||||
required: ["tags"],
|
||||
},
|
||||
onSuccess: (output) => {
|
||||
capturedOutput = output
|
||||
},
|
||||
})
|
||||
|
||||
// Valid array - AI SDK validates before calling execute()
|
||||
const validResult = await tool.execute!(
|
||||
{ tags: ["a", "b", "c"] },
|
||||
{
|
||||
toolCallId: "test-call-id",
|
||||
messages: [],
|
||||
abortSignal: undefined as any,
|
||||
},
|
||||
)
|
||||
|
||||
expect(capturedOutput).toEqual({ tags: ["a", "b", "c"] })
|
||||
expect(validResult.metadata.valid).toBe(true)
|
||||
|
||||
// Verify schema has correct array structure
|
||||
const inputSchema = tool.inputSchema as any
|
||||
expect(inputSchema.jsonSchema?.properties?.tags?.type).toBe("array")
|
||||
expect(inputSchema.jsonSchema?.properties?.tags?.items?.type).toBe("string")
|
||||
})
|
||||
|
||||
test("toModelOutput returns text value", () => {
|
||||
const tool = SessionPrompt.createStructuredOutputTool({
|
||||
schema: { type: "object" },
|
||||
onSuccess: () => {},
|
||||
})
|
||||
|
||||
expect(tool.toModelOutput).toBeDefined()
|
||||
const modelOutput = tool.toModelOutput!({
|
||||
output: "Test output",
|
||||
title: "Test",
|
||||
metadata: { valid: true },
|
||||
})
|
||||
|
||||
expect(modelOutput.type).toBe("text")
|
||||
expect(modelOutput.value).toBe("Test output")
|
||||
})
|
||||
|
||||
// Note: Retry behavior is handled by the AI SDK and the prompt loop, not the tool itself
|
||||
// The tool simply calls onSuccess when execute() is called with valid args
|
||||
// See prompt.ts loop() for actual retry logic
|
||||
})
|
||||
@@ -78,6 +78,32 @@ describe("tool.read external_directory permission", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("asks for directory-scoped external_directory permission when reading external directory", async () => {
|
||||
await using outerTmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(path.join(dir, "external", "a.txt"), "a")
|
||||
},
|
||||
})
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const read = await ReadTool.init()
|
||||
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
||||
const testCtx = {
|
||||
...ctx,
|
||||
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
||||
requests.push(req)
|
||||
},
|
||||
}
|
||||
await read.execute({ filePath: path.join(outerTmp.path, "external") }, testCtx)
|
||||
const extDirReq = requests.find((r) => r.permission === "external_directory")
|
||||
expect(extDirReq).toBeDefined()
|
||||
expect(extDirReq!.patterns).toContain(path.join(outerTmp.path, "external", "*"))
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("asks for external_directory permission when reading relative path outside project", async () => {
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
await Instance.provide({
|
||||
@@ -232,7 +258,7 @@ describe("tool.read truncation", () => {
|
||||
test("respects offset parameter", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const lines = Array.from({ length: 20 }, (_, i) => `line${i}`).join("\n")
|
||||
const lines = Array.from({ length: 20 }, (_, i) => `line${i + 1}`).join("\n")
|
||||
await Bun.write(path.join(dir, "offset.txt"), lines)
|
||||
},
|
||||
})
|
||||
@@ -249,6 +275,43 @@ describe("tool.read truncation", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("throws when offset is beyond end of file", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const lines = Array.from({ length: 3 }, (_, i) => `line${i + 1}`).join("\n")
|
||||
await Bun.write(path.join(dir, "short.txt"), lines)
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const read = await ReadTool.init()
|
||||
await expect(
|
||||
read.execute({ filePath: path.join(tmp.path, "short.txt"), offset: 4, limit: 5 }, ctx),
|
||||
).rejects.toThrow("Offset 4 is out of range for this file (3 lines)")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("does not mark final directory page as truncated", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Promise.all(
|
||||
Array.from({ length: 10 }, (_, i) => Bun.write(path.join(dir, "dir", `file-${i + 1}.txt`), `line${i}`)),
|
||||
)
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const read = await ReadTool.init()
|
||||
const result = await read.execute({ filePath: path.join(tmp.path, "dir"), offset: 6, limit: 5 }, ctx)
|
||||
expect(result.metadata.truncated).toBe(false)
|
||||
expect(result.output).not.toContain("Showing 5 of 10 entries")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("truncates long lines", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -57,6 +57,7 @@ import type {
|
||||
McpLocalConfig,
|
||||
McpRemoteConfig,
|
||||
McpStatusResponses,
|
||||
OutputFormat,
|
||||
Part as Part2,
|
||||
PartDeleteErrors,
|
||||
PartDeleteResponses,
|
||||
@@ -1473,6 +1474,7 @@ export class Session extends HeyApiClient {
|
||||
tools?: {
|
||||
[key: string]: boolean
|
||||
}
|
||||
format?: OutputFormat
|
||||
system?: string
|
||||
variant?: string
|
||||
parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
|
||||
@@ -1491,6 +1493,7 @@ export class Session extends HeyApiClient {
|
||||
{ in: "body", key: "agent" },
|
||||
{ in: "body", key: "noReply" },
|
||||
{ in: "body", key: "tools" },
|
||||
{ in: "body", key: "format" },
|
||||
{ in: "body", key: "system" },
|
||||
{ in: "body", key: "variant" },
|
||||
{ in: "body", key: "parts" },
|
||||
@@ -1561,6 +1564,7 @@ export class Session extends HeyApiClient {
|
||||
tools?: {
|
||||
[key: string]: boolean
|
||||
}
|
||||
format?: OutputFormat
|
||||
system?: string
|
||||
variant?: string
|
||||
parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
|
||||
@@ -1579,6 +1583,7 @@ export class Session extends HeyApiClient {
|
||||
{ in: "body", key: "agent" },
|
||||
{ in: "body", key: "noReply" },
|
||||
{ in: "body", key: "tools" },
|
||||
{ in: "body", key: "format" },
|
||||
{ in: "body", key: "system" },
|
||||
{ in: "body", key: "variant" },
|
||||
{ in: "body", key: "parts" },
|
||||
|
||||
@@ -90,6 +90,22 @@ export type EventFileEdited = {
|
||||
}
|
||||
}
|
||||
|
||||
export type OutputFormatText = {
|
||||
type: "text"
|
||||
}
|
||||
|
||||
export type JsonSchema = {
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type OutputFormatJsonSchema = {
|
||||
type: "json_schema"
|
||||
schema: JsonSchema
|
||||
retryCount?: number
|
||||
}
|
||||
|
||||
export type OutputFormat = OutputFormatText | OutputFormatJsonSchema
|
||||
|
||||
export type FileDiff = {
|
||||
file: string
|
||||
before: string
|
||||
@@ -106,6 +122,7 @@ export type UserMessage = {
|
||||
time: {
|
||||
created: number
|
||||
}
|
||||
format?: OutputFormat
|
||||
summary?: {
|
||||
title?: string
|
||||
body?: string
|
||||
@@ -152,6 +169,14 @@ export type MessageAbortedError = {
|
||||
}
|
||||
}
|
||||
|
||||
export type StructuredOutputError = {
|
||||
name: "StructuredOutputError"
|
||||
data: {
|
||||
message: string
|
||||
retries: number
|
||||
}
|
||||
}
|
||||
|
||||
export type ContextOverflowError = {
|
||||
name: "ContextOverflowError"
|
||||
data: {
|
||||
@@ -189,6 +214,7 @@ export type AssistantMessage = {
|
||||
| UnknownError
|
||||
| MessageOutputLengthError
|
||||
| MessageAbortedError
|
||||
| StructuredOutputError
|
||||
| ContextOverflowError
|
||||
| ApiError
|
||||
parentID: string
|
||||
@@ -212,6 +238,7 @@ export type AssistantMessage = {
|
||||
write: number
|
||||
}
|
||||
}
|
||||
structured?: unknown
|
||||
variant?: string
|
||||
finish?: string
|
||||
}
|
||||
@@ -841,6 +868,7 @@ export type EventSessionError = {
|
||||
| UnknownError
|
||||
| MessageOutputLengthError
|
||||
| MessageAbortedError
|
||||
| StructuredOutputError
|
||||
| ContextOverflowError
|
||||
| ApiError
|
||||
}
|
||||
@@ -1521,6 +1549,7 @@ export type ProviderConfig = {
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
api: string
|
||||
}
|
||||
/**
|
||||
* Variant-specific configuration
|
||||
@@ -3402,6 +3431,7 @@ export type SessionPromptData = {
|
||||
tools?: {
|
||||
[key: string]: boolean
|
||||
}
|
||||
format?: OutputFormat
|
||||
system?: string
|
||||
variant?: string
|
||||
parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
|
||||
@@ -3589,6 +3619,7 @@ export type SessionPromptAsyncData = {
|
||||
tools?: {
|
||||
[key: string]: boolean
|
||||
}
|
||||
format?: OutputFormat
|
||||
system?: string
|
||||
variant?: string
|
||||
parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
|
||||
@@ -4038,6 +4069,7 @@ export type ProviderListResponses = {
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
api: string
|
||||
}
|
||||
variants?: {
|
||||
[key: string]: {
|
||||
|
||||
@@ -2420,6 +2420,9 @@
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"format": {
|
||||
"$ref": "#/components/schemas/OutputFormat"
|
||||
},
|
||||
"system": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2796,6 +2799,9 @@
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"format": {
|
||||
"$ref": "#/components/schemas/OutputFormat"
|
||||
},
|
||||
"system": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3790,9 +3796,12 @@
|
||||
"properties": {
|
||||
"npm": {
|
||||
"type": "string"
|
||||
},
|
||||
"api": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["npm"]
|
||||
"required": ["npm", "api"]
|
||||
},
|
||||
"variants": {
|
||||
"type": "object",
|
||||
@@ -6070,6 +6079,52 @@
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"OutputFormatText": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "text"
|
||||
}
|
||||
},
|
||||
"required": ["type"]
|
||||
},
|
||||
"JSONSchema": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"OutputFormatJsonSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "json_schema"
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/JSONSchema"
|
||||
},
|
||||
"retryCount": {
|
||||
"default": 2,
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["type", "schema"]
|
||||
},
|
||||
"OutputFormat": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/OutputFormatText"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/OutputFormatJsonSchema"
|
||||
}
|
||||
]
|
||||
},
|
||||
"FileDiff": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -6117,6 +6172,9 @@
|
||||
},
|
||||
"required": ["created"]
|
||||
},
|
||||
"format": {
|
||||
"$ref": "#/components/schemas/OutputFormat"
|
||||
},
|
||||
"summary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -6242,6 +6300,28 @@
|
||||
},
|
||||
"required": ["name", "data"]
|
||||
},
|
||||
"StructuredOutputError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"const": "StructuredOutputError"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"retries": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["message", "retries"]
|
||||
}
|
||||
},
|
||||
"required": ["name", "data"]
|
||||
},
|
||||
"ContextOverflowError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -6349,6 +6429,9 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/MessageAbortedError"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/StructuredOutputError"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ContextOverflowError"
|
||||
},
|
||||
@@ -6420,6 +6503,7 @@
|
||||
},
|
||||
"required": ["input", "output", "reasoning", "cache"]
|
||||
},
|
||||
"structured": {},
|
||||
"variant": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -8125,6 +8209,9 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/MessageAbortedError"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/StructuredOutputError"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ContextOverflowError"
|
||||
},
|
||||
@@ -9314,9 +9401,12 @@
|
||||
"properties": {
|
||||
"npm": {
|
||||
"type": "string"
|
||||
},
|
||||
"api": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["npm"]
|
||||
"required": ["npm", "api"]
|
||||
},
|
||||
"variants": {
|
||||
"description": "Variant-specific configuration",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
export const dict = {
|
||||
"ui.sessionReview.title": "تغييرات الجلسة",
|
||||
"ui.sessionReview.title.lastTurn": "تغييرات آخر دور",
|
||||
"ui.sessionReview.diffStyle.unified": "موجد",
|
||||
"ui.sessionReview.diffStyle.unified": "موحد",
|
||||
"ui.sessionReview.diffStyle.split": "منقسم",
|
||||
"ui.sessionReview.expandAll": "توسيع الكل",
|
||||
"ui.sessionReview.collapseAll": "طي الكل",
|
||||
|
||||
"ui.sessionReview.change.added": "مضاف",
|
||||
"ui.sessionReview.change.removed": "محذوف",
|
||||
"ui.sessionReview.change.modified": "معدل",
|
||||
|
||||
"ui.lineComment.label.prefix": "تعليق على ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "جارٍ التعليق على ",
|
||||
"ui.lineComment.editorLabel.suffix": "",
|
||||
"ui.lineComment.placeholder": "أضف تعليقًا",
|
||||
"ui.lineComment.submit": "تعليق",
|
||||
|
||||
"ui.sessionTurn.steps.show": "إظهار الخطوات",
|
||||
"ui.sessionTurn.steps.hide": "إخفاء الخطوات",
|
||||
"ui.sessionTurn.summary.response": "استجابة",
|
||||
|
||||
@@ -5,16 +5,17 @@ export const dict = {
|
||||
"ui.sessionReview.diffStyle.split": "Dividido",
|
||||
"ui.sessionReview.expandAll": "Expandir tudo",
|
||||
"ui.sessionReview.collapseAll": "Recolher tudo",
|
||||
|
||||
"ui.sessionReview.change.added": "Adicionado",
|
||||
"ui.sessionReview.change.removed": "Removido",
|
||||
"ui.sessionReview.change.modified": "Modificado",
|
||||
|
||||
"ui.lineComment.label.prefix": "Comentar em ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "Comentando em ",
|
||||
"ui.lineComment.editorLabel.suffix": "",
|
||||
"ui.lineComment.placeholder": "Adicionar comentário",
|
||||
"ui.lineComment.submit": "Comentar",
|
||||
|
||||
"ui.sessionTurn.steps.show": "Mostrar passos",
|
||||
"ui.sessionTurn.steps.hide": "Ocultar passos",
|
||||
"ui.sessionTurn.summary.response": "Resposta",
|
||||
@@ -94,7 +95,7 @@ export const dict = {
|
||||
"ui.patch.action.deleted": "Excluído",
|
||||
"ui.patch.action.created": "Criado",
|
||||
"ui.patch.action.moved": "Movido",
|
||||
"ui.patch.action.patched": "Aplicado patch",
|
||||
"ui.patch.action.patched": "Patch aplicado",
|
||||
|
||||
"ui.question.subtitle.answered": "{{count}} respondidas",
|
||||
"ui.question.answer.none": "(sem resposta)",
|
||||
|
||||
@@ -63,8 +63,8 @@ export const dict = {
|
||||
"ui.tool.webfetch": "Webhentning",
|
||||
"ui.tool.shell": "Shell",
|
||||
"ui.tool.patch": "Patch",
|
||||
"ui.tool.todos": "To-dos",
|
||||
"ui.tool.todos.read": "Læs to-dos",
|
||||
"ui.tool.todos": "Opgaver",
|
||||
"ui.tool.todos.read": "Læs opgaver",
|
||||
"ui.tool.questions": "Spørgsmål",
|
||||
"ui.tool.agent": "{{type}} Agent",
|
||||
|
||||
@@ -94,7 +94,7 @@ export const dict = {
|
||||
"ui.patch.action.deleted": "Slettet",
|
||||
"ui.patch.action.created": "Oprettet",
|
||||
"ui.patch.action.moved": "Flyttet",
|
||||
"ui.patch.action.patched": "Patched",
|
||||
"ui.patch.action.patched": "Patchet",
|
||||
|
||||
"ui.question.subtitle.answered": "{{count}} besvaret",
|
||||
"ui.question.answer.none": "(intet svar)",
|
||||
|
||||
@@ -5,16 +5,17 @@ export const dict = {
|
||||
"ui.sessionReview.diffStyle.split": "Dividido",
|
||||
"ui.sessionReview.expandAll": "Expandir todo",
|
||||
"ui.sessionReview.collapseAll": "Colapsar todo",
|
||||
|
||||
"ui.sessionReview.change.added": "Añadido",
|
||||
"ui.sessionReview.change.removed": "Eliminado",
|
||||
"ui.sessionReview.change.modified": "Modificado",
|
||||
|
||||
"ui.lineComment.label.prefix": "Comentar en ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "Comentando en ",
|
||||
"ui.lineComment.editorLabel.suffix": "",
|
||||
"ui.lineComment.placeholder": "Añadir comentario",
|
||||
"ui.lineComment.submit": "Comentar",
|
||||
|
||||
"ui.sessionTurn.steps.show": "Mostrar pasos",
|
||||
"ui.sessionTurn.steps.hide": "Ocultar pasos",
|
||||
"ui.sessionTurn.summary.response": "Respuesta",
|
||||
|
||||
@@ -5,16 +5,17 @@ export const dict = {
|
||||
"ui.sessionReview.diffStyle.split": "Divisé",
|
||||
"ui.sessionReview.expandAll": "Tout développer",
|
||||
"ui.sessionReview.collapseAll": "Tout réduire",
|
||||
|
||||
"ui.sessionReview.change.added": "Ajouté",
|
||||
"ui.sessionReview.change.removed": "Supprimé",
|
||||
"ui.sessionReview.change.modified": "Modifié",
|
||||
|
||||
"ui.lineComment.label.prefix": "Commenter sur ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "Commentaire sur ",
|
||||
"ui.lineComment.editorLabel.suffix": "",
|
||||
"ui.lineComment.placeholder": "Ajouter un commentaire",
|
||||
"ui.lineComment.submit": "Commenter",
|
||||
|
||||
"ui.sessionTurn.steps.show": "Afficher les étapes",
|
||||
"ui.sessionTurn.steps.hide": "Masquer les étapes",
|
||||
"ui.sessionTurn.summary.response": "Réponse",
|
||||
|
||||
@@ -5,16 +5,17 @@ export const dict = {
|
||||
"ui.sessionReview.diffStyle.split": "분할 보기",
|
||||
"ui.sessionReview.expandAll": "모두 펼치기",
|
||||
"ui.sessionReview.collapseAll": "모두 접기",
|
||||
|
||||
"ui.sessionReview.change.added": "추가됨",
|
||||
"ui.sessionReview.change.removed": "삭제됨",
|
||||
"ui.sessionReview.change.modified": "수정됨",
|
||||
|
||||
"ui.lineComment.label.prefix": "",
|
||||
"ui.lineComment.label.suffix": "에 댓글 달기",
|
||||
"ui.lineComment.editorLabel.prefix": "",
|
||||
"ui.lineComment.editorLabel.suffix": "에 댓글 작성 중",
|
||||
"ui.lineComment.placeholder": "댓글 추가",
|
||||
"ui.lineComment.submit": "댓글",
|
||||
|
||||
"ui.sessionTurn.steps.show": "단계 표시",
|
||||
"ui.sessionTurn.steps.hide": "단계 숨기기",
|
||||
"ui.sessionTurn.summary.response": "응답",
|
||||
|
||||
@@ -8,16 +8,17 @@ export const dict: Record<Keys, string> = {
|
||||
"ui.sessionReview.diffStyle.split": "Delt",
|
||||
"ui.sessionReview.expandAll": "Utvid alle",
|
||||
"ui.sessionReview.collapseAll": "Fold sammen alle",
|
||||
|
||||
"ui.sessionReview.change.added": "Lagt til",
|
||||
"ui.sessionReview.change.removed": "Fjernet",
|
||||
"ui.sessionReview.change.modified": "Endret",
|
||||
|
||||
"ui.lineComment.label.prefix": "Kommenter på ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "Kommenterer på ",
|
||||
"ui.lineComment.editorLabel.suffix": "",
|
||||
"ui.lineComment.placeholder": "Legg til kommentar",
|
||||
"ui.lineComment.submit": "Kommenter",
|
||||
|
||||
"ui.sessionTurn.steps.show": "Vis trinn",
|
||||
"ui.sessionTurn.steps.hide": "Skjul trinn",
|
||||
"ui.sessionTurn.summary.response": "Svar",
|
||||
|
||||
@@ -57,6 +57,7 @@ export const dict = {
|
||||
"ui.imagePreview.alt": "ตัวอย่างรูปภาพ",
|
||||
|
||||
"ui.tool.read": "อ่าน",
|
||||
"ui.tool.loaded": "โหลดแล้ว",
|
||||
"ui.tool.list": "รายการ",
|
||||
"ui.tool.glob": "Glob",
|
||||
"ui.tool.grep": "Grep",
|
||||
|
||||
@@ -9,16 +9,17 @@ export const dict = {
|
||||
"ui.sessionReview.diffStyle.split": "拆分",
|
||||
"ui.sessionReview.expandAll": "全部展开",
|
||||
"ui.sessionReview.collapseAll": "全部收起",
|
||||
|
||||
"ui.sessionReview.change.added": "已添加",
|
||||
"ui.sessionReview.change.removed": "已移除",
|
||||
"ui.sessionReview.change.modified": "已修改",
|
||||
|
||||
"ui.lineComment.label.prefix": "评论 ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "正在评论 ",
|
||||
"ui.lineComment.editorLabel.suffix": "",
|
||||
"ui.lineComment.placeholder": "添加评论",
|
||||
"ui.lineComment.submit": "评论",
|
||||
|
||||
"ui.sessionTurn.steps.show": "显示步骤",
|
||||
"ui.sessionTurn.steps.hide": "隐藏步骤",
|
||||
"ui.sessionTurn.summary.response": "回复",
|
||||
@@ -92,7 +93,7 @@ export const dict = {
|
||||
"ui.message.expand": "展开消息",
|
||||
"ui.message.collapse": "收起消息",
|
||||
"ui.message.copy": "复制",
|
||||
"ui.message.copied": "已复制",
|
||||
"ui.message.copied": "已复制!",
|
||||
"ui.message.attachment.alt": "附件",
|
||||
|
||||
"ui.patch.action.deleted": "已删除",
|
||||
|
||||
@@ -9,16 +9,17 @@ export const dict = {
|
||||
"ui.sessionReview.diffStyle.split": "拆分",
|
||||
"ui.sessionReview.expandAll": "全部展開",
|
||||
"ui.sessionReview.collapseAll": "全部收合",
|
||||
|
||||
"ui.sessionReview.change.added": "已新增",
|
||||
"ui.sessionReview.change.removed": "已移除",
|
||||
"ui.sessionReview.change.modified": "已修改",
|
||||
|
||||
"ui.lineComment.label.prefix": "評論 ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "正在評論 ",
|
||||
"ui.lineComment.editorLabel.suffix": "",
|
||||
"ui.lineComment.placeholder": "新增評論",
|
||||
"ui.lineComment.submit": "評論",
|
||||
|
||||
"ui.sessionTurn.steps.show": "顯示步驟",
|
||||
"ui.sessionTurn.steps.hide": "隱藏步驟",
|
||||
"ui.sessionTurn.summary.response": "回覆",
|
||||
@@ -92,7 +93,7 @@ export const dict = {
|
||||
"ui.message.expand": "展開訊息",
|
||||
"ui.message.collapse": "收合訊息",
|
||||
"ui.message.copy": "複製",
|
||||
"ui.message.copied": "已複製",
|
||||
"ui.message.copied": "已複製!",
|
||||
"ui.message.attachment.alt": "附件",
|
||||
|
||||
"ui.patch.action.deleted": "已刪除",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.1.59",
|
||||
"version": "1.1.60",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -45,7 +45,7 @@ description: هيّئ الوكلاء المتخصصين واستخدمهم.
|
||||
|
||||
---
|
||||
|
||||
### استخدام build
|
||||
### استخدام Build
|
||||
|
||||
_الوضع_: `primary`
|
||||
|
||||
@@ -53,7 +53,7 @@ Build هو الوكيل الأساسي **الافتراضي** مع تفعيل ج
|
||||
|
||||
---
|
||||
|
||||
### استخدام plan
|
||||
### استخدام Plan
|
||||
|
||||
_الوضع_: `primary`
|
||||
|
||||
@@ -67,7 +67,7 @@ _الوضع_: `primary`
|
||||
|
||||
---
|
||||
|
||||
### استخدام general
|
||||
### استخدام General
|
||||
|
||||
_الوضع_: `subagent`
|
||||
|
||||
@@ -75,7 +75,7 @@ _الوضع_: `subagent`
|
||||
|
||||
---
|
||||
|
||||
### استخدام explore
|
||||
### استخدام Explore
|
||||
|
||||
_الوضع_: `subagent`
|
||||
|
||||
@@ -83,7 +83,7 @@ _الوضع_: `subagent`
|
||||
|
||||
---
|
||||
|
||||
### استخدام compaction
|
||||
### استخدام Compaction
|
||||
|
||||
_الوضع_: `primary`
|
||||
|
||||
@@ -91,7 +91,7 @@ _الوضع_: `primary`
|
||||
|
||||
---
|
||||
|
||||
### استخدام title
|
||||
### استخدام Title
|
||||
|
||||
_الوضع_: `primary`
|
||||
|
||||
@@ -99,7 +99,7 @@ _الوضع_: `primary`
|
||||
|
||||
---
|
||||
|
||||
### استخدام summary
|
||||
### استخدام Summary
|
||||
|
||||
_الوضع_: `primary`
|
||||
|
||||
|
||||
@@ -69,10 +69,10 @@ opencode attach [url]
|
||||
يتيح ذلك استخدام واجهة TUI مع واجهة خلفية لـ OpenCode تعمل عن بعد. على سبيل المثال:
|
||||
|
||||
```bash
|
||||
# Start the backend server for web/mobile access
|
||||
# ابدأ خادم الواجهة الخلفية للوصول عبر الويب/الجوال
|
||||
opencode web --port 4096 --hostname 0.0.0.0
|
||||
|
||||
# In another terminal, attach the TUI to the running backend
|
||||
# في محطة طرفية (terminal) أخرى، اربط TUI بالواجهة الخلفية قيد التشغيل
|
||||
opencode attach http://10.20.30.40:4096
|
||||
```
|
||||
|
||||
@@ -326,10 +326,10 @@ opencode run Explain the use of context in Go
|
||||
يمكنك أيضا الإرفاق بمثيل `opencode serve` قيد التشغيل لتجنّب زمن الإقلاع البارد لخوادم MCP في كل تشغيل:
|
||||
|
||||
```bash
|
||||
# Start a headless server in one terminal
|
||||
# ابدأ خادمًا بلا واجهة في محطة طرفية واحدة
|
||||
opencode serve
|
||||
|
||||
# In another terminal, run commands that attach to it
|
||||
# في محطة طرفية أخرى، شغّل الأوامر التي ترتبط به
|
||||
opencode run --attach http://localhost:4096 "Explain async/await in JavaScript"
|
||||
```
|
||||
|
||||
|
||||
@@ -54,11 +54,11 @@ Focus on the failing tests and suggest fixes.
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"command": {
|
||||
// This becomes the name of the command
|
||||
// يصبح هذا اسم الأمر
|
||||
"test": {
|
||||
// This is the prompt that will be sent to the LLM
|
||||
// هذه هي المطالبة التي ستُرسل إلى LLM
|
||||
"template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes.",
|
||||
// This is shown as the description in the TUI
|
||||
// يظهر هذا كوصف في TUI
|
||||
"description": "Run tests with coverage",
|
||||
"agent": "build",
|
||||
"model": "anthropic/claude-3-5-sonnet-20241022"
|
||||
|
||||
@@ -14,7 +14,7 @@ description: استخدام ملف إعدادات OpenCode بصيغة JSON.
|
||||
```jsonc title="opencode.jsonc"
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
// Theme configuration
|
||||
// إعدادات السمة
|
||||
"theme": "opencode",
|
||||
"model": "anthropic/claude-sonnet-4-5",
|
||||
"autoupdate": true,
|
||||
@@ -326,7 +326,7 @@ opencode run "Hello world"
|
||||
"model": "anthropic/claude-sonnet-4-5",
|
||||
"prompt": "You are a code reviewer. Focus on security, performance, and maintainability.",
|
||||
"tools": {
|
||||
// Disable file modification tools for review-only agent
|
||||
// تعطيل أدوات تعديل الملفات لوكيل المراجعة فقط
|
||||
"write": false,
|
||||
"edit": false,
|
||||
},
|
||||
|
||||
@@ -221,7 +221,7 @@ How is authentication handled in @packages/functions/src/api/index.ts
|
||||
|
||||
انتقل إليه باستخدام مفتاح **Tab**. سترى مؤشرا لذلك في الزاوية السفلية اليمنى.
|
||||
|
||||
```bash frame="none" title="Switch to Plan mode"
|
||||
```bash frame="none" title="التبديل إلى وضع Plan"
|
||||
<TAB>
|
||||
```
|
||||
|
||||
|
||||
@@ -278,10 +278,10 @@ opencode mcp logout my-oauth-server
|
||||
إذا فشل خادم MCP بعيد في المصادقة، يمكنك تشخيص المشكلة باستخدام:
|
||||
|
||||
```bash
|
||||
# View auth status for all OAuth-capable servers
|
||||
# عرض حالة المصادقة لجميع الخوادم القادرة على OAuth
|
||||
opencode mcp auth list
|
||||
|
||||
# Debug connection and OAuth flow for a specific server
|
||||
# تصحيح الاتصال وتدفق OAuth لخادم محدد
|
||||
opencode mcp debug my-oauth-server
|
||||
```
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@ description: إعداد الوكلاء والشهادات المخصصة.
|
||||
يتبع OpenCode متغيرات بيئة الوكيل القياسية.
|
||||
|
||||
```bash
|
||||
# HTTPS proxy (recommended)
|
||||
# وكيل HTTPS (موصى به)
|
||||
export HTTPS_PROXY=https://proxy.example.com:8080
|
||||
|
||||
# HTTP proxy (if HTTPS not available)
|
||||
# وكيل HTTP (إذا لم يتوفر HTTPS)
|
||||
export HTTP_PROXY=http://proxy.example.com:8080
|
||||
|
||||
# Bypass proxy for local server (required)
|
||||
# تجاوز الوكيل للخادم المحلي (مطلوب)
|
||||
export NO_PROXY=localhost,127.0.0.1
|
||||
```
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ OpenCode Zen هي قائمة نماذج يوفّرها فريق OpenCode وقد
|
||||
فلا تتردد في فتح PR.
|
||||
|
||||
:::note
|
||||
ألا ترى مزوّدا هنا؟ أرسل PR.
|
||||
لم تجد المزوّد الذي تبحث عنه؟ أرسل PR.
|
||||
:::
|
||||
|
||||
---
|
||||
@@ -139,13 +139,13 @@ OpenCode Zen هي قائمة نماذج يوفّرها فريق OpenCode وقد
|
||||
عيّن أحد متغيرات البيئة التالية أثناء تشغيل opencode:
|
||||
|
||||
```bash
|
||||
# Option 1: Using AWS access keys
|
||||
# الخيار 1: استخدام مفاتيح وصول AWS
|
||||
AWS_ACCESS_KEY_ID=XXX AWS_SECRET_ACCESS_KEY=YYY opencode
|
||||
|
||||
# Option 2: Using named AWS profile
|
||||
# الخيار 2: استخدام ملف تعريف AWS مسمّى
|
||||
AWS_PROFILE=my-profile opencode
|
||||
|
||||
# Option 3: Using Bedrock bearer token
|
||||
# الخيار 3: استخدام رمز Bedrock المميز (bearer token)
|
||||
AWS_BEARER_TOKEN_BEDROCK=XXX opencode
|
||||
```
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ OPENCODE_ENABLE_EXA=1 opencode
|
||||
|
||||
---
|
||||
|
||||
### Ignore patterns
|
||||
### أنماط التجاهل
|
||||
|
||||
لتضمين ملفات يتم تجاهلها عادة، أنشئ ملف `.ignore` في جذر المشروع. يمكن لهذا الملف السماح صراحة بمسارات محددة.
|
||||
|
||||
|
||||
@@ -290,12 +290,12 @@ How is auth handled in @packages/functions/src/api/index.ts?
|
||||
<Tabs>
|
||||
<TabItem label="Linux/macOS">
|
||||
```bash
|
||||
# Example for nano or vim
|
||||
# مثال لـ nano أو vim
|
||||
export EDITOR=nano
|
||||
export EDITOR=vim
|
||||
|
||||
# For GUI editors, VS Code, Cursor, VSCodium, Windsurf, Zed, etc.
|
||||
# include --wait
|
||||
# للمحررات الرسومية، VS Code، Cursor، VSCodium، Windsurf، Zed، إلخ.
|
||||
# قم بتضمين --wait
|
||||
export EDITOR="code --wait"
|
||||
```
|
||||
|
||||
@@ -308,8 +308,8 @@ How is auth handled in @packages/functions/src/api/index.ts?
|
||||
```bash
|
||||
set EDITOR=notepad
|
||||
|
||||
# For GUI editors, VS Code, Cursor, VSCodium, Windsurf, Zed, etc.
|
||||
# include --wait
|
||||
# للمحررات الرسومية، VS Code، Cursor، VSCodium، Windsurf، Zed، إلخ.
|
||||
# قم بتضمين --wait
|
||||
set EDITOR=code --wait
|
||||
```
|
||||
|
||||
@@ -321,8 +321,8 @@ How is auth handled in @packages/functions/src/api/index.ts?
|
||||
```powershell
|
||||
$env:EDITOR = "notepad"
|
||||
|
||||
# For GUI editors, VS Code, Cursor, VSCodium, Windsurf, Zed, etc.
|
||||
# include --wait
|
||||
# للمحررات الرسومية، VS Code، Cursor، VSCodium، Windsurf، Zed، إلخ.
|
||||
# قم بتضمين --wait
|
||||
$env:EDITOR = "code --wait"
|
||||
```
|
||||
|
||||
|
||||
@@ -113,10 +113,10 @@ OPENCODE_SERVER_PASSWORD=secret opencode web
|
||||
يمكنك إرفاق واجهة terminal (TUI) بخادم ويب قيد التشغيل:
|
||||
|
||||
```bash
|
||||
# Start the web server
|
||||
# ابدأ خادم الويب
|
||||
opencode web --port 4096
|
||||
|
||||
# In another terminal, attach the TUI
|
||||
# في محطة طرفية أخرى، اربط TUI
|
||||
opencode attach http://localhost:4096
|
||||
```
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ ACP je otvoreni protokol koji standardizira komunikaciju između uređivača kod
|
||||
|
||||
---
|
||||
|
||||
## Konfiguriši
|
||||
## Konfiguracija
|
||||
|
||||
Da biste koristili OpenCode putem ACP-a, konfigurirajte svoj uređivač da pokrene naredbu `opencode acp`.
|
||||
|
||||
@@ -140,7 +140,7 @@ Ova konfiguracija postavlja CodeCompanion da koristi OpenCode kao ACP chat agent
|
||||
|
||||
Ako trebate proslijediti varijable okruženja (kao što je `OPENCODE_API_KEY`), pogledajte [Configuring Adapters: Environment Variables](https://codecompanion.olimorris.dev/getting-started#setting-an-api-key) u dokumentaciji CodeCompanion.nvim.
|
||||
|
||||
## Podrška
|
||||
## Podržane funkcije
|
||||
|
||||
OpenCode radi isto kroz ACP kao i u terminalu. Podržane su sve funkcije:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Možete se prebacivati između agenata tokom sesije ili ih pozvati spominj
|
||||
|
||||
## Vrste
|
||||
|
||||
Postoje dvije vrste agenata u OpenCode; primarni agenti i podagenti.
|
||||
Postoje dvije vrste agenata u OpenCode: primarni agenti i podagenti.
|
||||
|
||||
---
|
||||
|
||||
@@ -45,17 +45,17 @@ OpenCode dolazi sa dva ugrađena primarna agenta i dva ugrađena subagenta.
|
||||
|
||||
---
|
||||
|
||||
### Koristi build
|
||||
### Build agent
|
||||
|
||||
_Mode_: `primary`
|
||||
_Režim_: `primary`
|
||||
|
||||
Build je **podrazumevani** primarni agent sa svim omogućenim alatima. Ovo je standardni agent za razvojni rad gdje vam je potreban pun pristup operacijama datoteka i sistemskim komandama.
|
||||
|
||||
---
|
||||
|
||||
### Koristi plan
|
||||
### Plan agent
|
||||
|
||||
_Mode_: `primary`
|
||||
_Režim_: `primary`
|
||||
|
||||
Ograničeni agent dizajniran za planiranje i analizu. Koristimo sistem dozvola kako bismo vam pružili veću kontrolu i spriječili neželjene promjene.
|
||||
Prema zadanim postavkama, sve sljedeće je postavljeno na `ask`:
|
||||
@@ -67,47 +67,47 @@ Ovaj agent je koristan kada želite da LLM analizira kod, predloži promjene ili
|
||||
|
||||
---
|
||||
|
||||
### Koristi general
|
||||
### General agent
|
||||
|
||||
_Mode_: `subagent`
|
||||
_Režim_: `subagent`
|
||||
|
||||
Agent opće namjene za istraživanje složenih pitanja i izvršavanje zadataka u više koraka. Ima potpuni pristup alatima (osim todo), tako da može mijenjati fajlove kada je to potrebno. Koristite ovo za paralelno pokretanje više jedinica rada.
|
||||
|
||||
---
|
||||
|
||||
### Koristi explore
|
||||
### Explore agent
|
||||
|
||||
_Mode_: `subagent`
|
||||
_Režim_: `subagent`
|
||||
|
||||
Brzi agent samo za čitanje za istraživanje kodnih baza. Nije moguće mijenjati fajlove. Koristite ovo kada trebate brzo pronaći datoteke po uzorku, pretražiti kod za ključne riječi ili odgovoriti na pitanja o bazi kodova.
|
||||
|
||||
---
|
||||
|
||||
### Koristi compaction
|
||||
### Compaction agent
|
||||
|
||||
_Mode_: `primary`
|
||||
_Režim_: `primary`
|
||||
|
||||
Skriveni sistemski agent koji sažima dugi kontekst u manji sažetak. Pokreće se automatski kada je potrebno i ne može se odabrati u korisničkom interfejsu.
|
||||
|
||||
---
|
||||
|
||||
### Koristi title
|
||||
### Title agent
|
||||
|
||||
_Mode_: `primary`
|
||||
_Režim_: `primary`
|
||||
|
||||
Skriveni sistemski agent koji generiše kratke naslove sesija. Pokreće se automatski i ne može se odabrati u korisničkom interfejsu.
|
||||
|
||||
---
|
||||
|
||||
### Koristi summary
|
||||
### Summary agent
|
||||
|
||||
_Mode_: `primary`
|
||||
_Režim_: `primary`
|
||||
|
||||
Skriveni sistemski agent koji kreira sažetke sesije. Pokreće se automatski i ne može se odabrati u korisničkom interfejsu.
|
||||
|
||||
---
|
||||
|
||||
## Upotreba
|
||||
## Korištenje
|
||||
|
||||
1. Za primarne agente, koristite taster **Tab** za kretanje kroz njih tokom sesije. Također možete koristiti svoju konfiguriranu vezu tipke `switch_agent`.
|
||||
|
||||
@@ -215,7 +215,7 @@ Pogledajmo ove opcije konfiguracije detaljno.
|
||||
|
||||
---
|
||||
|
||||
### Description
|
||||
### Opis
|
||||
|
||||
Koristite opciju `description` da pružite kratak opis onoga što agent radi i kada ga koristiti.
|
||||
|
||||
@@ -233,7 +233,7 @@ Ovo je **obavezna** opcija konfiguracije.
|
||||
|
||||
---
|
||||
|
||||
### Temperature
|
||||
### Temperatura
|
||||
|
||||
Kontrolišite slučajnost i kreativnost odgovora LLM-a pomoću `temperature` konfiguracije.
|
||||
|
||||
@@ -280,7 +280,7 @@ Ako temperatura nije navedena, OpenCode koristi standardne postavke specifične
|
||||
|
||||
---
|
||||
|
||||
### Max steps
|
||||
### Maksimalan broj koraka
|
||||
|
||||
Kontrolirajte maksimalni broj iteracija agenta koje agent može izvesti prije nego što bude prisiljen da odgovori samo tekstom. Ovo omogućava korisnicima koji žele kontrolirati troškove da postave ograničenje na akcije agenta.
|
||||
|
||||
@@ -306,7 +306,7 @@ Naslijeđeno polje `maxSteps` je zastarjelo. Umjesto toga koristite `steps`.
|
||||
|
||||
---
|
||||
|
||||
### Disable
|
||||
### Onemogućavanje
|
||||
|
||||
Postavite na `true` da onemogućite agenta.
|
||||
|
||||
@@ -322,7 +322,7 @@ Postavite na `true` da onemogućite agenta.
|
||||
|
||||
---
|
||||
|
||||
### Prompt
|
||||
### Upit
|
||||
|
||||
Navedite prilagođenu sistemsku prompt datoteku za ovog agenta sa `prompt` konfiguracijom. Datoteka s promptom treba da sadrži upute specifične za svrhu agenta.
|
||||
|
||||
@@ -362,7 +362,7 @@ ID modela u vašoj OpenCode konfiguraciji koristi format `provider/model-id`. Na
|
||||
|
||||
---
|
||||
|
||||
### Tools
|
||||
### Alati
|
||||
|
||||
Kontrolirajte koji su alati dostupni u ovom agentu koristeći konfiguraciju `tools`. Možete omogućiti ili onemogućiti određene alate tako što ćete ih postaviti na `true` ili `false`.
|
||||
|
||||
@@ -409,7 +409,7 @@ Također možete koristiti zamjenske znakove za kontrolu više alata odjednom. N
|
||||
|
||||
---
|
||||
|
||||
### Permissions
|
||||
### Dozvole
|
||||
|
||||
Možete konfigurirati dozvole za upravljanje radnjama koje agent može poduzeti. Trenutno se dozvole za alate `edit`, `bash` i `webfetch` mogu konfigurirati na:
|
||||
|
||||
@@ -521,7 +521,7 @@ Budući da posljednje podudarno pravilo ima prednost, prvo postavite zamjenski z
|
||||
|
||||
---
|
||||
|
||||
### Mode
|
||||
### Način rada
|
||||
|
||||
Kontrolirajte način rada agenta koristeći konfiguraciju `mode`. Opcija `mode` se koristi da specificira kako se agent može koristiti.
|
||||
|
||||
@@ -539,7 +539,7 @@ Opcija `mode` se može postaviti na `primary`, `subagent` ili `all`. Ako `mode`
|
||||
|
||||
---
|
||||
|
||||
### Hidden
|
||||
### Skriveno
|
||||
|
||||
Sakrij podagenta iz `@` menija za automatsko dovršavanje sa `hidden: true`. Korisno za interne podagente koje bi drugi agenti trebali programski pozvati samo preko Task alata.
|
||||
|
||||
@@ -562,7 +562,7 @@ Odnosi se samo na `mode: subagent` agente.
|
||||
|
||||
---
|
||||
|
||||
### Task permissions
|
||||
### Dozvole zadataka
|
||||
|
||||
Kontrolirajte koje podagente agent može pozvati preko Task alata sa `permission.task`. Koristi glob uzorke za fleksibilno uparivanje.
|
||||
|
||||
@@ -595,7 +595,7 @@ Korisnici uvijek mogu pozvati bilo kojeg subagenta direktno preko `@` menija za
|
||||
|
||||
---
|
||||
|
||||
### Color
|
||||
### Boja
|
||||
|
||||
Prilagodite vizualni izgled agenta u korisničkom sučelju s opcijom `color`. Ovo utiče na to kako se agent pojavljuje u interfejsu.
|
||||
|
||||
@@ -634,7 +634,7 @@ Vrijednosti se kreću od 0.0 do 1.0. Niže vrijednosti su više fokusirane, viš
|
||||
|
||||
---
|
||||
|
||||
### Additional
|
||||
### Dodatno
|
||||
|
||||
Sve druge opcije koje navedete u konfiguraciji agenta će biti **direktno proslijeđene** dobavljaču kao opcije modela. Ovo vam omogućava da koristite karakteristike i parametre specifične za provajdera.
|
||||
|
||||
@@ -661,7 +661,7 @@ Pokrenite `opencode models` da vidite listu dostupnih modela.
|
||||
|
||||
---
|
||||
|
||||
## Kreirajte agente
|
||||
## Kreiranje agenata
|
||||
|
||||
Možete kreirati nove agente koristeći sljedeću naredbu:
|
||||
|
||||
@@ -679,7 +679,7 @@ Ova interaktivna komanda će:
|
||||
|
||||
---
|
||||
|
||||
## Slučajevi upotrebe
|
||||
## Primjeri upotrebe
|
||||
|
||||
Evo nekoliko uobičajenih slučajeva upotrebe različitih agenata.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ OpenCode CLI po defaultu pokreće [TUI](/docs/tui) kada se pokrene bez ikakvih a
|
||||
opencode
|
||||
```
|
||||
|
||||
Ali takođe prihvata komande kao što je dokumentovano na ovoj stranici. Ovo vam omogućava programsku interakciju sa OpenCode.
|
||||
Ali takođe prihvata naredbe kao što je dokumentovano na ovoj stranici. Ovo vam omogućava programsku interakciju sa OpenCode.
|
||||
|
||||
```bash
|
||||
opencode run "Explain how closures work in JavaScript"
|
||||
@@ -21,36 +21,36 @@ opencode run "Explain how closures work in JavaScript"
|
||||
|
||||
### tui
|
||||
|
||||
Pokrenite korisnički interfejs OpenCode terminala.
|
||||
Pokrenite OpenCode terminalski korisnički interfejs.
|
||||
|
||||
```bash
|
||||
opencode [project]
|
||||
```
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Kratko | Opis |
|
||||
| Opcija | Kratko | Opis |
|
||||
| ------------ | ------ | ------------------------------------------------------------------------ |
|
||||
| `--continue` | `-c` | Nastavite posljednju sesiju |
|
||||
| `--session` | `-s` | ID sesije za nastavak |
|
||||
| `--fork` | | Forkujte sesiju pri nastavku (koristiti sa `--continue` ili `--session`) |
|
||||
| `--prompt` | | Uputstvo za upotrebu |
|
||||
| `--model` | `-m` | Model za korištenje u obliku dobavljača/modela |
|
||||
| `--prompt` | | Prompt za upotrebu |
|
||||
| `--model` | `-m` | Model za korištenje u obliku provider/model |
|
||||
| `--agent` | | Agent za korištenje |
|
||||
| `--port` | | Port za slušanje na |
|
||||
| `--hostname` | | Slušajte ime hosta |
|
||||
| `--port` | | Port na kojem treba slušati |
|
||||
| `--hostname` | | Hostname na kojem treba slušati |
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
## Naredbe
|
||||
|
||||
OpenCode CLI takođe ima sledeće komande.
|
||||
OpenCode CLI takođe ima sljedeće naredbe.
|
||||
|
||||
---
|
||||
|
||||
### agent
|
||||
|
||||
Upravljajte agentima za OpenCode.
|
||||
Upravljajte OpenCode agentima.
|
||||
|
||||
```bash
|
||||
opencode agent [command]
|
||||
@@ -60,13 +60,13 @@ opencode agent [command]
|
||||
|
||||
### attach
|
||||
|
||||
Priključite terminal na već pokrenut OpenCode backend server pokrenut putem `serve` ili `web` komandi.
|
||||
Priključite terminal na već pokrenut OpenCode backend server pokrenut putem `serve` ili `web` naredbi.
|
||||
|
||||
```bash
|
||||
opencode attach [url]
|
||||
```
|
||||
|
||||
Ovo omogućava korištenje TUI-ja sa udaljenim OpenCode backend-om. na primjer:
|
||||
Ovo omogućava korištenje TUI-ja sa udaljenim OpenCode backend-om. Na primjer:
|
||||
|
||||
```bash
|
||||
# Start the backend server for web/mobile access
|
||||
@@ -76,11 +76,11 @@ opencode web --port 4096 --hostname 0.0.0.0
|
||||
opencode attach http://10.20.30.40:4096
|
||||
```
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Kratko | Opis |
|
||||
| Opcija | Kratko | Opis |
|
||||
| ----------- | ------ | ------------------------------------ |
|
||||
| `--dir` | | Radni direktorij za pokretanje TUI u |
|
||||
| `--dir` | | Radni direktorij za pokretanje TUI-a |
|
||||
| `--session` | `-s` | ID sesije za nastavak |
|
||||
|
||||
---
|
||||
@@ -93,7 +93,7 @@ Kreirajte novog agenta s prilagođenom konfiguracijom.
|
||||
opencode agent create
|
||||
```
|
||||
|
||||
Ova komanda će vas voditi kroz kreiranje novog agenta sa prilagođenim sistemskim promptom i konfiguracijom alata.
|
||||
Ova naredba će vas voditi kroz kreiranje novog agenta sa prilagođenim sistemskim promptom i konfiguracijom alata.
|
||||
|
||||
---
|
||||
|
||||
@@ -131,7 +131,7 @@ Kada se OpenCode pokrene, učitava dobavljače iz datoteke vjerodajnica. I ako p
|
||||
|
||||
#### list
|
||||
|
||||
Navodi sve autentifikovane dobavljače pohranjene u datoteci akreditiva.
|
||||
Navodi sve autentifikovane dobavljače pohranjene u datoteci vjerodajnica.
|
||||
|
||||
```bash
|
||||
opencode auth list
|
||||
@@ -157,7 +157,7 @@ opencode auth logout
|
||||
|
||||
### github
|
||||
|
||||
Upravljajte GitHub agentom za automatizaciju spremišta.
|
||||
Upravljajte GitHub agentom za automatizaciju repozitorija.
|
||||
|
||||
```bash
|
||||
opencode github [command]
|
||||
@@ -167,7 +167,7 @@ opencode github [command]
|
||||
|
||||
#### install
|
||||
|
||||
Instalirajte GitHub agenta u svoje spremište.
|
||||
Instalirajte GitHub agenta u svoj repozitorij.
|
||||
|
||||
```bash
|
||||
opencode github install
|
||||
@@ -179,24 +179,24 @@ Ovo postavlja neophodni tok rada GitHub Actions i vodi vas kroz proces konfigura
|
||||
|
||||
#### run
|
||||
|
||||
Pokrenite GitHub agent. Ovo se obično koristi u GitHub akcijama.
|
||||
Pokrenite GitHub agent. Ovo se obično koristi u GitHub Actions.
|
||||
|
||||
```bash
|
||||
opencode github run
|
||||
```
|
||||
|
||||
##### Zastave
|
||||
##### Opcije
|
||||
|
||||
| Zastava | Opis |
|
||||
| --------- | -------------------------------------------- |
|
||||
| `--event` | GitHub lažni događaj za pokretanje agenta za |
|
||||
| `--token` | GitHub token ličnog pristupa |
|
||||
| Opcija | Opis |
|
||||
| --------- | -------------------------------------- |
|
||||
| `--event` | GitHub mock event za pokretanje agenta |
|
||||
| `--token` | GitHub Personal Access Token |
|
||||
|
||||
---
|
||||
|
||||
### mcp
|
||||
|
||||
Upravljajte serverima protokola konteksta modela.
|
||||
Upravljajte Model Context Protocol (MCP) serverima.
|
||||
|
||||
```bash
|
||||
opencode mcp [command]
|
||||
@@ -212,7 +212,7 @@ Dodajte MCP server svojoj konfiguraciji.
|
||||
opencode mcp add
|
||||
```
|
||||
|
||||
Ova komanda će vas voditi kroz dodavanje lokalnog ili udaljenog MCP servera.
|
||||
Ova naredba će vas voditi kroz dodavanje lokalnog ili udaljenog MCP servera.
|
||||
|
||||
---
|
||||
|
||||
@@ -241,7 +241,7 @@ opencode mcp auth [name]
|
||||
```
|
||||
|
||||
Ako ne navedete ime servera, od vas će biti zatraženo da izaberete neki od dostupnih servera koji podržavaju OAuth.
|
||||
Također možete navesti servere koji podržavaju OAuth i njihov status provjere autentičnosti.
|
||||
Također možete navesti servere koji podržavaju OAuth i njihov status autentifikacije.
|
||||
|
||||
```bash
|
||||
opencode mcp auth list
|
||||
@@ -267,7 +267,7 @@ opencode mcp logout [name]
|
||||
|
||||
#### debug
|
||||
|
||||
Otklanjanje grešaka OAuth veze sa MCP serverom.
|
||||
Otklanjanje grešaka (debug) OAuth veze sa MCP serverom.
|
||||
|
||||
```bash
|
||||
opencode mcp debug <name>
|
||||
@@ -291,11 +291,11 @@ Opciono možete proslijediti ID provajdera za filtriranje modela po tom dobavlja
|
||||
opencode models anthropic
|
||||
```
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Opis |
|
||||
| Opcija | Opis |
|
||||
| ----------- | ------------------------------------------------------------------------ |
|
||||
| `--refresh` | Osvježite predmemoriju modela sa models.dev |
|
||||
| `--refresh` | Osvježite keš modela sa models.dev |
|
||||
| `--verbose` | Koristite detaljniji izlaz modela (uključuje metapodatke poput troškova) |
|
||||
|
||||
Koristite `--refresh` zastavicu da ažurirate keširanu listu modela. Ovo je korisno kada su novi modeli dodani provajderu i želite da ih vidite u OpenCode.
|
||||
@@ -308,13 +308,13 @@ opencode models --refresh
|
||||
|
||||
### run
|
||||
|
||||
Pokrenite opencode u neinteraktivnom modu tako što ćete direktno proslijediti prompt.
|
||||
Pokrenite OpenCode u neinteraktivnom modu tako što ćete direktno proslijediti prompt.
|
||||
|
||||
```bash
|
||||
opencode run [message..]
|
||||
```
|
||||
|
||||
Ovo je korisno za skriptiranje, automatizaciju ili kada želite brz odgovor bez pokretanja punog TUI-ja. Na primjer.
|
||||
Ovo je korisno za skriptiranje, automatizaciju ili kada želite brz odgovor bez pokretanja punog TUI-ja. Na primjer:
|
||||
|
||||
```bash "opencode run"
|
||||
opencode run Explain the use of context in Go
|
||||
@@ -330,11 +330,11 @@ opencode serve
|
||||
opencode run --attach http://localhost:4096 "Explain async/await in JavaScript"
|
||||
```
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Kratko | Opis |
|
||||
| Opcija | Kratko | Opis |
|
||||
| ------------ | ------ | ------------------------------------------------------------------------ |
|
||||
| `--command` | | Naredba za pokretanje, koristite poruku za args |
|
||||
| `--command` | | Naredba za pokretanje, koristite poruku za argumente |
|
||||
| `--continue` | `-c` | Nastavite posljednju sesiju |
|
||||
| `--session` | `-s` | ID sesije za nastavak |
|
||||
| `--fork` | | Forkujte sesiju pri nastavku (koristiti sa `--continue` ili `--session`) |
|
||||
@@ -344,29 +344,29 @@ opencode run --attach http://localhost:4096 "Explain async/await in JavaScript"
|
||||
| `--file` | `-f` | Fajlovi koje treba priložiti poruci |
|
||||
| `--format` | | Format: default (formatiran) ili json (sirovi JSON događaji) |
|
||||
| `--title` | | Naslov sesije (koristi skraćeni prompt ako nije navedena vrijednost) |
|
||||
| `--attach` | | Priključite na pokrenuti opencode server (npr. http://localhost:4096) |
|
||||
| `--attach` | | Priključite na pokrenuti OpenCode server (npr. http://localhost:4096) |
|
||||
| `--port` | | Port za lokalni server (zadano na nasumični port) |
|
||||
|
||||
---
|
||||
|
||||
### serve
|
||||
|
||||
Pokrenite OpenCode server bez glave za pristup API-ju. Pogledajte [server docs](/docs/server) za kompletan HTTP interfejs.
|
||||
Pokrenite OpenCode headless server za API pristup. Pogledajte [server docs](/docs/server) za kompletan HTTP interfejs.
|
||||
|
||||
```bash
|
||||
opencode serve
|
||||
```
|
||||
|
||||
Ovo pokreće HTTP server koji pruža API pristup funkcionalnosti otvorenog koda bez TUI interfejsa. Postavite `OPENCODE_SERVER_PASSWORD` da omogućite HTTP osnovnu auth (korisničko ime je zadano na `opencode`).
|
||||
Ovo pokreće HTTP server koji pruža API pristup funkcionalnosti OpenCode-a bez TUI interfejsa. Postavite `OPENCODE_SERVER_PASSWORD` da omogućite HTTP osnovnu auth (korisničko ime je zadano na `opencode`).
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Opis |
|
||||
| ------------ | ---------------------------------------------------- |
|
||||
| `--port` | Port za slušanje na |
|
||||
| `--hostname` | Ime hosta za slušanje |
|
||||
| `--mdns` | Omogući mDNS otkrivanje |
|
||||
| `--cors` | Dodatni izvor(a) pretraživača koji dozvoljavaju CORS |
|
||||
| Opcija | Opis |
|
||||
| ------------ | ----------------------------------------------------- |
|
||||
| `--port` | Port na kojem treba slušati |
|
||||
| `--hostname` | Hostname na kojem treba slušati |
|
||||
| `--mdns` | Omogući mDNS otkrivanje |
|
||||
| `--cors` | Dodatni origin(i) pretraživača koji dozvoljavaju CORS |
|
||||
|
||||
---
|
||||
|
||||
@@ -388,12 +388,12 @@ Navedite sve OpenCode sesije.
|
||||
opencode session list
|
||||
```
|
||||
|
||||
##### Zastave
|
||||
##### Opcije
|
||||
|
||||
| Zastava | Kratko | Opis |
|
||||
| ------------- | ------ | ----------------------------------------- |
|
||||
| `--max-count` | `-n` | Ograničenje na N najnovijih sesija |
|
||||
| `--format` | | Izlazni format: tablica ili json (tabela) |
|
||||
| Opcija | Kratko | Opis |
|
||||
| ------------- | ------ | -------------------------------------- |
|
||||
| `--max-count` | `-n` | Ograničenje na N najnovijih sesija |
|
||||
| `--format` | | Izlazni format: table ili json (table) |
|
||||
|
||||
---
|
||||
|
||||
@@ -405,14 +405,14 @@ Prikaži statistiku upotrebe tokena i troškova za vaše OpenCode sesije.
|
||||
opencode stats
|
||||
```
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Opis |
|
||||
| ----------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| `--days` | Prikaži statistiku za zadnjih N dana (sva vremena) |
|
||||
| `--tools` | Broj alata za prikaz (svi) |
|
||||
| `--models` | Prikaži raščlambu korištenja modela (skriveno prema zadanim postavkama). Proslijedite broj za prikaz vrha N |
|
||||
| `--project` | Filtriraj po projektu (svi projekti, prazan niz: trenutni projekt) |
|
||||
| Opcija | Opis |
|
||||
| ----------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `--days` | Prikaži statistiku za zadnjih N dana (sva vremena) |
|
||||
| `--tools` | Broj alata za prikaz (svi) |
|
||||
| `--models` | Prikaži raščlambu korištenja modela (skriveno prema zadanim postavkama). Proslijedite broj za prikaz top N |
|
||||
| `--project` | Filtriraj po projektu (svi projekti, prazan niz: trenutni projekt) |
|
||||
|
||||
---
|
||||
|
||||
@@ -447,22 +447,22 @@ opencode import https://opncd.ai/s/abc123
|
||||
|
||||
### web
|
||||
|
||||
Pokrenite OpenCode server bez glave sa web interfejsom.
|
||||
Pokrenite OpenCode headless server sa web interfejsom.
|
||||
|
||||
```bash
|
||||
opencode web
|
||||
```
|
||||
|
||||
Ovo pokreće HTTP server i otvara web pretraživač za pristup OpenCode preko web interfejsa. Postavite `OPENCODE_SERVER_PASSWORD` da omogućite HTTP osnovnu auth (korisničko ime je zadano na `opencode`).
|
||||
Ovo pokreće HTTP server i otvara web pretraživač za pristup OpenCode-u preko web interfejsa. Postavite `OPENCODE_SERVER_PASSWORD` da omogućite HTTP osnovnu auth (korisničko ime je zadano na `opencode`).
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Opis |
|
||||
| ------------ | ---------------------------------------------------- |
|
||||
| `--port` | Port za slušanje na |
|
||||
| `--hostname` | Ime hosta za slušanje |
|
||||
| `--mdns` | Omogući mDNS otkrivanje |
|
||||
| `--cors` | Dodatni izvor(a) pretraživača koji dozvoljavaju CORS |
|
||||
| Opcija | Opis |
|
||||
| ------------ | ----------------------------------------------------- |
|
||||
| `--port` | Port na kojem treba slušati |
|
||||
| `--hostname` | Hostname na kojem treba slušati |
|
||||
| `--mdns` | Omogući mDNS otkrivanje |
|
||||
| `--cors` | Dodatni origin(i) pretraživača koji dozvoljavaju CORS |
|
||||
|
||||
---
|
||||
|
||||
@@ -474,15 +474,15 @@ Pokrenite ACP (Agent Client Protocol) server.
|
||||
opencode acp
|
||||
```
|
||||
|
||||
Ova komanda pokreće ACP server koji komunicira preko stdin/stdout koristeći nd-JSON.
|
||||
Ova naredba pokreće ACP server koji komunicira preko stdin/stdout koristeći nd-JSON.
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Opis |
|
||||
| ------------ | ------------------- |
|
||||
| `--cwd` | Radni imenik |
|
||||
| `--port` | Port za slušanje na |
|
||||
| `--hostname` | Slušajte ime hosta |
|
||||
| Opcija | Opis |
|
||||
| ------------ | --------------------------- |
|
||||
| `--cwd` | Radni direktorij |
|
||||
| `--port` | Port na kojem treba slušati |
|
||||
| `--hostname` | Hostname na kojem slušati |
|
||||
|
||||
---
|
||||
|
||||
@@ -494,12 +494,12 @@ Deinstalirajte OpenCode i uklonite sve povezane datoteke.
|
||||
opencode uninstall
|
||||
```
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Kratko | Opis |
|
||||
| Opcija | Kratko | Opis |
|
||||
| --------------- | ------ | --------------------------------------------- |
|
||||
| `--keep-config` | `-c` | Čuvajte konfiguracijske datoteke |
|
||||
| `--keep-data` | `-d` | Čuvajte podatke i snimke sesije |
|
||||
| `--keep-config` | `-c` | Sačuvajte konfiguracijske datoteke |
|
||||
| `--keep-data` | `-d` | Sačuvajte podatke i snimke sesije |
|
||||
| `--dry-run` | | Pokažite šta bi bilo uklonjeno bez uklanjanja |
|
||||
| `--force` | `-f` | Preskoči upite za potvrdu |
|
||||
|
||||
@@ -507,7 +507,7 @@ opencode uninstall
|
||||
|
||||
### upgrade
|
||||
|
||||
Ažurira opencode na najnoviju verziju ili određenu verziju.
|
||||
Ažurira OpenCode na najnoviju verziju ili određenu verziju.
|
||||
|
||||
```bash
|
||||
opencode upgrade [target]
|
||||
@@ -525,73 +525,76 @@ Za nadogradnju na određenu verziju.
|
||||
opencode upgrade v0.1.48
|
||||
```
|
||||
|
||||
#### Zastave
|
||||
#### Opcije
|
||||
|
||||
| Zastava | Kratko | Opis |
|
||||
| Opcija | Kratko | Opis |
|
||||
| ---------- | ------ | ------------------------------------------------------- |
|
||||
| `--method` | `-m` | Korišteni način instalacije; curl, npm, pnpm, bun, brew |
|
||||
|
||||
---
|
||||
|
||||
## Globalne zastave
|
||||
## Globalne opcije
|
||||
|
||||
CLI otvorenog koda uzima sljedeće globalne zastavice.
|
||||
| Zastava | Kratko | Opis
|
||||
|-------------- | ----- | ------------------------------------ |
|
||||
| `--help` | `-h` | Prikaži pomoć |
|
||||
| `--version` | `-v` | Odštampaj broj verzije |
|
||||
| `--print-logs` | | Ispis zapisnika u stderr |
|
||||
| `--log-level` | | Nivo dnevnika (DEBUG, INFO, WARN, ERROR) |
|
||||
OpenCode CLI prihvata sljedeće globalne zastavice.
|
||||
|
||||
| Opcija | Kratko | Opis |
|
||||
| -------------- | ------ | ----------------------------------------- |
|
||||
| `--help` | `-h` | Prikaži pomoć |
|
||||
| `--version` | `-v` | Ispiši broj verzije |
|
||||
| `--print-logs` | | Ispis logova u stderr |
|
||||
| `--log-level` | | Nivo logovanja (DEBUG, INFO, WARN, ERROR) |
|
||||
|
||||
---
|
||||
|
||||
## Varijable okruženja
|
||||
|
||||
OpenCode se može konfigurirati pomoću varijabli okruženja.
|
||||
| Varijabilna | Vrsta | Opis
|
||||
|------------------------------------- | ------- | ------------------------------------------------- |
|
||||
| `OPENCODE_AUTO_SHARE` | boolean | Automatski dijeli sesije |
|
||||
| `OPENCODE_GIT_BASH_PATH` | string | Putanja do Git Bash izvršne datoteke na Windows |
|
||||
| `OPENCODE_CONFIG` | string | Put do konfiguracionog fajla |
|
||||
| `OPENCODE_CONFIG_DIR` | string | Put do konfiguracijskog direktorija |
|
||||
| `OPENCODE_CONFIG_CONTENT` | string | Inline json konfiguracijski sadržaj |
|
||||
| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | Onemogući automatske provjere ažuriranja |
|
||||
| `OPENCODE_DISABLE_PRUNE` | boolean | Onemogući orezivanje starih podataka |
|
||||
| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | Onemogući automatsko ažuriranje naslova terminala |
|
||||
| `OPENCODE_PERMISSION` | string | Umetnuta json konfiguracija dozvola |
|
||||
| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | Onemogući podrazumevane dodatke |
|
||||
| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | Onemogući automatsko preuzimanje LSP servera |
|
||||
| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | Omogući eksperimentalne modele |
|
||||
| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | Onemogući automatsko sažimanje konteksta |
|
||||
| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | Onemogući čitanje sa `.claude` (prompt + vještine) |
|
||||
| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | Onemogući čitanje `~/.claude/CLAUDE.md` |
|
||||
| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | Onemogući učitavanje `.claude/skills` |
|
||||
| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | Onemogući dohvaćanje modela iz udaljenih izvora |
|
||||
| `OPENCODE_FAKE_VCS` | string | Lažni VCS provajder za potrebe testiranja |
|
||||
| `OPENCODE_DISABLE_FILETIME_CHECK` | boolean | Onemogući provjeru vremena datoteke radi optimizacije |
|
||||
| `OPENCODE_CLIENT` | string | Identifikator klijenta (zadano na `cli`) |
|
||||
| `OPENCODE_ENABLE_EXA` | boolean | Omogući Exa alate za web pretraživanje |
|
||||
| `OPENCODE_SERVER_PASSWORD` | string | Omogući osnovnu autorizaciju za `serve`/`web` |
|
||||
| `OPENCODE_SERVER_USERNAME` | string | Poništi osnovno korisničko ime autentifikacije (zadano `opencode`) |
|
||||
| `OPENCODE_MODELS_URL` | string | Prilagođeni URL za dohvaćanje konfiguracije modela |
|
||||
|
||||
| Varijabla | Tip | Opis |
|
||||
| ------------------------------------- | ------- | ------------------------------------------------------------------ |
|
||||
| `OPENCODE_AUTO_SHARE` | boolean | Automatski dijeli sesije |
|
||||
| `OPENCODE_GIT_BASH_PATH` | string | Putanja do Git Bash izvršne datoteke na Windows-u |
|
||||
| `OPENCODE_CONFIG` | string | Putanja do konfiguracijskog fajla |
|
||||
| `OPENCODE_CONFIG_DIR` | string | Putanja do konfiguracijskog direktorija |
|
||||
| `OPENCODE_CONFIG_CONTENT` | string | Inline json konfiguracijski sadržaj |
|
||||
| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | Onemogući automatske provjere ažuriranja |
|
||||
| `OPENCODE_DISABLE_PRUNE` | boolean | Onemogući brisanje (pruning) starih podataka |
|
||||
| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | Onemogući automatsko ažuriranje naslova terminala |
|
||||
| `OPENCODE_PERMISSION` | string | Inline json konfiguracija dozvola |
|
||||
| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | Onemogući podrazumijevane dodatke (plugins) |
|
||||
| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | Onemogući automatsko preuzimanje LSP servera |
|
||||
| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | Omogući eksperimentalne modele |
|
||||
| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | Onemogući automatsko sažimanje konteksta |
|
||||
| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | Onemogući čitanje iz `.claude` (prompt + vještine) |
|
||||
| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | Onemogući čitanje `~/.claude/CLAUDE.md` |
|
||||
| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | Onemogući učitavanje `.claude/skills` |
|
||||
| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | Onemogući dohvaćanje modela iz udaljenih izvora |
|
||||
| `OPENCODE_FAKE_VCS` | string | Lažni VCS provajder za potrebe testiranja |
|
||||
| `OPENCODE_DISABLE_FILETIME_CHECK` | boolean | Onemogući provjeru vremena datoteke radi optimizacije |
|
||||
| `OPENCODE_CLIENT` | string | Identifikator klijenta (zadano na `cli`) |
|
||||
| `OPENCODE_ENABLE_EXA` | boolean | Omogući Exa alate za web pretraživanje |
|
||||
| `OPENCODE_SERVER_PASSWORD` | string | Omogući osnovnu autentifikaciju za `serve`/`web` |
|
||||
| `OPENCODE_SERVER_USERNAME` | string | Poništi osnovno korisničko ime autentifikacije (zadano `opencode`) |
|
||||
| `OPENCODE_MODELS_URL` | string | Prilagođeni URL za dohvaćanje konfiguracije modela |
|
||||
|
||||
---
|
||||
|
||||
### Eksperimentalno
|
||||
|
||||
Ove varijable okruženja omogućavaju eksperimentalne karakteristike koje se mogu promijeniti ili ukloniti.
|
||||
| Varijabilna | Vrsta | Opis
|
||||
|----------------------------------------------- | ------- | --------------------------------------- |
|
||||
| `OPENCODE_EXPERIMENTAL` | boolean | Omogući sve eksperimentalne funkcije |
|
||||
| `OPENCODE_EXPERIMENTAL_ICON_DISCOVERY` | boolean | Omogući otkrivanje ikona |
|
||||
| `OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT` | boolean | Onemogući kopiranje pri odabiru u TUI |
|
||||
| `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS` | broj | Zadano vremensko ograničenje za bash komande u ms |
|
||||
| `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | broj | Maksimalni izlazni tokeni za LLM odgovore |
|
||||
| `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | Omogući praćenje datoteka za cijeli dir |
|
||||
| `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | Omogući oxfmt formatter |
|
||||
| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | Omogući eksperimentalni LSP alat |
|
||||
| `OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER` | boolean | Onemogući praćenje datoteka |
|
||||
| `OPENCODE_EXPERIMENTAL_EXA` | boolean | Omogući eksperimentalne Exa funkcije |
|
||||
| `OPENCODE_EXPERIMENTAL_LSP_TY` | boolean | Omogući eksperimentalnu provjeru tipa LSP |
|
||||
| `OPENCODE_EXPERIMENTAL_MARKDOWN` | boolean | Omogući eksperimentalne Markdown funkcije |
|
||||
| `OPENCODE_EXPERIMENTAL_PLAN_MODE` | boolean | Omogući režim plana |
|
||||
|
||||
| Varijabla | Tip | Opis |
|
||||
| ----------------------------------------------- | ------- | ------------------------------------------------- |
|
||||
| `OPENCODE_EXPERIMENTAL` | boolean | Omogući sve eksperimentalne funkcije |
|
||||
| `OPENCODE_EXPERIMENTAL_ICON_DISCOVERY` | boolean | Omogući otkrivanje ikona |
|
||||
| `OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT` | boolean | Onemogući kopiranje pri odabiru u TUI |
|
||||
| `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS` | number | Zadano vremensko ograničenje za bash naredbe u ms |
|
||||
| `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | number | Maksimalni izlazni tokeni za LLM odgovore |
|
||||
| `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | Omogući praćenje datoteka za cijeli direktorij |
|
||||
| `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | Omogući oxfmt formatter |
|
||||
| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | Omogući eksperimentalni LSP alat |
|
||||
| `OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER` | boolean | Onemogući praćenje datoteka |
|
||||
| `OPENCODE_EXPERIMENTAL_EXA` | boolean | Omogući eksperimentalne Exa funkcije |
|
||||
| `OPENCODE_EXPERIMENTAL_LSP_TY` | boolean | Omogući eksperimentalnu provjeru tipa LSP |
|
||||
| `OPENCODE_EXPERIMENTAL_MARKDOWN` | boolean | Omogući eksperimentalne Markdown funkcije |
|
||||
| `OPENCODE_EXPERIMENTAL_PLAN_MODE` | boolean | Omogući Plan mod |
|
||||
|
||||
@@ -13,7 +13,7 @@ Prilagođene komande su dodatak ugrađenim komandama kao što su `/init`, `/undo
|
||||
|
||||
---
|
||||
|
||||
## Kreirajte komandne fajlove
|
||||
## Kreiranje datoteka naredbi
|
||||
|
||||
Kreirajte markdown fajlove u direktorijumu `commands/` da definišete prilagođene komande.
|
||||
Kreiraj `.opencode/commands/test.md`:
|
||||
@@ -38,7 +38,7 @@ Koristite komandu tako što ćete upisati `/` nakon čega slijedi naziv komande.
|
||||
|
||||
---
|
||||
|
||||
## Konfiguriši
|
||||
## Konfiguracija
|
||||
|
||||
Možete dodati prilagođene komande kroz OpenCode konfiguraciju ili kreiranjem markdown datoteka u direktoriju `commands/`.
|
||||
|
||||
@@ -99,7 +99,7 @@ Ime markdown datoteke postaje ime naredbe. Na primjer, `test.md` vam omogućava
|
||||
|
||||
---
|
||||
|
||||
## Prompt config
|
||||
## Konfiguracija upita
|
||||
|
||||
Promptovi za prilagođene komande podržavaju nekoliko posebnih čuvara mjesta i sintakse.
|
||||
|
||||
@@ -190,7 +190,7 @@ Naredbe se pokreću u korijenskom direktoriju vašeg projekta i njihov izlaz pos
|
||||
|
||||
---
|
||||
|
||||
### Reference fajlova
|
||||
### Reference datoteka
|
||||
|
||||
Uključite datoteke u svoju naredbu koristeći `@` nakon čega slijedi naziv datoteke.
|
||||
|
||||
@@ -213,7 +213,7 @@ Pogledajmo detaljno opcije konfiguracije.
|
||||
|
||||
---
|
||||
|
||||
### Template
|
||||
### Šablon
|
||||
|
||||
Opcija `template` definira prompt koji će biti poslan LLM-u kada se naredba izvrši.
|
||||
|
||||
@@ -269,7 +269,7 @@ Ovo je **opciona** opcija konfiguracije. Ako nije navedeno, podrazumevano je va
|
||||
|
||||
---
|
||||
|
||||
### Subtask
|
||||
### Podzadatak
|
||||
|
||||
Koristite `subtask` boolean da prisilite naredbu da pokrene [subagent](/docs/agents/#subagents) pozivanje.
|
||||
Ovo je korisno ako želite da naredba ne zagađuje vaš primarni kontekst i da će **primorati** agenta da djeluje kao subagent,
|
||||
@@ -307,9 +307,9 @@ Ovo je **opciona** opcija konfiguracije.
|
||||
|
||||
---
|
||||
|
||||
## Ugrađene
|
||||
## Ugrađene naredbe
|
||||
|
||||
opencode uključuje nekoliko ugrađenih naredbi kao što su `/init`, `/undo`, `/redo`, `/share`, `/help`; [saznaj više](/docs/tui#commands).
|
||||
OpenCode uključuje nekoliko ugrađenih naredbi kao što su `/init`, `/undo`, `/redo`, `/share`, `/help`; [saznaj više](/docs/tui#commands).
|
||||
:::note
|
||||
Prilagođene komande mogu nadjačati ugrađene komande.
|
||||
:::
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Config
|
||||
title: Konfiguracija
|
||||
description: Korištenje OpenCode JSON konfiguracije.
|
||||
---
|
||||
|
||||
@@ -25,10 +25,10 @@ OpenCode podržava i **JSON** i **JSONC** (JSON sa komentarima) formate.
|
||||
|
||||
## Lokacije
|
||||
|
||||
Možete postaviti svoju konfiguraciju na nekoliko različitih lokacija i one imaju a
|
||||
drugačiji redosled prioriteta.
|
||||
Možete postaviti svoju konfiguraciju na nekoliko različitih lokacija i one imaju drugačiji redoslijed prioriteta.
|
||||
|
||||
:::note
|
||||
Konfiguracijski fajlovi su **spojeni zajedno**, a ne zamijenjeni.
|
||||
Konfiguracijski fajlovi se **spajaju**, ne zamjenjuju.
|
||||
:::
|
||||
Konfiguracijski fajlovi se spajaju, ne zamjenjuju. Kombiniraju se postavke sa sljedećih konfiguracijskih lokacija. Kasnije konfiguracije poništavaju prethodne samo za konfliktne ključeve. Nekonfliktne postavke iz svih konfiguracija su sačuvane.
|
||||
Na primjer, ako vaša globalna konfiguracija postavlja `theme: "opencode"` i `autoupdate: true`, a vaša projektna konfiguracija postavlja `model: "anthropic/claude-sonnet-4-5"`, konačna konfiguracija će uključivati sve tri postavke.
|
||||
@@ -39,12 +39,13 @@ Na primjer, ako vaša globalna konfiguracija postavlja `theme: "opencode"` i `au
|
||||
|
||||
Izvori konfiguracije se učitavaju ovim redoslijedom (kasniji izvori poništavaju ranije):
|
||||
|
||||
1. **Udaljena konfiguracija** (od `.well-known/opencode`) - organizacione postavke
|
||||
1. **Udaljena konfiguracija** (od `.well-known/opencode`) - organizacijske postavke
|
||||
2. **Globalna konfiguracija** (`~/.config/opencode/opencode.json`) - korisničke postavke
|
||||
3. **Prilagođena konfiguracija** (`OPENCODE_CONFIG` env var) - prilagođena zaobilaženja
|
||||
3. **Prilagođena konfiguracija** (`OPENCODE_CONFIG` env var) - prilagođena preinačenja
|
||||
4. **Konfiguracija projekta** (`opencode.json` u projektu) - postavke specifične za projekat
|
||||
5. **`.opencode` direktoriji** - agenti, komande, dodaci
|
||||
6. **Inline config** (`OPENCODE_CONFIG_CONTENT` env var) - runtime nadjačava
|
||||
6. **Inline konfiguracija** (`OPENCODE_CONFIG_CONTENT` env var) - runtime preinačenja
|
||||
|
||||
To znači da konfiguracije projekta mogu nadjačati globalne zadane postavke, a globalne konfiguracije mogu nadjačati postavke udaljene organizacije.
|
||||
:::note
|
||||
Direktoriji `.opencode` i `~/.config/opencode` koriste **imena u množini** za poddirektorije: `agents/`, `commands/`, `modes/`, `plugins/`, `skills/`, `tools/` i `themes/`. Pojedinačna imena (npr. `agent/`) su također podržana za kompatibilnost unatrag.
|
||||
@@ -52,9 +53,9 @@ Izvori konfiguracije se učitavaju ovim redoslijedom (kasniji izvori poništavaj
|
||||
|
||||
---
|
||||
|
||||
### Daljinski
|
||||
### Udaljeno (Remote)
|
||||
|
||||
Organizacije mogu pružiti zadanu konfiguraciju preko `.well-known/opencode` krajnje točke. Ovo se automatski preuzima kada se autentifikujete kod provajdera koji to podržava.
|
||||
Organizacije mogu pružiti zadanu konfiguraciju preko `.well-known/opencode` krajnje tačke. Ovo se automatski preuzima kada se autentifikujete kod provajdera koji to podržava.
|
||||
Prvo se učitava udaljena konfiguracija koja služi kao osnovni sloj. Svi ostali izvori konfiguracije (globalni, projektni) mogu nadjačati ove zadane postavke.
|
||||
Na primjer, ako vaša organizacija nudi MCP servere koji su po defaultu onemogućeni:
|
||||
|
||||
@@ -86,14 +87,14 @@ Možete omogućiti određene servere u vašoj lokalnoj konfiguraciji:
|
||||
|
||||
---
|
||||
|
||||
### Global
|
||||
### Globalno
|
||||
|
||||
Postavite svoju globalnu OpenCode konfiguraciju u `~/.config/opencode/opencode.json`. Koristite globalnu konfiguraciju za korisničke preferencije kao što su teme, provajderi ili veze tipki.
|
||||
Globalna konfiguracija poništava zadane postavke udaljene organizacije.
|
||||
|
||||
---
|
||||
|
||||
### Project
|
||||
### Projekt
|
||||
|
||||
Dodajte `opencode.json` u korijen projekta. Konfiguracija projekta ima najveći prioritet među standardnim konfiguracijskim datotekama - ona nadjačava globalne i udaljene konfiguracije.
|
||||
:::tip
|
||||
@@ -104,7 +105,7 @@ Ovo je također sigurno provjeriti u Git i koristi istu shemu kao globalna.
|
||||
|
||||
---
|
||||
|
||||
### Custom config
|
||||
### Prilagođena konfiguracija
|
||||
|
||||
Navedite prilagođenu putanju konfiguracijske datoteke koristeći varijablu okruženja `OPENCODE_CONFIG`.
|
||||
|
||||
@@ -117,12 +118,9 @@ Prilagođena konfiguracija se učitava između globalne i projektne konfiguracij
|
||||
|
||||
---
|
||||
|
||||
### Custom directory
|
||||
### Prilagođeni direktorij
|
||||
|
||||
Navedite prilagođeni konfiguracijski direktorij koristeći `OPENCODE_CONFIG_DIR`
|
||||
varijabla okruženja. U ovom direktoriju će se tražiti agenti, komande,
|
||||
modove i dodatke baš kao standardni `.opencode` direktorij, i trebali bi
|
||||
prate istu strukturu.
|
||||
Navedite prilagođeni konfiguracijski direktorij koristeći `OPENCODE_CONFIG_DIR` varijablu okruženja. U ovom direktoriju će se tražiti agenti, komande, modovi i dodaci baš kao standardni `.opencode` direktorij, i trebali bi pratiti istu strukturu.
|
||||
|
||||
```bash
|
||||
export OPENCODE_CONFIG_DIR=/path/to/my/config-directory
|
||||
@@ -133,10 +131,10 @@ Prilagođeni direktorij se učitava nakon direktorija globalne konfiguracije i `
|
||||
|
||||
---
|
||||
|
||||
## Shema
|
||||
## Šema
|
||||
|
||||
Konfiguracijski fajl ima šemu koja je definirana u [**`opencode.ai/config.json`**](https://opencode.ai/config.json).
|
||||
Vaš uređivač bi trebao biti u mogućnosti da potvrdi i autodovršava na osnovu šeme.
|
||||
Vaš editor bi trebao biti u mogućnosti da validira i autodovršava na osnovu šeme.
|
||||
|
||||
---
|
||||
|
||||
@@ -162,13 +160,13 @@ Dostupne opcije:
|
||||
- `scroll_acceleration.enabled` - Omogući ubrzanje skrolovanja u macOS stilu. **Ima prednost nad `scroll_speed`.**
|
||||
- `scroll_speed` - Prilagođeni množitelj brzine pomicanja (podrazumevano: `3`, minimalno: `1`). Zanemareno ako je `scroll_acceleration.enabled` `true`.
|
||||
- `diff_style` - Kontrola prikaza razlike. `"auto"` se prilagođava širini terminala, `"stacked"` uvijek prikazuje jednu kolonu.
|
||||
[Ovdje saznajte više o korištenju TUI](/docs/tui).
|
||||
[Saznajte više o korištenju TUI](/docs/tui) ovdje.
|
||||
|
||||
---
|
||||
|
||||
### Server
|
||||
|
||||
Možete konfigurirati postavke servera za komande `opencode serve` i `opencode web` putem opcije `server`.
|
||||
Možete konfigurirati postavke servera za naredbe `opencode serve` i `opencode web` putem opcije `server`.
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
@@ -186,15 +184,15 @@ Možete konfigurirati postavke servera za komande `opencode serve` i `opencode w
|
||||
Dostupne opcije:
|
||||
|
||||
- `port` - Port za slušanje.
|
||||
- `hostname` - Ime hosta za slušanje. Kada je `mdns` omogućen i nije postavljeno ime hosta, podrazumevano je `0.0.0.0`.
|
||||
- `mdns` - Omogući otkrivanje mDNS usluge. Ovo omogućava drugim uređajima na mreži da otkriju vaš OpenCode server.
|
||||
- `mdnsDomain` - Prilagođeno ime domene za mDNS uslugu. Zadano je `opencode.local`. Korisno za pokretanje više instanci na istoj mreži.
|
||||
- `cors` - Dodatni izvori koji omogućavaju CORS kada koristite HTTP server iz klijenta baziranog na pretraživaču. Vrijednosti moraju biti punog porijekla (šema + host + opcijski port), npr. `https://app.example.com`.
|
||||
- `hostname` - Hostname za slušanje. Kada je `mdns` omogućen i nije postavljeno ime hosta, podrazumevano je `0.0.0.0`.
|
||||
- `mdns` - Omogući mDNS otkrivanje servisa. Ovo omogućava drugim uređajima na mreži da otkriju vaš OpenCode server.
|
||||
- `mdnsDomain` - Prilagođeno ime domene za mDNS servis. Zadano je `opencode.local`. Korisno za pokretanje više instanci na istoj mreži.
|
||||
- `cors` - Dodatni origini koji omogućavaju CORS kada koristite HTTP server iz klijenta baziranog na pretraživaču. Vrijednosti moraju biti puni origin (shema + host + opcijski port), npr. `https://app.example.com`.
|
||||
[Saznajte više o serveru](/docs/server) ovdje.
|
||||
|
||||
---
|
||||
|
||||
### Tools
|
||||
### Alati
|
||||
|
||||
Možete upravljati alatima koje LLM može koristiti putem opcije `tools`.
|
||||
|
||||
@@ -244,7 +242,7 @@ Opcije provajdera mogu uključivati `timeout` i `setCacheKey`:
|
||||
|
||||
- `timeout` - Vrijeme čekanja zahtjeva u milisekundama (podrazumevano: 300000). Postavite na `false` da onemogućite.
|
||||
- `setCacheKey` - Osigurajte da je ključ keš memorije uvijek postavljen za određenog provajdera.
|
||||
Također možete konfigurirati [lokalni modeli](/docs/models#local). [Saznajte više](/docs/models).
|
||||
Također možete konfigurirati [lokalne modele](/docs/models#local). [Saznajte više](/docs/models).
|
||||
|
||||
---
|
||||
|
||||
@@ -272,8 +270,8 @@ Amazon Bedrock podržava konfiguraciju specifičnu za AWS:
|
||||
```
|
||||
|
||||
- `region` - AWS regija za Bedrock (zadano na `AWS_REGION` env var ili `us-east-1`)
|
||||
- `profile` - AWS imenovan profil od `~/.aws/credentials` (podrazumevano na `AWS_PROFILE` env var)
|
||||
- `endpoint` - URL prilagođene krajnje tačke za VPC krajnje tačke. Ovo je pseudonim za generičku opciju `baseURL` koristeći terminologiju specifičnu za AWS. Ako su oba navedena, `endpoint` ima prednost.
|
||||
- `profile` - AWS imenovani profil iz `~/.aws/credentials` (zadano na `AWS_PROFILE` env var)
|
||||
- `endpoint` - URL prilagođene krajnje tačke za VPC krajnje tačke. Ovo je alias za generičku opciju `baseURL` koristeći terminologiju specifičnu za AWS. Ako su oba navedena, `endpoint` ima prednost.
|
||||
:::note
|
||||
Tokeni nosioca (`AWS_BEARER_TOKEN_BEDROCK` ili `/connect`) imaju prednost nad autentifikacijom zasnovanom na profilu. Pogledajte [prednost autentifikacije](/docs/providers#authentication-precedence) za detalje.
|
||||
:::
|
||||
@@ -281,7 +279,7 @@ Amazon Bedrock podržava konfiguraciju specifičnu za AWS:
|
||||
|
||||
---
|
||||
|
||||
### Theme
|
||||
### Tema
|
||||
|
||||
Možete konfigurirati temu koju želite koristiti u svojoj OpenCode konfiguraciji putem opcije `theme`.
|
||||
|
||||
@@ -333,7 +331,7 @@ Možete postaviti zadanog agenta koristeći opciju `default_agent`. Ovo određuj
|
||||
}
|
||||
```
|
||||
|
||||
Zadani agent mora biti primarni agent (ne podagent). Ovo može biti ugrađeni agent kao što je `"build"` ili `"plan"`, ili [prilagođeni agent](/docs/agents) koji ste definirali. Ako navedeni agent ne postoji ili je subagent, OpenCode će se vratiti na `"build"` s upozorenjem.
|
||||
Zadani agent mora biti primarni agent (ne podagent). Ovo može biti ugrađeni agent kao što je `"build"` ili `"plan"`, ili [prilagođeni agent](/docs/agents) koji ste definirali. Ako navedeni agent ne postoji ili je podagent, OpenCode će se vratiti na `"build"` s upozorenjem.
|
||||
Ova postavka se primjenjuje na sva sučelja: TUI, CLI (`opencode run`), desktop aplikaciju i GitHub Action.
|
||||
|
||||
---
|
||||
@@ -349,18 +347,18 @@ Možete konfigurirati funkciju [share](/docs/share) putem opcije `share`.
|
||||
}
|
||||
```
|
||||
|
||||
Ovo traje:
|
||||
Ovo prihvata:
|
||||
|
||||
- `"manual"` - Dozvoli ručno dijeljenje putem komandi (podrazumevano)
|
||||
- `"manual"` - Dozvoli ručno dijeljenje putem naredbi (podrazumevano)
|
||||
- `"auto"` - Automatski dijelite nove razgovore
|
||||
- `"disabled"` - Onemogući dijeljenje u potpunosti
|
||||
Podrazumevano, dijeljenje je postavljeno na ručni način rada gdje trebate eksplicitno dijeliti razgovore pomoću naredbe `/share`.
|
||||
|
||||
---
|
||||
|
||||
### Command
|
||||
### Naredbe
|
||||
|
||||
Možete konfigurirati prilagođene komande za ponavljanje zadataka putem opcije `command`.
|
||||
Možete konfigurirati prilagođene naredbe za ponavljanje zadataka putem opcije `command`.
|
||||
|
||||
```jsonc title="opencode.jsonc"
|
||||
{
|
||||
@@ -380,13 +378,13 @@ Možete konfigurirati prilagođene komande za ponavljanje zadataka putem opcije
|
||||
}
|
||||
```
|
||||
|
||||
Također možete definirati komande koristeći markdown fajlove u `~/.config/opencode/commands/` ili `.opencode/commands/`. [Saznajte više ovdje](/docs/commands).
|
||||
Također možete definirati naredbe koristeći markdown fajlove u `~/.config/opencode/commands/` ili `.opencode/commands/`. [Saznajte više ovdje](/docs/commands).
|
||||
|
||||
---
|
||||
|
||||
### Keybinds
|
||||
### Prečice tipki
|
||||
|
||||
Možete prilagoditi svoje veze ključeva putem opcije `keybinds`.
|
||||
Možete prilagoditi svoje veze tipki putem opcije `keybinds`.
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
@@ -415,7 +413,7 @@ Imajte na umu da ovo funkcionira samo ako nije instalirano pomoću upravitelja p
|
||||
|
||||
---
|
||||
|
||||
### Formatters
|
||||
### Formateri
|
||||
|
||||
Možete konfigurirati formatere koda putem opcije `formatter`.
|
||||
|
||||
@@ -437,13 +435,13 @@ Možete konfigurirati formatere koda putem opcije `formatter`.
|
||||
}
|
||||
```
|
||||
|
||||
[Saznajte više o formatterima](/docs/formatters) ovdje.
|
||||
[Saznajte više o formaterima](/docs/formatters) ovdje.
|
||||
|
||||
---
|
||||
|
||||
### Dozvole
|
||||
|
||||
Prema zadanim postavkama, opencode **dopušta sve operacije** bez potrebe za eksplicitnim dopuštenjem. Ovo možete promijeniti koristeći opciju `permission`.
|
||||
Prema zadanim postavkama, OpenCode **dopušta sve operacije** bez potrebe za eksplicitnim dopuštenjem. Ovo možete promijeniti koristeći opciju `permission`.
|
||||
Na primjer, da osigurate da alati `edit` i `bash` zahtijevaju odobrenje korisnika:
|
||||
|
||||
```json title="opencode.json"
|
||||
@@ -456,11 +454,11 @@ Na primjer, da osigurate da alati `edit` i `bash` zahtijevaju odobrenje korisnik
|
||||
}
|
||||
```
|
||||
|
||||
[Ovdje saznajte više o ](/docs/permissions) dozvolama.
|
||||
[Saznajte više o dozvolama](/docs/permissions) ovdje.
|
||||
|
||||
---
|
||||
|
||||
### Compaction
|
||||
### Sažimanje
|
||||
|
||||
Možete kontrolirati ponašanje sažimanja konteksta putem opcije `compaction`.
|
||||
|
||||
@@ -479,7 +477,7 @@ Možete kontrolirati ponašanje sažimanja konteksta putem opcije `compaction`.
|
||||
|
||||
---
|
||||
|
||||
### Watcher
|
||||
### Promatrač (Watcher)
|
||||
|
||||
Možete konfigurirati obrasce ignoriranja promatrača datoteka putem opcije `watcher`.
|
||||
|
||||
@@ -511,7 +509,7 @@ Možete konfigurirati MCP servere koje želite koristiti putem opcije `mcp`.
|
||||
|
||||
---
|
||||
|
||||
### Extras
|
||||
### Dodaci
|
||||
|
||||
[Plugins](/docs/plugins) proširuju OpenCode sa prilagođenim alatima, kukicama i integracijama.
|
||||
Postavite datoteke dodataka u `.opencode/plugins/` ili `~/.config/opencode/plugins/`. Također možete učitati dodatke iz npm-a preko opcije `plugin`.
|
||||
@@ -538,8 +536,7 @@ Možete konfigurirati upute za model koji koristite putem opcije `instructions`.
|
||||
}
|
||||
```
|
||||
|
||||
Ovo uzima niz putanja i uzoraka globusa do datoteka instrukcija. [Saznajte više
|
||||
o pravilima ovdje](/docs/rules).
|
||||
Ovo uzima niz putanja i glob uzoraka do datoteka instrukcija. [Saznajte više o pravilima ovdje](/docs/rules).
|
||||
|
||||
---
|
||||
|
||||
@@ -560,7 +557,7 @@ Možete onemogućiti dobavljače koji se automatski učitavaju preko opcije `dis
|
||||
Opcija `disabled_providers` prihvata niz ID-ova provajdera. Kada je provajder onemogućen:
|
||||
|
||||
- Neće se učitati čak i ako su varijable okruženja postavljene.
|
||||
- Neće se učitati čak i ako su API ključevi konfigurirani putem `/connect` komande.
|
||||
- Neće se učitati čak i ako su API ključevi konfigurirani putem `/connect` naredbe.
|
||||
- Modeli dobavljača se neće pojaviti na listi za odabir modela.
|
||||
|
||||
---
|
||||
@@ -576,7 +573,7 @@ Možete odrediti listu dozvoljenih dobavljača putem opcije `enabled_providers`.
|
||||
}
|
||||
```
|
||||
|
||||
Ovo je korisno kada želite da ograničite OpenCode da koristi samo određene provajdere umesto da ih onemogućavate jednog po jednog.
|
||||
Ovo je korisno kada želite da ograničite OpenCode da koristi samo određene provajdere umjesto da ih onemogućavate jednog po jednog.
|
||||
:::note
|
||||
`disabled_providers` ima prioritet nad `enabled_providers`.
|
||||
:::
|
||||
@@ -607,7 +604,7 @@ Možete koristiti zamjenu varijabli u vašim konfiguracijskim datotekama da bist
|
||||
|
||||
---
|
||||
|
||||
### Env vars
|
||||
### Varijable okruženja
|
||||
|
||||
Koristite `{env:VARIABLE_NAME}` za zamjenu varijabli okruženja:
|
||||
|
||||
@@ -630,7 +627,7 @@ Ako varijabla okruženja nije postavljena, bit će zamijenjena praznim nizom.
|
||||
|
||||
---
|
||||
|
||||
### Fajlovi
|
||||
### Datoteke
|
||||
|
||||
Koristite `{file:path/to/file}` da zamijenite sadržaj fajla:
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Custom Tools
|
||||
title: Prilagođeni alati
|
||||
description: Kreirajte alate koje LLM može pozvati u otvorenom kodu.
|
||||
---
|
||||
|
||||
@@ -133,7 +133,7 @@ Koristite `context.worktree` za korijen git radnog stabla.
|
||||
|
||||
## Primjeri
|
||||
|
||||
### Napišite alat u Python-u
|
||||
### Pisanje alata u Python-u
|
||||
|
||||
Možete pisati svoje alate na bilo kom jeziku koji želite. Evo primjera koji zbraja dva broja koristeći Python.
|
||||
Prvo kreirajte alat kao Python skriptu:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Enterprise
|
||||
title: Za preduzeća
|
||||
description: Sigurno korištenje OpenCode u vašoj organizaciji.
|
||||
---
|
||||
|
||||
@@ -19,7 +19,7 @@ Da započnete s OpenCode Enterprise:
|
||||
|
||||
---
|
||||
|
||||
## Trial
|
||||
## Proba
|
||||
|
||||
OpenCode je otvorenog koda i ne pohranjuje vaš kod niti kontekstualne podatke, tako da vaši developeri mogu jednostavno [započeti](/docs/) i provesti probu.
|
||||
|
||||
@@ -60,19 +60,19 @@ Preporučujemo da ovo onemogućite tokom probe.
|
||||
|
||||
---
|
||||
|
||||
## Pricing
|
||||
## Cijene
|
||||
|
||||
Koristimo model naplate po sjedištu za OpenCode Enterprise. Ako imate vlastiti LLM gateway, ne naplaćujemo korištene tokene. Za više detalja o cijenama i opcijama implementacije, **<a href={email}>kontaktirajte nas</a>**.
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
## Postavljanje
|
||||
|
||||
Nakon što završite probni period i spremni ste koristiti OpenCode u svojoj organizaciji, možete **<a href={email}>kontaktirati nas</a>** da razgovaramo o cijenama i opcijama implementacije.
|
||||
|
||||
---
|
||||
|
||||
### Central Config
|
||||
### Centralna konfiguracija
|
||||
|
||||
Možemo postaviti OpenCode da koristi jednu centralnu konfiguraciju za cijelu organizaciju.
|
||||
|
||||
@@ -96,7 +96,7 @@ Također možete onemogućiti sve druge AI provajdere, čime osiguravate da svi
|
||||
|
||||
---
|
||||
|
||||
### Self-hosting
|
||||
### Samostalno hostovanje
|
||||
|
||||
Iako preporučujemo onemogućavanje share stranica kako biste osigurali da podaci nikada ne napuštaju vašu organizaciju, možemo vam pomoći i da ih samostalno hostujete na vlastitoj infrastrukturi.
|
||||
|
||||
@@ -104,17 +104,17 @@ Ovo je trenutno na našoj mapi puta. Ako ste zainteresovani, **<a href={email}>j
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
## Često postavljana pitanja
|
||||
|
||||
<details>
|
||||
<summary>What is OpenCode Enterprise?</summary>
|
||||
<summary>Šta je OpenCode Enterprise?</summary>
|
||||
|
||||
OpenCode Enterprise je za organizacije koje žele osigurati da njihov kod i podaci nikada ne napuštaju njihovu infrastrukturu. To omogućava centralizovana konfiguracija koja se integriše s vašim SSO-om i internim AI gateway-om.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>How do I get started with OpenCode Enterprise?</summary>
|
||||
<summary>Kako započeti s OpenCode Enterprise?</summary>
|
||||
|
||||
Jednostavno započnite internu probu sa svojim timom. OpenCode po defaultu ne pohranjuje vaš kod ni kontekstualne podatke, što olakšava početak.
|
||||
|
||||
@@ -123,21 +123,21 @@ Zatim **<a href={email}>kontaktirajte nas</a>** da razgovaramo o cijenama i opci
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>How does enterprise pricing work?</summary>
|
||||
<summary>Kako funkcionišu enterprise cijene?</summary>
|
||||
|
||||
Nudimo enterprise cijene po sjedištu. Ako imate vlastiti LLM gateway, ne naplaćujemo korištene tokene. Za više detalja, **<a href={email}>kontaktirajte nas</a>** za prilagođenu ponudu prema potrebama vaše organizacije.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Is my data secure with OpenCode Enterprise?</summary>
|
||||
<summary>Jesu li moji podaci sigurni uz OpenCode Enterprise?</summary>
|
||||
|
||||
Da. OpenCode ne pohranjuje vaš kod niti kontekstualne podatke. Sva obrada se odvija lokalno ili putem direktnih API poziva vašem AI provajderu. Uz centralnu konfiguraciju i SSO integraciju, vaši podaci ostaju sigurni unutar infrastrukture vaše organizacije.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Can we use our own private NPM registry?</summary>
|
||||
<summary>Možemo li koristiti vlastiti privatni NPM registar?</summary>
|
||||
|
||||
OpenCode podržava privatne npm registre kroz Bunovu izvornu podršku za `.npmrc` datoteku. Ako vaša organizacija koristi privatni registar, kao što je JFrog Artifactory, Nexus ili slično, osigurajte da su developeri autentifikovani prije pokretanja OpenCode.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Formatters
|
||||
title: Formateri
|
||||
description: OpenCode koristi formatere specifične za jezik.
|
||||
---
|
||||
|
||||
@@ -7,7 +7,7 @@ OpenCode automatski formatira datoteke nakon što su napisane ili uređene pomo
|
||||
|
||||
---
|
||||
|
||||
## Ugrađeno
|
||||
## Ugrađeni
|
||||
|
||||
OpenCode dolazi sa nekoliko ugrađenih formatera za popularne jezike i okvire. Ispod je lista formatera, podržanih ekstenzija datoteka i naredbi ili opcija konfiguracije koje su mu potrebne.
|
||||
| Formatter | Ekstenzije | Zahtjevi
|
||||
@@ -49,7 +49,7 @@ Kada OpenCode piše ili uređuje datoteku, on:
|
||||
|
||||
---
|
||||
|
||||
## Konfiguriši
|
||||
## Konfiguracija
|
||||
|
||||
Možete prilagoditi formatere kroz `formatter` odjeljak u vašoj OpenCode konfiguraciji.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ OpenCode se integriše sa vašim GitHub tokovom rada. Spomenite `/opencode` ili
|
||||
|
||||
---
|
||||
|
||||
## Karakteristike
|
||||
## Funkcije
|
||||
|
||||
- **Problemi trijaže**: Zamolite OpenCode da ispita problem i objasni vam ga.
|
||||
- **Popravi i implementiraj**: Zamolite OpenCode da popravi problem ili implementira funkciju. I radit će u novoj poslovnici i dostavljati PR sa svim promjenama.
|
||||
@@ -244,7 +244,7 @@ Za `issues` događaje, `prompt` unos je **potreban** jer nema komentara za izvla
|
||||
|
||||
---
|
||||
|
||||
## Prilagođene upite
|
||||
## Prilagođeni upiti
|
||||
|
||||
Zaobiđite zadani prompt da biste prilagodili ponašanje OpenCode za vaš tok posla.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Ovdje koristimo CI/CD komponentu kreiranu u zajednici za OpenCode — [nagyv/git
|
||||
|
||||
---
|
||||
|
||||
### Karakteristike
|
||||
### Funkcije
|
||||
|
||||
- **Koristite prilagođenu konfiguraciju po poslu**: Konfigurirajte OpenCode s prilagođenim konfiguracijskim direktorijem, na primjer `./config/#custom-directory` da omogućite ili onemogućite funkcionalnost po OpenCode pozivanju.
|
||||
- **Minimalno podešavanje**: CI komponenta postavlja OpenCode u pozadini, samo trebate kreirati OpenCode konfiguraciju i početnu prompt.
|
||||
@@ -49,7 +49,7 @@ Spomenite `@opencode` u komentaru i OpenCode će izvršiti zadatke unutar vašeg
|
||||
|
||||
---
|
||||
|
||||
### Karakteristike
|
||||
### Funkcije
|
||||
|
||||
- **Problemi trijaže**: Zamolite OpenCode da ispita problem i objasni vam ga.
|
||||
- **Popravi i implementiraj**: Zamolite OpenCode da popravi problem ili implementira funkciju.
|
||||
@@ -74,7 +74,7 @@ Pogledajte [**GitLab dokumente**](https://docs.gitlab.com/user/duo_agent_platfor
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Flow configuration</summary>
|
||||
<summary>Konfiguracija toka</summary>
|
||||
|
||||
```yaml
|
||||
image: node:22-slim
|
||||
|
||||
@@ -7,7 +7,7 @@ OpenCode se integriše sa VS kodom, Cursor-om ili bilo kojim IDE-om koji podrža
|
||||
|
||||
---
|
||||
|
||||
## Upotreba
|
||||
## Korištenje
|
||||
|
||||
- **Brzo pokretanje**: Koristite `Cmd+Esc` (Mac) ili `Ctrl+Esc` (Windows/Linux) da otvorite OpenCode u prikazu podijeljenog terminala ili fokusirajte postojeću terminalsku sesiju ako je već pokrenuta.
|
||||
- **Nova sesija**: Koristite `Cmd+Shift+Esc` (Mac) ili `Ctrl+Shift+Esc` (Windows/Linux) da započnete novu OpenCode terminalsku sesiju, čak i ako je ona već otvorena. Takođe možete kliknuti na dugme OpenCode u korisničkom sučelju.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user