mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-02 16:26:14 -04:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6a3be31de | |||
| 715265de4b | |||
| 2048f32491 | |||
| 2d7ba43a21 | |||
| bd198d8550 | |||
| cbffbcdd3d | |||
| 2be8b2269f | |||
| c1fa257a92 | |||
| 7fd81dd93e | |||
| d554e7aaef |
@@ -146,4 +146,3 @@
|
||||
| 2025-11-18 | 791,563 (+11,402) | 732,544 (+9,205) | 1,524,107 (+20,607) |
|
||||
| 2025-11-19 | 804,409 (+12,846) | 747,624 (+15,080) | 1,552,033 (+27,926) |
|
||||
| 2025-11-20 | 814,620 (+10,211) | 757,907 (+10,283) | 1,572,527 (+20,494) |
|
||||
| 2025-11-21 | 826,309 (+11,689) | 769,307 (+11,400) | 1,595,616 (+23,089) |
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -68,7 +68,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -92,7 +92,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -156,7 +156,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
@@ -172,7 +172,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -251,7 +251,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -271,7 +271,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -282,7 +282,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -295,7 +295,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -325,7 +325,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -335,7 +335,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -386,7 +386,7 @@
|
||||
"@tailwindcss/vite": "4.1.11",
|
||||
"@tsconfig/bun": "1.0.9",
|
||||
"@tsconfig/node22": "22.0.2",
|
||||
"@types/bun": "1.3.3",
|
||||
"@types/bun": "1.3.0",
|
||||
"@types/node": "22.13.9",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251014.1",
|
||||
"ai": "5.0.97",
|
||||
@@ -1410,7 +1410,7 @@
|
||||
|
||||
"@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
|
||||
"@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
|
||||
|
||||
"@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
|
||||
|
||||
@@ -1702,7 +1702,7 @@
|
||||
|
||||
"bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
|
||||
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
|
||||
+4
-7
@@ -97,12 +97,8 @@ export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint",
|
||||
],
|
||||
})
|
||||
|
||||
const ZEN_MODELS = [
|
||||
new sst.Secret("ZEN_MODELS1"),
|
||||
new sst.Secret("ZEN_MODELS2"),
|
||||
new sst.Secret("ZEN_MODELS3"),
|
||||
new sst.Secret("ZEN_MODELS4"),
|
||||
]
|
||||
const ZEN_MODELS1 = new sst.Secret("ZEN_MODELS1")
|
||||
const ZEN_MODELS2 = new sst.Secret("ZEN_MODELS2")
|
||||
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
|
||||
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||
properties: { value: auth.url.apply((url) => url!) },
|
||||
@@ -136,10 +132,11 @@ new sst.cloudflare.x.SolidStart("Console", {
|
||||
AUTH_API_URL,
|
||||
STRIPE_WEBHOOK_SECRET,
|
||||
STRIPE_SECRET_KEY,
|
||||
ZEN_MODELS1,
|
||||
ZEN_MODELS2,
|
||||
EMAILOCTOPUS_API_KEY,
|
||||
AWS_SES_ACCESS_KEY_ID,
|
||||
AWS_SES_SECRET_ACCESS_KEY,
|
||||
...ZEN_MODELS,
|
||||
...($dev
|
||||
? [
|
||||
new sst.Secret("CLOUDFLARE_DEFAULT_ACCOUNT_ID", process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID!),
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
set -euo pipefail
|
||||
APP=opencode
|
||||
|
||||
MUTED='\033[0;2m'
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
ORANGE='\033[38;2;255;140;0m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
@@ -44,11 +45,6 @@ case "$filename" in
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! command -v unzip >/dev/null 2>&1; then
|
||||
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INSTALL_DIR=$HOME/.opencode/bin
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
@@ -71,8 +67,8 @@ print_message() {
|
||||
local color=""
|
||||
|
||||
case $level in
|
||||
info) color="${NC}" ;;
|
||||
warning) color="${NC}" ;;
|
||||
info) color="${GREEN}" ;;
|
||||
warning) color="${YELLOW}" ;;
|
||||
error) color="${RED}" ;;
|
||||
esac
|
||||
|
||||
@@ -90,113 +86,18 @@ check_version() {
|
||||
installed_version=$(echo $installed_version | awk '{print $2}')
|
||||
|
||||
if [[ "$installed_version" != "$specific_version" ]]; then
|
||||
print_message info "${MUTED}Installed version: ${NC}$installed_version."
|
||||
print_message info "Installed version: ${YELLOW}$installed_version."
|
||||
else
|
||||
print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
|
||||
print_message info "Version ${YELLOW}$specific_version${GREEN} already installed"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
unbuffered_sed() {
|
||||
if echo | sed -u -e "" >/dev/null 2>&1; then
|
||||
sed -nu "$@"
|
||||
elif echo | sed -l -e "" >/dev/null 2>&1; then
|
||||
sed -nl "$@"
|
||||
else
|
||||
local pad="$(printf "\n%512s" "")"
|
||||
sed -ne "s/$/\\${pad}/" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
print_progress() {
|
||||
local bytes="$1"
|
||||
local length="$2"
|
||||
[ "$length" -gt 0 ] || return 0
|
||||
|
||||
local width=50
|
||||
local percent=$(( bytes * 100 / length ))
|
||||
[ "$percent" -gt 100 ] && percent=100
|
||||
local on=$(( percent * width / 100 ))
|
||||
local off=$(( width - on ))
|
||||
|
||||
local filled=$(printf "%*s" "$on" "")
|
||||
filled=${filled// /■}
|
||||
local empty=$(printf "%*s" "$off" "")
|
||||
empty=${empty// /・}
|
||||
|
||||
printf "\r${ORANGE}%s%s %3d%%${NC}" "$filled" "$empty" "$percent" >&4
|
||||
}
|
||||
|
||||
download_with_progress() {
|
||||
local url="$1"
|
||||
local output="$2"
|
||||
|
||||
if [ -t 2 ]; then
|
||||
exec 4>&2
|
||||
else
|
||||
exec 4>/dev/null
|
||||
fi
|
||||
|
||||
local tmp_dir=${TMPDIR:-/tmp}
|
||||
local basename="${tmp_dir}/opencode_install_$$"
|
||||
local tracefile="${basename}.trace"
|
||||
|
||||
rm -f "$tracefile"
|
||||
mkfifo "$tracefile"
|
||||
|
||||
# Hide cursor
|
||||
printf "\033[?25l" >&4
|
||||
|
||||
trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN
|
||||
|
||||
(
|
||||
curl --trace-ascii "$tracefile" -s -L -o "$output" "$url"
|
||||
) &
|
||||
local curl_pid=$!
|
||||
|
||||
unbuffered_sed \
|
||||
-e 'y/ACDEGHLNORTV/acdeghlnortv/' \
|
||||
-e '/^0000: content-length:/p' \
|
||||
-e '/^<= recv data/p' \
|
||||
"$tracefile" | \
|
||||
{
|
||||
local length=0
|
||||
local bytes=0
|
||||
|
||||
while IFS=" " read -r -a line; do
|
||||
[ "${#line[@]}" -lt 2 ] && continue
|
||||
local tag="${line[0]} ${line[1]}"
|
||||
|
||||
if [ "$tag" = "0000: content-length:" ]; then
|
||||
length="${line[2]}"
|
||||
length=$(echo "$length" | tr -d '\r')
|
||||
bytes=0
|
||||
elif [ "$tag" = "<= recv" ]; then
|
||||
local size="${line[3]}"
|
||||
bytes=$(( bytes + size ))
|
||||
if [ "$length" -gt 0 ]; then
|
||||
print_progress "$bytes" "$length"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
wait $curl_pid
|
||||
local ret=$?
|
||||
echo "" >&4
|
||||
return $ret
|
||||
}
|
||||
|
||||
download_and_install() {
|
||||
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version"
|
||||
print_message info "Downloading ${ORANGE}opencode ${GREEN}version: ${YELLOW}$specific_version ${GREEN}..."
|
||||
mkdir -p opencodetmp && cd opencodetmp
|
||||
|
||||
if [[ "$os" == "windows" ]] || ! download_with_progress "$url" "$filename"; then
|
||||
# Fallback to standard curl on Windows or if custom progress fails
|
||||
curl -# -L -o "$filename" "$url"
|
||||
fi
|
||||
|
||||
curl -# -L -o "$filename" "$url"
|
||||
unzip -q "$filename"
|
||||
mv opencode "$INSTALL_DIR"
|
||||
chmod 755 "${INSTALL_DIR}/opencode"
|
||||
@@ -216,7 +117,7 @@ add_to_path() {
|
||||
elif [[ -w $config_file ]]; then
|
||||
echo -e "\n# opencode" >> "$config_file"
|
||||
echo "$command" >> "$config_file"
|
||||
print_message info "${MUTED}Successfully added ${NC}opencode ${MUTED}to \$PATH in ${NC}$config_file"
|
||||
print_message info "Successfully added ${ORANGE}opencode ${GREEN}to \$PATH in $config_file"
|
||||
else
|
||||
print_message warning "Manually add the directory to $config_file (or similar):"
|
||||
print_message info " $command"
|
||||
@@ -290,20 +191,3 @@ if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
echo "$INSTALL_DIR" >> $GITHUB_PATH
|
||||
print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
echo -e "${MUTED} ${NC} ▄ "
|
||||
echo -e "${MUTED}█▀▀█ █▀▀█ █▀▀█ █▀▀▄ ${NC}█▀▀▀ █▀▀█ █▀▀█ █▀▀█"
|
||||
echo -e "${MUTED}█░░█ █░░█ █▀▀▀ █░░█ ${NC}█░░░ █░░█ █░░█ █▀▀▀"
|
||||
echo -e "${MUTED}▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ${NC}▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀"
|
||||
echo -e ""
|
||||
echo -e ""
|
||||
echo -e "${MUTED}To get started, navigate to a project and run:${NC}"
|
||||
echo -e "opencode ${MUTED}Use free models${NC}"
|
||||
echo -e "opencode auth login ${MUTED}Add paid provider API keys${NC}"
|
||||
echo -e "opencode help ${MUTED}List commands and options${NC}"
|
||||
echo -e ""
|
||||
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs"
|
||||
echo -e ""
|
||||
echo -e ""
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
"description": "AI-powered development tool",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.3.3",
|
||||
"packageManager": "bun@1.3.2",
|
||||
"scripts": {
|
||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
||||
"typecheck": "bun turbo typecheck",
|
||||
@@ -19,7 +19,7 @@
|
||||
"packages/slack"
|
||||
],
|
||||
"catalog": {
|
||||
"@types/bun": "1.3.3",
|
||||
"@types/bun": "1.3.0",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"ulid": "3.0.1",
|
||||
"@kobalte/core": "0.13.11",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
||||
"build": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||
"start": "vinxi start",
|
||||
"version": "1.0.90"
|
||||
"version": "1.0.85"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -11,21 +11,20 @@ const root = path.resolve(process.cwd(), "..", "..", "..")
|
||||
|
||||
// read the secret
|
||||
const ret = await $`bun sst secret list`.cwd(root).text()
|
||||
const lines = ret.split("\n")
|
||||
const value1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[1]
|
||||
const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
|
||||
const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
|
||||
const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
|
||||
const value1 = ret
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS1"))
|
||||
?.split("=")[1]
|
||||
const value2 = ret
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS2"))
|
||||
?.split("=")[1]
|
||||
if (!value1) throw new Error("ZEN_MODELS1 not found")
|
||||
if (!value2) throw new Error("ZEN_MODELS2 not found")
|
||||
if (!value3) throw new Error("ZEN_MODELS3 not found")
|
||||
if (!value4) throw new Error("ZEN_MODELS4 not found")
|
||||
|
||||
// validate value
|
||||
ZenData.validate(JSON.parse(value1 + value2 + value3 + value4))
|
||||
ZenData.validate(JSON.parse(value1 + value2))
|
||||
|
||||
// update the secret
|
||||
await $`bun sst secret set ZEN_MODELS1 ${value1} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${value2} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS3 ${value3} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS4 ${value4} --stage ${stage}`
|
||||
|
||||
@@ -9,20 +9,21 @@ const root = path.resolve(process.cwd(), "..", "..", "..")
|
||||
const models = await $`bun sst secret list`.cwd(root).text()
|
||||
|
||||
// read the line starting with "ZEN_MODELS"
|
||||
const lines = models.split("\n")
|
||||
const oldValue1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[1]
|
||||
const oldValue2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
|
||||
const oldValue3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
|
||||
const oldValue4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
|
||||
const oldValue1 = models
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS1"))
|
||||
?.split("=")[1]
|
||||
const oldValue2 = models
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS2"))
|
||||
?.split("=")[1]
|
||||
if (!oldValue1) throw new Error("ZEN_MODELS1 not found")
|
||||
if (!oldValue2) throw new Error("ZEN_MODELS2 not found")
|
||||
if (!oldValue3) throw new Error("ZEN_MODELS3 not found")
|
||||
if (!oldValue4) throw new Error("ZEN_MODELS4 not found")
|
||||
|
||||
// store the prettified json to a temp file
|
||||
const filename = `models-${Date.now()}.json`
|
||||
const tempFile = Bun.file(path.join(os.tmpdir(), filename))
|
||||
await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4), null, 2))
|
||||
await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2), null, 2))
|
||||
console.log("tempFile", tempFile.name)
|
||||
|
||||
// open temp file in vim and read the file on close
|
||||
@@ -31,12 +32,6 @@ const newValue = JSON.stringify(JSON.parse(await tempFile.text()))
|
||||
ZenData.validate(JSON.parse(newValue))
|
||||
|
||||
// update the secret
|
||||
const chunk = Math.ceil(newValue.length / 4)
|
||||
const newValue1 = newValue.slice(0, chunk)
|
||||
const newValue2 = newValue.slice(chunk, chunk * 2)
|
||||
const newValue3 = newValue.slice(chunk * 2, chunk * 3)
|
||||
const newValue4 = newValue.slice(chunk * 3)
|
||||
await $`bun sst secret set ZEN_MODELS1 ${newValue1}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${newValue2}`
|
||||
await $`bun sst secret set ZEN_MODELS3 ${newValue3}`
|
||||
await $`bun sst secret set ZEN_MODELS4 ${newValue4}`
|
||||
const mid = Math.floor(newValue.length / 2)
|
||||
await $`bun sst secret set ZEN_MODELS1 ${newValue.slice(0, mid)}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${newValue.slice(mid)}`
|
||||
|
||||
@@ -53,9 +53,7 @@ export namespace ZenData {
|
||||
})
|
||||
|
||||
export const list = fn(z.void(), () => {
|
||||
const json = JSON.parse(
|
||||
Resource.ZEN_MODELS1.value + Resource.ZEN_MODELS2.value + Resource.ZEN_MODELS3.value + Resource.ZEN_MODELS4.value,
|
||||
)
|
||||
const json = JSON.parse(Resource.ZEN_MODELS1.value + Resource.ZEN_MODELS2.value)
|
||||
return ModelsSchema.parse(json)
|
||||
})
|
||||
}
|
||||
|
||||
Vendored
-8
@@ -94,14 +94,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
// cloudflare
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
-8
@@ -94,14 +94,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
// cloudflare
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
-8
@@ -94,14 +94,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
// cloudflare
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The AI coding agent built for the terminal"
|
||||
version = "1.0.90"
|
||||
version = "1.0.85"
|
||||
schema_version = 1
|
||||
authors = ["Anomaly"]
|
||||
repository = "https://github.com/sst/opencode"
|
||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
||||
icon = "./icons/opencode.svg"
|
||||
|
||||
[agent_servers.opencode.targets.darwin-aarch64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.90/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.90/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.90/opencode-linux-arm64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-linux-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.90/opencode-linux-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-linux-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.90/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
Vendored
-8
@@ -94,14 +94,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
// cloudflare
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
|
||||
@@ -108,11 +108,9 @@ for (const item of targets) {
|
||||
plugins: [solidPlugin],
|
||||
sourcemap: "external",
|
||||
compile: {
|
||||
autoloadBunfig: false,
|
||||
autoloadDotenv: false,
|
||||
target: name.replace(pkg.name, "bun") as any,
|
||||
outfile: `dist/${name}/bin/opencode`,
|
||||
execArgv: [`--user-agent=opencode/${Script.version}`],
|
||||
execArgv: [`--user-agent=opencode/${Script.version}`, `--env-file=""`, `--`],
|
||||
windows: {},
|
||||
},
|
||||
entrypoints: ["./src/index.ts", parserWorker, workerPath],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createOpencodeClient, type Event } from "@opencode-ai/sdk"
|
||||
import { createSimpleContext } from "./helper"
|
||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||
import { batch, onCleanup } from "solid-js"
|
||||
import { onCleanup } from "solid-js"
|
||||
|
||||
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||
name: "SDK",
|
||||
@@ -17,42 +17,8 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||
}>()
|
||||
|
||||
sdk.event.subscribe().then(async (events) => {
|
||||
let queue: Event[] = []
|
||||
let timer: Timer | undefined
|
||||
let last = 0
|
||||
|
||||
const flush = () => {
|
||||
if (queue.length === 0) return
|
||||
const events = queue
|
||||
queue = []
|
||||
timer = undefined
|
||||
last = Date.now()
|
||||
// Batch all event emissions so all store updates result in a single render
|
||||
batch(() => {
|
||||
for (const event of events) {
|
||||
emitter.emit(event.type, event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for await (const event of events.stream) {
|
||||
queue.push(event)
|
||||
const elapsed = Date.now() - last
|
||||
|
||||
if (timer) continue
|
||||
// If we just flushed recently (within 16ms), batch this with future events
|
||||
// Otherwise, process immediately to avoid latency
|
||||
if (elapsed < 16) {
|
||||
timer = setTimeout(flush, 16)
|
||||
continue
|
||||
}
|
||||
flush()
|
||||
}
|
||||
|
||||
// Flush any remaining events
|
||||
if (timer) clearTimeout(timer)
|
||||
if (queue.length > 0) {
|
||||
flush()
|
||||
emitter.emit(event.type, event)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -189,7 +189,6 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
|
||||
})
|
||||
|
||||
const syntax = createMemo(() => generateSyntax(values()))
|
||||
const subtleSyntax = createMemo(() => generateSubtleSyntax(values()))
|
||||
|
||||
return {
|
||||
theme: new Proxy(values(), {
|
||||
@@ -205,7 +204,6 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
|
||||
return store.themes
|
||||
},
|
||||
syntax,
|
||||
subtleSyntax,
|
||||
mode() {
|
||||
return store.mode
|
||||
},
|
||||
@@ -429,35 +427,7 @@ function generateMutedTextColor(bg: RGBA, isDark: boolean): RGBA {
|
||||
}
|
||||
|
||||
function generateSyntax(theme: Theme) {
|
||||
return SyntaxStyle.fromTheme(getSyntaxRules(theme))
|
||||
}
|
||||
|
||||
function generateSubtleSyntax(theme: Theme) {
|
||||
const rules = getSyntaxRules(theme)
|
||||
return SyntaxStyle.fromTheme(
|
||||
rules.map((rule) => {
|
||||
if (rule.style.foreground) {
|
||||
const fg = rule.style.foreground
|
||||
return {
|
||||
...rule,
|
||||
style: {
|
||||
...rule.style,
|
||||
foreground: RGBA.fromInts(
|
||||
Math.round(fg.r * 255),
|
||||
Math.round(fg.g * 255),
|
||||
Math.round(fg.b * 255),
|
||||
Math.round(0.6 * 255),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
return rule
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function getSyntaxRules(theme: Theme) {
|
||||
return [
|
||||
return SyntaxStyle.fromTheme([
|
||||
{
|
||||
scope: ["prompt"],
|
||||
style: {
|
||||
@@ -951,5 +921,5 @@ function getSyntaxRules(theme: Theme) {
|
||||
foreground: theme.textMuted,
|
||||
},
|
||||
},
|
||||
]
|
||||
])
|
||||
}
|
||||
|
||||
@@ -653,50 +653,44 @@ export function Session() {
|
||||
},
|
||||
])
|
||||
|
||||
const revertInfo = createMemo(() => session()?.revert)
|
||||
const revertMessageID = createMemo(() => revertInfo()?.messageID)
|
||||
|
||||
const revertDiffFiles = createMemo(() => {
|
||||
const diffText = revertInfo()?.diff ?? ""
|
||||
if (!diffText) return []
|
||||
|
||||
try {
|
||||
const patches = parsePatch(diffText)
|
||||
return patches.map((patch) => {
|
||||
const filename = patch.newFileName || patch.oldFileName || "unknown"
|
||||
const cleanFilename = filename.replace(/^[ab]\//, "")
|
||||
return {
|
||||
filename: cleanFilename,
|
||||
additions: patch.hunks.reduce(
|
||||
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("+")).length,
|
||||
0,
|
||||
),
|
||||
deletions: patch.hunks.reduce(
|
||||
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("-")).length,
|
||||
0,
|
||||
),
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
const revertRevertedMessages = createMemo(() => {
|
||||
const messageID = revertMessageID()
|
||||
if (!messageID) return []
|
||||
return messages().filter((x) => x.id >= messageID && x.role === "user")
|
||||
})
|
||||
|
||||
const revert = createMemo(() => {
|
||||
const info = revertInfo()
|
||||
if (!info) return
|
||||
if (!info.messageID) return
|
||||
const s = session()
|
||||
if (!s) return
|
||||
const messageID = s.revert?.messageID
|
||||
if (!messageID) return
|
||||
const reverted = messages().filter((x) => x.id >= messageID && x.role === "user")
|
||||
|
||||
const diffFiles = (() => {
|
||||
const diffText = s.revert?.diff || ""
|
||||
if (!diffText) return []
|
||||
|
||||
try {
|
||||
const patches = parsePatch(diffText)
|
||||
return patches.map((patch) => {
|
||||
const filename = patch.newFileName || patch.oldFileName || "unknown"
|
||||
const cleanFilename = filename.replace(/^[ab]\//, "")
|
||||
return {
|
||||
filename: cleanFilename,
|
||||
additions: patch.hunks.reduce(
|
||||
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("+")).length,
|
||||
0,
|
||||
),
|
||||
deletions: patch.hunks.reduce(
|
||||
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("-")).length,
|
||||
0,
|
||||
),
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
})()
|
||||
|
||||
return {
|
||||
messageID: info.messageID,
|
||||
reverted: revertRevertedMessages(),
|
||||
diff: info.diff,
|
||||
diffFiles: revertDiffFiles(),
|
||||
messageID,
|
||||
reverted,
|
||||
diff: s.revert!.diff,
|
||||
diffFiles,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1080,7 +1074,7 @@ const PART_MAPPING = {
|
||||
}
|
||||
|
||||
function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: AssistantMessage }) {
|
||||
const { theme, subtleSyntax } = useTheme()
|
||||
const { theme, syntax } = useTheme()
|
||||
const ctx = use()
|
||||
const content = createMemo(() => props.part.text.trim())
|
||||
return (
|
||||
@@ -1098,10 +1092,10 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
|
||||
filetype="markdown"
|
||||
drawUnstyledText={false}
|
||||
streaming={true}
|
||||
syntaxStyle={subtleSyntax()}
|
||||
syntaxStyle={syntax()}
|
||||
content={"_Thinking:_ " + content()}
|
||||
conceal={ctx.conceal()}
|
||||
fg={theme.textMuted}
|
||||
fg={theme.text}
|
||||
/>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { InputRenderable, RGBA, ScrollBoxRenderable, TextAttributes } from "@opentui/core"
|
||||
import { useTheme } from "@tui/context/theme"
|
||||
import { entries, filter, flatMap, groupBy, pipe, take } from "remeda"
|
||||
import { batch, createEffect, createMemo, For, Show, type JSX } from "solid-js"
|
||||
import { batch, createEffect, createMemo, For, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
|
||||
import * as fuzzysort from "fuzzysort"
|
||||
|
||||
@@ -599,20 +599,77 @@ export namespace Provider {
|
||||
|
||||
const provider = s.providers[providerID]
|
||||
if (!provider) {
|
||||
const availableProviders = Object.keys(s.providers)
|
||||
const matches = fuzzysort.go(providerID, availableProviders, { limit: 3, threshold: -10000 })
|
||||
const suggestions = matches.map((m) => m.target)
|
||||
let suggestions: string[] = []
|
||||
const normalize = (str: string) => str.toLowerCase().replace(/[^a-z0-9]/g, "")
|
||||
const levenshtein = (a: string, b: string) => {
|
||||
const m = a.length,
|
||||
n = b.length
|
||||
const dp = Array.from({ length: m + 1 }, () => new Array<number>(n + 1).fill(0))
|
||||
for (let i = 0; i <= m; i++) dp[i][0] = i
|
||||
for (let j = 0; j <= n; j++) dp[0][j] = j
|
||||
for (let i = 1; i <= m; i++) {
|
||||
for (let j = 1; j <= n; j++) {
|
||||
const cost = a[i - 1] === b[j - 1] ? 0 : 1
|
||||
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost)
|
||||
}
|
||||
}
|
||||
return dp[m][n]
|
||||
}
|
||||
if (!modelID || modelID.trim() === "") {
|
||||
// Treat single-token input as an unqualified model; search across all providers' models.
|
||||
const q = normalize(providerID)
|
||||
const entries: { combo: string; norm: string }[] = []
|
||||
for (const [pid, prov] of Object.entries(s.providers)) {
|
||||
for (const mid of Object.keys(prov.info.models)) {
|
||||
entries.push({ combo: pid + "/" + mid, norm: normalize(mid) })
|
||||
}
|
||||
}
|
||||
const byNorm = fuzzysort.go(q, entries as any, { limit: 5, key: "norm" }).map((r: any) => r.obj.combo)
|
||||
const combos = entries.map((e) => e.combo)
|
||||
const byRaw = fuzzysort.go(providerID, combos, { limit: 5 }).map((r) => r.target)
|
||||
let merged = Array.from(new Set([...byNorm, ...byRaw]))
|
||||
if (merged.length === 0) {
|
||||
// fallback to edit distance on normalized mid
|
||||
const scored = entries
|
||||
.map((e) => ({ combo: e.combo, d: levenshtein(q, e.norm) }))
|
||||
.sort((a, b) => a.d - b.d)
|
||||
.slice(0, 3)
|
||||
.map((x) => x.combo)
|
||||
merged = scored
|
||||
}
|
||||
suggestions = merged.slice(0, 3)
|
||||
} else {
|
||||
const pcands = Object.keys(s.providers)
|
||||
const corpus = pcands.map((raw) => ({ raw, norm: normalize(raw) }))
|
||||
const q = normalize(providerID)
|
||||
const hits = fuzzysort.go(q, corpus as any, { limit: 5, key: "norm" })
|
||||
let ranked = hits.map((r: any) => r.obj.raw)
|
||||
if (ranked.length === 0) {
|
||||
ranked = pcands
|
||||
.map((p) => ({ p, d: levenshtein(q, normalize(p)) }))
|
||||
.sort((a, b) => a.d - b.d)
|
||||
.slice(0, 3)
|
||||
.map((x) => x.p)
|
||||
}
|
||||
const providerSuggestions = ranked.map((r) => r + "/" + modelID)
|
||||
suggestions = providerSuggestions
|
||||
}
|
||||
throw new ModelNotFoundError({ providerID, modelID, suggestions })
|
||||
}
|
||||
|
||||
const info = provider.info.models[modelID]
|
||||
if (!info) {
|
||||
const availableModels = Object.keys(provider.info.models)
|
||||
const matches = fuzzysort.go(modelID, availableModels, { limit: 3, threshold: -10000 })
|
||||
const suggestions = matches.map((m) => m.target)
|
||||
const candidates = Object.keys(provider.info.models)
|
||||
// Normalize punctuation differences like '-' vs '.' by stripping non-alphanumerics
|
||||
const normalize = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, "")
|
||||
const corpus = candidates.map((raw) => ({ raw, norm: normalize(raw) }))
|
||||
const query = normalize(modelID)
|
||||
const results = fuzzysort.go(query, corpus as any, { limit: 5, key: "norm" })
|
||||
const ranked = results.map((r) => ("obj" in r ? (r as any).obj.raw : (r as any).target)) as string[]
|
||||
const fallback = fuzzysort.go(modelID, candidates, { limit: 5 }).map((r) => r.target)
|
||||
const merged = Array.from(new Set([...ranked, ...fallback]))
|
||||
const suggestions = merged.slice(0, 3).map((m) => providerID + "/" + m)
|
||||
throw new ModelNotFoundError({ providerID, modelID, suggestions })
|
||||
}
|
||||
|
||||
const sdk = await getSDK(provider.info, info)
|
||||
|
||||
try {
|
||||
|
||||
@@ -147,7 +147,7 @@ export namespace ProviderTransform {
|
||||
result["promptCacheKey"] = sessionID
|
||||
}
|
||||
|
||||
if (providerID === "google" || (providerID.startsWith("opencode") && modelID.includes("gemini-3"))) {
|
||||
if (providerID === "google" || (providerID === "opencode" && modelID.includes("gemini-3"))) {
|
||||
result["thinkingConfig"] = {
|
||||
includeThoughts: true,
|
||||
}
|
||||
@@ -166,7 +166,7 @@ export namespace ProviderTransform {
|
||||
result["textVerbosity"] = "low"
|
||||
}
|
||||
|
||||
if (providerID.startsWith("opencode")) {
|
||||
if (providerID === "opencode") {
|
||||
result["promptCacheKey"] = sessionID
|
||||
result["include"] = ["reasoning.encrypted_content"]
|
||||
result["reasoningSummary"] = "auto"
|
||||
|
||||
@@ -813,18 +813,8 @@ export namespace Server {
|
||||
async (c) => {
|
||||
const id = c.req.valid("param").id
|
||||
const body = c.req.valid("json")
|
||||
const msgs = await Session.messages({ sessionID: id })
|
||||
let currentAgent = "build"
|
||||
for (let i = msgs.length - 1; i >= 0; i--) {
|
||||
const info = msgs[i].info
|
||||
if (info.role === "user") {
|
||||
currentAgent = info.agent || "build"
|
||||
break
|
||||
}
|
||||
}
|
||||
await SessionCompaction.create({
|
||||
sessionID: id,
|
||||
agent: currentAgent,
|
||||
model: {
|
||||
providerID: body.providerID,
|
||||
modelID: body.modelID,
|
||||
|
||||
@@ -92,7 +92,6 @@ export namespace SessionCompaction {
|
||||
providerID: string
|
||||
modelID: string
|
||||
}
|
||||
agent: string
|
||||
abort: AbortSignal
|
||||
}) {
|
||||
const model = await Provider.getModel(input.model.providerID, input.model.modelID)
|
||||
@@ -102,7 +101,7 @@ export namespace SessionCompaction {
|
||||
role: "assistant",
|
||||
parentID: input.parentID,
|
||||
sessionID: input.sessionID,
|
||||
mode: input.agent,
|
||||
mode: "build",
|
||||
summary: true,
|
||||
path: {
|
||||
cwd: Instance.directory,
|
||||
@@ -200,7 +199,7 @@ export namespace SessionCompaction {
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
agent: input.agent,
|
||||
agent: "build",
|
||||
model: input.model,
|
||||
})
|
||||
await Session.updatePart({
|
||||
@@ -223,7 +222,6 @@ export namespace SessionCompaction {
|
||||
export const create = fn(
|
||||
z.object({
|
||||
sessionID: Identifier.schema("session"),
|
||||
agent: z.string(),
|
||||
model: z.object({
|
||||
providerID: z.string(),
|
||||
modelID: z.string(),
|
||||
@@ -235,7 +233,7 @@ export namespace SessionCompaction {
|
||||
role: "user",
|
||||
model: input.model,
|
||||
sessionID: input.sessionID,
|
||||
agent: input.agent,
|
||||
agent: "build",
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
|
||||
@@ -265,11 +265,7 @@ export namespace SessionPrompt {
|
||||
}
|
||||
|
||||
if (!lastUser) throw new Error("No user message found in stream. This should never happen.")
|
||||
if (
|
||||
lastAssistant?.finish &&
|
||||
!["tool-calls", "unknown"].includes(lastAssistant.finish) &&
|
||||
lastUser.id < lastAssistant.id
|
||||
) {
|
||||
if (lastAssistant?.finish && lastAssistant.finish !== "tool-calls" && lastUser.id < lastAssistant.id) {
|
||||
log.info("exiting loop", { sessionID })
|
||||
break
|
||||
}
|
||||
@@ -402,7 +398,6 @@ export namespace SessionPrompt {
|
||||
messages: msgs,
|
||||
parentID: lastUser.id,
|
||||
abort,
|
||||
agent: lastUser.agent,
|
||||
model: {
|
||||
providerID: model.providerID,
|
||||
modelID: model.modelID,
|
||||
@@ -421,7 +416,6 @@ export namespace SessionPrompt {
|
||||
) {
|
||||
await SessionCompaction.create({
|
||||
sessionID,
|
||||
agent: lastUser.agent,
|
||||
model: lastUser.model,
|
||||
})
|
||||
continue
|
||||
|
||||
+229
-255
@@ -14,7 +14,6 @@ import { Permission } from "@/permission"
|
||||
import { fileURLToPath } from "url"
|
||||
import { Flag } from "@/flag/flag.ts"
|
||||
import path from "path"
|
||||
import { iife } from "@/util/iife"
|
||||
|
||||
const DEFAULT_MAX_OUTPUT_LENGTH = 30_000
|
||||
const MAX_OUTPUT_LENGTH = (() => {
|
||||
@@ -55,283 +54,258 @@ const parser = lazy(async () => {
|
||||
return p
|
||||
})
|
||||
|
||||
// TODO: we may wanna rename this tool so it works better on other shells
|
||||
|
||||
export const BashTool = Tool.define("bash", async () => {
|
||||
const shell = iife(() => {
|
||||
const s = process.env.SHELL
|
||||
if (s) {
|
||||
if (!new Set(["/bin/fish", "/bin/nu", "/usr/bin/fish", "/usr/bin/nu"]).has(s)) {
|
||||
return s
|
||||
}
|
||||
export const BashTool = Tool.define("bash", {
|
||||
description: DESCRIPTION,
|
||||
parameters: z.object({
|
||||
command: z.string().describe("The command to execute"),
|
||||
timeout: z.number().describe("Optional timeout in milliseconds").optional(),
|
||||
description: z
|
||||
.string()
|
||||
.describe(
|
||||
"Clear, concise description of what this command does in 5-10 words. Examples:\nInput: ls\nOutput: Lists files in current directory\n\nInput: git status\nOutput: Shows working tree status\n\nInput: npm install\nOutput: Installs package dependencies\n\nInput: mkdir foo\nOutput: Creates directory 'foo'",
|
||||
),
|
||||
}),
|
||||
async execute(params, ctx) {
|
||||
if (params.timeout !== undefined && params.timeout < 0) {
|
||||
throw new Error(`Invalid timeout value: ${params.timeout}. Timeout must be a positive number.`)
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
return "/bin/zsh"
|
||||
const timeout = Math.min(params.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT)
|
||||
const tree = await parser().then((p) => p.parse(params.command))
|
||||
if (!tree) {
|
||||
throw new Error("Failed to parse command")
|
||||
}
|
||||
const agent = await Agent.get(ctx.agent)
|
||||
const permissions = agent.permission.bash
|
||||
|
||||
const bash = Bun.which("bash")
|
||||
if (bash) {
|
||||
return bash
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
log.info("bash tool using shell", { shell })
|
||||
|
||||
return {
|
||||
description: DESCRIPTION,
|
||||
parameters: z.object({
|
||||
command: z.string().describe("The command to execute"),
|
||||
timeout: z.number().describe("Optional timeout in milliseconds").optional(),
|
||||
description: z
|
||||
.string()
|
||||
.describe(
|
||||
"Clear, concise description of what this command does in 5-10 words. Examples:\nInput: ls\nOutput: Lists files in current directory\n\nInput: git status\nOutput: Shows working tree status\n\nInput: npm install\nOutput: Installs package dependencies\n\nInput: mkdir foo\nOutput: Creates directory 'foo'",
|
||||
),
|
||||
}),
|
||||
async execute(params, ctx) {
|
||||
if (params.timeout !== undefined && params.timeout < 0) {
|
||||
throw new Error(`Invalid timeout value: ${params.timeout}. Timeout must be a positive number.`)
|
||||
}
|
||||
const timeout = Math.min(params.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT)
|
||||
const tree = await parser().then((p) => p.parse(params.command))
|
||||
if (!tree) {
|
||||
throw new Error("Failed to parse command")
|
||||
}
|
||||
const agent = await Agent.get(ctx.agent)
|
||||
const permissions = agent.permission.bash
|
||||
|
||||
const askPatterns = new Set<string>()
|
||||
for (const node of tree.rootNode.descendantsOfType("command")) {
|
||||
if (!node) continue
|
||||
const command = []
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i)
|
||||
if (!child) continue
|
||||
if (
|
||||
child.type !== "command_name" &&
|
||||
child.type !== "word" &&
|
||||
child.type !== "string" &&
|
||||
child.type !== "raw_string" &&
|
||||
child.type !== "concatenation"
|
||||
) {
|
||||
continue
|
||||
}
|
||||
command.push(child.text)
|
||||
const askPatterns = new Set<string>()
|
||||
for (const node of tree.rootNode.descendantsOfType("command")) {
|
||||
if (!node) continue
|
||||
const command = []
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i)
|
||||
if (!child) continue
|
||||
if (
|
||||
child.type !== "command_name" &&
|
||||
child.type !== "word" &&
|
||||
child.type !== "string" &&
|
||||
child.type !== "raw_string" &&
|
||||
child.type !== "concatenation"
|
||||
) {
|
||||
continue
|
||||
}
|
||||
command.push(child.text)
|
||||
}
|
||||
|
||||
// not an exhaustive list, but covers most common cases
|
||||
if (["cd", "rm", "cp", "mv", "mkdir", "touch", "chmod", "chown"].includes(command[0])) {
|
||||
for (const arg of command.slice(1)) {
|
||||
if (arg.startsWith("-") || (command[0] === "chmod" && arg.startsWith("+"))) continue
|
||||
const resolved = await $`realpath ${arg}`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.text()
|
||||
.then((x) => x.trim())
|
||||
log.info("resolved path", { arg, resolved })
|
||||
if (resolved) {
|
||||
// Git Bash on Windows returns Unix-style paths like /c/Users/...
|
||||
const normalized =
|
||||
process.platform === "win32" && resolved.match(/^\/[a-z]\//)
|
||||
? resolved.replace(/^\/([a-z])\//, (_, drive) => `${drive.toUpperCase()}:\\`).replace(/\//g, "\\")
|
||||
: resolved
|
||||
// not an exhaustive list, but covers most common cases
|
||||
if (["cd", "rm", "cp", "mv", "mkdir", "touch", "chmod", "chown"].includes(command[0])) {
|
||||
for (const arg of command.slice(1)) {
|
||||
if (arg.startsWith("-") || (command[0] === "chmod" && arg.startsWith("+"))) continue
|
||||
const resolved = await $`realpath ${arg}`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.text()
|
||||
.then((x) => x.trim())
|
||||
log.info("resolved path", { arg, resolved })
|
||||
if (resolved) {
|
||||
// Git Bash on Windows returns Unix-style paths like /c/Users/...
|
||||
const normalized =
|
||||
process.platform === "win32" && resolved.match(/^\/[a-z]\//)
|
||||
? resolved.replace(/^\/([a-z])\//, (_, drive) => `${drive.toUpperCase()}:\\`).replace(/\//g, "\\")
|
||||
: resolved
|
||||
|
||||
if (!Filesystem.contains(Instance.directory, normalized)) {
|
||||
const parentDir = path.dirname(normalized)
|
||||
if (agent.permission.external_directory === "ask") {
|
||||
await Permission.ask({
|
||||
type: "external_directory",
|
||||
pattern: [parentDir, path.join(parentDir, "*")],
|
||||
sessionID: ctx.sessionID,
|
||||
messageID: ctx.messageID,
|
||||
callID: ctx.callID,
|
||||
title: `This command references paths outside of ${Instance.directory}`,
|
||||
metadata: {
|
||||
command: params.command,
|
||||
},
|
||||
})
|
||||
} else if (agent.permission.external_directory === "deny") {
|
||||
throw new Permission.RejectedError(
|
||||
ctx.sessionID,
|
||||
"external_directory",
|
||||
ctx.callID,
|
||||
{
|
||||
command: params.command,
|
||||
},
|
||||
`This command references paths outside of ${Instance.directory} so it is not allowed to be executed.`,
|
||||
)
|
||||
}
|
||||
if (!Filesystem.contains(Instance.directory, normalized)) {
|
||||
const parentDir = path.dirname(normalized)
|
||||
if (agent.permission.external_directory === "ask") {
|
||||
await Permission.ask({
|
||||
type: "external_directory",
|
||||
pattern: [parentDir, path.join(parentDir, "*")],
|
||||
sessionID: ctx.sessionID,
|
||||
messageID: ctx.messageID,
|
||||
callID: ctx.callID,
|
||||
title: `This command references paths outside of ${Instance.directory}`,
|
||||
metadata: {
|
||||
command: params.command,
|
||||
},
|
||||
})
|
||||
} else if (agent.permission.external_directory === "deny") {
|
||||
throw new Permission.RejectedError(
|
||||
ctx.sessionID,
|
||||
"external_directory",
|
||||
ctx.callID,
|
||||
{
|
||||
command: params.command,
|
||||
},
|
||||
`This command references paths outside of ${Instance.directory} so it is not allowed to be executed.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// always allow cd if it passes above check
|
||||
if (command[0] !== "cd") {
|
||||
const action = Wildcard.allStructured({ head: command[0], tail: command.slice(1) }, permissions)
|
||||
if (action === "deny") {
|
||||
throw new Error(
|
||||
`The user has specifically restricted access to this command, you are not allowed to execute it. Here is the configuration: ${JSON.stringify(permissions)}`,
|
||||
)
|
||||
}
|
||||
if (action === "ask") {
|
||||
const pattern = (() => {
|
||||
if (command.length === 0) return
|
||||
const head = command[0]
|
||||
// Find first non-flag argument as subcommand
|
||||
const sub = command.slice(1).find((arg) => !arg.startsWith("-"))
|
||||
return sub ? `${head} ${sub} *` : `${head} *`
|
||||
})()
|
||||
if (pattern) {
|
||||
askPatterns.add(pattern)
|
||||
}
|
||||
// always allow cd if it passes above check
|
||||
if (command[0] !== "cd") {
|
||||
const action = Wildcard.allStructured({ head: command[0], tail: command.slice(1) }, permissions)
|
||||
if (action === "deny") {
|
||||
throw new Error(
|
||||
`The user has specifically restricted access to this command, you are not allowed to execute it. Here is the configuration: ${JSON.stringify(permissions)}`,
|
||||
)
|
||||
}
|
||||
if (action === "ask") {
|
||||
const pattern = (() => {
|
||||
if (command.length === 0) return
|
||||
const head = command[0]
|
||||
// Find first non-flag argument as subcommand
|
||||
const sub = command.slice(1).find((arg) => !arg.startsWith("-"))
|
||||
return sub ? `${head} ${sub} *` : `${head} *`
|
||||
})()
|
||||
if (pattern) {
|
||||
askPatterns.add(pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (askPatterns.size > 0) {
|
||||
const patterns = Array.from(askPatterns)
|
||||
await Permission.ask({
|
||||
type: "bash",
|
||||
pattern: patterns,
|
||||
sessionID: ctx.sessionID,
|
||||
messageID: ctx.messageID,
|
||||
callID: ctx.callID,
|
||||
title: params.command,
|
||||
metadata: {
|
||||
command: params.command,
|
||||
patterns,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const proc = spawn(params.command, {
|
||||
shell,
|
||||
cwd: Instance.directory,
|
||||
env: {
|
||||
...process.env,
|
||||
if (askPatterns.size > 0) {
|
||||
const patterns = Array.from(askPatterns)
|
||||
await Permission.ask({
|
||||
type: "bash",
|
||||
pattern: patterns,
|
||||
sessionID: ctx.sessionID,
|
||||
messageID: ctx.messageID,
|
||||
callID: ctx.callID,
|
||||
title: params.command,
|
||||
metadata: {
|
||||
command: params.command,
|
||||
patterns,
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
detached: process.platform !== "win32",
|
||||
})
|
||||
}
|
||||
|
||||
let output = ""
|
||||
const proc = spawn(params.command, {
|
||||
shell: true,
|
||||
cwd: Instance.directory,
|
||||
env: {
|
||||
...process.env,
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
detached: process.platform !== "win32",
|
||||
})
|
||||
|
||||
// Initialize metadata with empty output
|
||||
let output = ""
|
||||
|
||||
// Initialize metadata with empty output
|
||||
ctx.metadata({
|
||||
metadata: {
|
||||
output: "",
|
||||
description: params.description,
|
||||
},
|
||||
})
|
||||
|
||||
const append = (chunk: Buffer) => {
|
||||
output += chunk.toString()
|
||||
ctx.metadata({
|
||||
metadata: {
|
||||
output: "",
|
||||
description: params.description,
|
||||
},
|
||||
})
|
||||
|
||||
const append = (chunk: Buffer) => {
|
||||
output += chunk.toString()
|
||||
ctx.metadata({
|
||||
metadata: {
|
||||
output,
|
||||
description: params.description,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
proc.stdout?.on("data", append)
|
||||
proc.stderr?.on("data", append)
|
||||
|
||||
let timedOut = false
|
||||
let aborted = false
|
||||
let exited = false
|
||||
|
||||
const killTree = async () => {
|
||||
const pid = proc.pid
|
||||
if (!pid || exited) {
|
||||
return
|
||||
}
|
||||
|
||||
if (process.platform === "win32") {
|
||||
await new Promise<void>((resolve) => {
|
||||
const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], { stdio: "ignore" })
|
||||
killer.once("exit", resolve)
|
||||
killer.once("error", resolve)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
process.kill(-pid, "SIGTERM")
|
||||
await Bun.sleep(SIGKILL_TIMEOUT_MS)
|
||||
if (!exited) {
|
||||
process.kill(-pid, "SIGKILL")
|
||||
}
|
||||
} catch (_e) {
|
||||
proc.kill("SIGTERM")
|
||||
await Bun.sleep(SIGKILL_TIMEOUT_MS)
|
||||
if (!exited) {
|
||||
proc.kill("SIGKILL")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.abort.aborted) {
|
||||
aborted = true
|
||||
await killTree()
|
||||
}
|
||||
|
||||
const abortHandler = () => {
|
||||
aborted = true
|
||||
void killTree()
|
||||
}
|
||||
|
||||
ctx.abort.addEventListener("abort", abortHandler, { once: true })
|
||||
|
||||
const timeoutTimer = setTimeout(() => {
|
||||
timedOut = true
|
||||
void killTree()
|
||||
}, timeout)
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const cleanup = () => {
|
||||
clearTimeout(timeoutTimer)
|
||||
ctx.abort.removeEventListener("abort", abortHandler)
|
||||
}
|
||||
|
||||
proc.once("exit", () => {
|
||||
exited = true
|
||||
cleanup()
|
||||
resolve()
|
||||
})
|
||||
|
||||
proc.once("error", (error) => {
|
||||
exited = true
|
||||
cleanup()
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
|
||||
if (output.length > MAX_OUTPUT_LENGTH) {
|
||||
output = output.slice(0, MAX_OUTPUT_LENGTH)
|
||||
output += "\n\n(Output was truncated due to length limit)"
|
||||
}
|
||||
|
||||
if (timedOut) {
|
||||
output += `\n\n(Command timed out after ${timeout} ms)`
|
||||
}
|
||||
|
||||
if (aborted) {
|
||||
output += "\n\n(Command was aborted)"
|
||||
}
|
||||
|
||||
return {
|
||||
title: params.description,
|
||||
metadata: {
|
||||
output,
|
||||
exit: proc.exitCode,
|
||||
description: params.description,
|
||||
},
|
||||
output,
|
||||
})
|
||||
}
|
||||
|
||||
proc.stdout?.on("data", append)
|
||||
proc.stderr?.on("data", append)
|
||||
|
||||
let timedOut = false
|
||||
let aborted = false
|
||||
let exited = false
|
||||
|
||||
const killTree = async () => {
|
||||
const pid = proc.pid
|
||||
if (!pid || exited) {
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (process.platform === "win32") {
|
||||
await new Promise<void>((resolve) => {
|
||||
const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], { stdio: "ignore" })
|
||||
killer.once("exit", resolve)
|
||||
killer.once("error", resolve)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
process.kill(-pid, "SIGTERM")
|
||||
await Bun.sleep(SIGKILL_TIMEOUT_MS)
|
||||
if (!exited) {
|
||||
process.kill(-pid, "SIGKILL")
|
||||
}
|
||||
} catch (_e) {
|
||||
proc.kill("SIGTERM")
|
||||
await Bun.sleep(SIGKILL_TIMEOUT_MS)
|
||||
if (!exited) {
|
||||
proc.kill("SIGKILL")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.abort.aborted) {
|
||||
aborted = true
|
||||
await killTree()
|
||||
}
|
||||
|
||||
const abortHandler = () => {
|
||||
aborted = true
|
||||
void killTree()
|
||||
}
|
||||
|
||||
ctx.abort.addEventListener("abort", abortHandler, { once: true })
|
||||
|
||||
const timeoutTimer = setTimeout(() => {
|
||||
timedOut = true
|
||||
void killTree()
|
||||
}, timeout)
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const cleanup = () => {
|
||||
clearTimeout(timeoutTimer)
|
||||
ctx.abort.removeEventListener("abort", abortHandler)
|
||||
}
|
||||
|
||||
proc.once("exit", () => {
|
||||
exited = true
|
||||
cleanup()
|
||||
resolve()
|
||||
})
|
||||
|
||||
proc.once("error", (error) => {
|
||||
exited = true
|
||||
cleanup()
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
|
||||
if (output.length > MAX_OUTPUT_LENGTH) {
|
||||
output = output.slice(0, MAX_OUTPUT_LENGTH)
|
||||
output += "\n\n(Output was truncated due to length limit)"
|
||||
}
|
||||
|
||||
if (timedOut) {
|
||||
output += `\n\n(Command timed out after ${timeout} ms)`
|
||||
}
|
||||
|
||||
if (aborted) {
|
||||
output += "\n\n(Command was aborted)"
|
||||
}
|
||||
|
||||
return {
|
||||
title: params.description,
|
||||
metadata: {
|
||||
output,
|
||||
exit: proc.exitCode,
|
||||
description: params.description,
|
||||
},
|
||||
output,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
@@ -24,4 +24,4 @@
|
||||
"typescript": "catalog:",
|
||||
"@typescript/native-preview": "catalog:"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
@@ -26,4 +26,4 @@
|
||||
"publishConfig": {
|
||||
"directory": "dist"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +90,4 @@ class Resource:
|
||||
class ZEN_MODELS2:
|
||||
type: str
|
||||
value: str
|
||||
class ZEN_MODELS3:
|
||||
type: str
|
||||
value: str
|
||||
class ZEN_MODELS4:
|
||||
type: str
|
||||
value: str
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/components/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
/* This file is auto-generated by SST. Do not edit. */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* deno-fmt-ignore-file */
|
||||
|
||||
/// <reference path="../../sst-env.d.ts" />
|
||||
|
||||
import "sst"
|
||||
export {}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -133,10 +133,10 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**.
|
||||
| Claude Opus 4.1 | $15.00 | $75.00 | $1.50 | $18.75 |
|
||||
| Gemini 3 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - |
|
||||
| Gemini 3 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - |
|
||||
| GPT 5.1 | $1.07 | $8.50 | $0.107 | - |
|
||||
| GPT 5.1 Codex | $1.07 | $8.50 | $0.107 | - |
|
||||
| GPT 5 | $1.07 | $8.50 | $0.107 | - |
|
||||
| GPT 5 Codex | $1.07 | $8.50 | $0.107 | - |
|
||||
| GPT 5.1 | $1.25 | $10.00 | $0.125 | - |
|
||||
| GPT 5.1 Codex | $1.25 | $10.00 | $0.125 | - |
|
||||
| GPT 5 | $1.25 | $10.00 | $0.125 | - |
|
||||
| GPT 5 Codex | $1.25 | $10.00 | $0.125 | - |
|
||||
| GPT 5 Nano | Free | Free | Free | - |
|
||||
|
||||
You might notice _Claude Haiku 3.5_ in your usage history. This is a [low cost model](/docs/config/#models) that's used to generate the titles of your sessions.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.0.90",
|
||||
"version": "1.0.85",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Vendored
-8
@@ -116,14 +116,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <reference path="sst-env.d.ts" />
|
||||
|
||||
Reference in New Issue
Block a user