Merge branch 'dev' into opentui

This commit is contained in:
Dax Raad 2025-10-29 10:38:48 -04:00
commit 8b2ce5486d
285 changed files with 23552 additions and 528 deletions

71
.github/publish-python-sdk.yml vendored Normal file
View file

@ -0,0 +1,71 @@
#
# This file is intentionally in the wrong dir, will move and add later....
#
# name: publish-python-sdk
# on:
# release:
# types: [published]
# workflow_dispatch:
# jobs:
# publish:
# runs-on: ubuntu-latest
# permissions:
# contents: read
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# - name: Setup Bun
# uses: oven-sh/setup-bun@v1
# with:
# bun-version: 1.2.21
# - name: Install dependencies (JS/Bun)
# run: bun install
# - name: Install uv
# shell: bash
# run: curl -LsSf https://astral.sh/uv/install.sh | sh
# - name: Generate Python SDK from OpenAPI (CLI)
# shell: bash
# run: |
# ~/.local/bin/uv run --project packages/sdk/python python packages/sdk/python/scripts/generate.py --source cli
# - name: Sync Python dependencies
# shell: bash
# run: |
# ~/.local/bin/uv sync --dev --project packages/sdk/python
# - name: Set version from release tag
# shell: bash
# run: |
# TAG="${GITHUB_REF_NAME:-}"
# if [ -z "$TAG" ]; then
# TAG="$(git describe --tags --abbrev=0 || echo 0.0.0)"
# fi
# echo "Using version: $TAG"
# VERSION="$TAG" ~/.local/bin/uv run --project packages/sdk/python python - <<'PY'
# import os, re, pathlib
# root = pathlib.Path('packages/sdk/python')
# pt = (root / 'pyproject.toml').read_text()
# version = os.environ.get('VERSION','0.0.0').lstrip('v')
# pt = re.sub(r'(?m)^(version\s*=\s*")[^"]+("\s*)$', f"\\1{version}\\2", pt)
# (root / 'pyproject.toml').write_text(pt)
# # Also update generator config override for consistency
# cfgp = root / 'openapi-python-client.yaml'
# if cfgp.exists():
# cfg = cfgp.read_text()
# cfg = re.sub(r'(?m)^(package_version_override:\s*)\S+$', f"\\1{version}", cfg)
# cfgp.write_text(cfg)
# PY
# - name: Build and publish to PyPI
# env:
# PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
# shell: bash
# run: |
# ~/.local/bin/uv run --project packages/sdk/python python packages/sdk/python/scripts/publish.py

View file

@ -53,13 +53,17 @@ jobs:
- name: Install OpenCode
run: curl -fsSL https://opencode.ai/install | bash
- name: Setup npm auth
run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
- name: Publish
run: |
./script/publish.ts
env:
OPENCODE_BUMP: ${{ inputs.bump }}
OPENCODE_CHANNEL: latest
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}

View file

@ -122,3 +122,4 @@
| 2025-10-26 | 584,409 (+5,482) | 521,179 (+5,050) | 1,105,588 (+10,532) |
| 2025-10-27 | 589,999 (+5,590) | 526,001 (+4,822) | 1,116,000 (+10,412) |
| 2025-10-28 | 595,776 (+5,777) | 532,438 (+6,437) | 1,128,214 (+12,214) |
| 2025-10-29 | 606,259 (+10,483) | 542,064 (+9,626) | 1,148,323 (+20,109) |

View file

@ -39,7 +39,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@ -66,7 +66,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@ -90,7 +90,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@ -111,12 +111,12 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@pierre/precision-diffs": "0.3.5",
"@pierre/precision-diffs": "catalog:",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3",
"@solid-primitives/event-bus": "1.1.2",
@ -152,7 +152,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0",
@ -168,7 +168,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "0.15.20",
"version": "0.15.23",
"bin": {
"opencode": "./bin/opencode",
},
@ -244,7 +244,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
@ -264,7 +264,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "0.15.20",
"version": "0.15.23",
"devDependencies": {
"@hey-api/openapi-ts": "0.81.0",
"@tsconfig/node22": "catalog:",
@ -275,7 +275,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@ -288,7 +288,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@kobalte/core": "catalog:",
"@pierre/precision-diffs": "catalog:",
@ -311,7 +311,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@ -358,7 +358,7 @@
"@hono/zod-validator": "0.4.2",
"@kobalte/core": "0.13.11",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/precision-diffs": "0.3.2",
"@pierre/precision-diffs": "0.3.6",
"@solidjs/meta": "0.29.4",
"@tailwindcss/vite": "4.1.11",
"@tsconfig/bun": "1.0.9",
@ -1027,7 +1027,7 @@
"@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="],
"@pierre/precision-diffs": ["@pierre/precision-diffs@0.3.5", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/transformers": "3.13.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.13.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-qbotIS8CahO/7guljDzU3RVpDfg6WViWe0EB0/SZQi3xHD+nzxxlC+pGoyIFSn+47GG0EKxTnvkfaYANm19FCA=="],
"@pierre/precision-diffs": ["@pierre/precision-diffs@0.3.6", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/transformers": "3.13.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.13.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-cKM3HcMmyr5wPFll0bHYcgHplcHgMlL6Dw4Pi4giL0jVt7ySlGwwVyXTRFW5Fva43stOL+EWB+9U5VBDSktBJA=="],
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
@ -1113,7 +1113,7 @@
"@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="],
"@rollup/plugin-replace": ["@rollup/plugin-replace@6.0.2", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "magic-string": "^0.30.3" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ=="],
"@rollup/plugin-replace": ["@rollup/plugin-replace@6.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "magic-string": "^0.30.3" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA=="],
"@rollup/plugin-terser": ["@rollup/plugin-terser@0.4.4", "", { "dependencies": { "serialize-javascript": "^6.0.1", "smob": "^1.0.0", "terser": "^5.17.4" }, "peerDependencies": { "rollup": "^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A=="],
@ -1441,7 +1441,7 @@
"@types/scheduler": ["@types/scheduler@0.26.0", "", {}, "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA=="],
"@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="],
"@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="],
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
@ -1583,7 +1583,7 @@
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
"axios": ["axios@1.13.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-zt40Pz4zcRXra9CVV31KeyofwiNvAbJ5B6YPz9pMJ+yOSLikvPT4Yi5LjfgjRa9CawVYBaD1JQzIVcIvBejKeA=="],
"axios": ["axios@1.13.1", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
@ -1617,7 +1617,7 @@
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.20", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q=="],
"bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
@ -1913,7 +1913,7 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"electron-to-chromium": ["electron-to-chromium@1.5.241", "", {}, "sha512-ILMvKX/ZV5WIJzzdtuHg8xquk2y0BOGlFOxBVwTpbiXqWIH0hamG45ddU4R3PQ0gYu+xgo0vdHXHli9sHIGb4w=="],
"electron-to-chromium": ["electron-to-chromium@1.5.243", "", {}, "sha512-ZCphxFW3Q1TVhcgS9blfut1PX8lusVi2SvXQgmEEnK4TCmE1JhH2JkjJN+DNt0pJJwfBri5AROBnz2b/C+YU9g=="],
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
@ -2703,7 +2703,7 @@
"no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="],
"node-abi": ["node-abi@3.78.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ=="],
"node-abi": ["node-abi@3.79.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-Pr/5KdBQGG8TirdkS0qN3B+f3eo8zTOfZQWAxHoJqopMz2/uvRnG+S4fWu/6AZxKei2CP2p/psdQ5HFC2Ap5BA=="],
"node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="],
@ -3137,7 +3137,7 @@
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"sitemap": ["sitemap@8.0.1", "", { "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/cli.js" } }, "sha512-4Y8ynSMFAy/DadeAeio8Kx4zfC8/0VcKi7TH0I1SazvBcrU2fpJaGoeWsX1FMRaHoe3VGMA53DqVoLErZrtG9Q=="],
"sitemap": ["sitemap@8.0.2", "", { "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/cli.js" } }, "sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ=="],
"slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
@ -3507,7 +3507,7 @@
"workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="],
"wrangler": ["wrangler@4.45.1", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.8", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.21", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-SmmbDl6NUkv6mHT8/Scb09lvxXy0Y2hD98oZHswCysrYbs4JW5LP1eTuroE23Z2jK75D7TEzv2MXmwcDIytxhg=="],
"wrangler": ["wrangler@4.45.2", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.8", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.21", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-+G24gD+Rh7iBus5QiVBhNgSLzyAiyuZSm+3Ih5li8+PEA+gosAXfDSZlNZwsVmXd8VJTjq+BR4vhUiruQRWgTw=="],
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
@ -3705,8 +3705,6 @@
"@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
"@opencode-ai/ui/@pierre/precision-diffs": ["@pierre/precision-diffs@0.3.2", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/transformers": "3.13.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.13.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-HE+wFB0TV+wmjur/J+qI5PsRQl5RN6tCEFTusW0S5FDfZJUIpkxJCacqUxyEI0DriXMKhgGQ+oCQShfaFELdrQ=="],
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/types": "3.4.2" } }, "sha512-I5baLVi/ynLEOZoWSAMlACHNnG+yw5HDmse0oe+GW6U1u+ULdEB3UHiVWaHoJSSONV7tlcVxuaMy74sREDkSvg=="],
"@opencode-ai/web/@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="],
@ -3775,12 +3773,12 @@
"@tanstack/directive-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
"@tanstack/router-utils/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
"@tanstack/router-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
"@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="],
"@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@vercel/nft/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
@ -3863,6 +3861,8 @@
"editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="],
"editorconfig/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"esbuild-plugin-copy/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
@ -3959,14 +3959,14 @@
"nitropack/unenv": ["unenv@2.0.0-rc.23", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-NeOb/HbW2OwOzYaV21MewVQYfzlSwG0kVUB74RyV0gEIP44M5DsYTK9e7jDcekB/3YU+pfNWniZj+r4M/aejyQ=="],
"node-abi/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"nypm/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
"opencode/@pierre/precision-diffs": ["@pierre/precision-diffs@0.3.2", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/transformers": "3.13.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.13.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-HE+wFB0TV+wmjur/J+qI5PsRQl5RN6tCEFTusW0S5FDfZJUIpkxJCacqUxyEI0DriXMKhgGQ+oCQShfaFELdrQ=="],
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
"opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
@ -4325,10 +4325,6 @@
"@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
"@opencode-ai/ui/@pierre/precision-diffs/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="],
"@opencode-ai/ui/@pierre/precision-diffs/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
"@opencode-ai/web/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ=="],
"@opencode-ai/web/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.4.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg=="],
@ -4515,10 +4511,6 @@
"nypm/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
"opencode/@pierre/precision-diffs/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="],
"opencode/@pierre/precision-diffs/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
"opencontrol/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
@ -4705,18 +4697,6 @@
"@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"@opencode-ai/ui/@pierre/precision-diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
"@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
"@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
"@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
"@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
"@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
"@vercel/nft/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
@ -4743,18 +4723,6 @@
"nitropack/serve-static/send/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
"opencode/@pierre/precision-diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
"opencode/@pierre/precision-diffs/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
"opencode/@pierre/precision-diffs/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
"opencode/@pierre/precision-diffs/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
"opencode/@pierre/precision-diffs/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
"opencode/@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
"opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"opencontrol/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],

View file

@ -28,7 +28,7 @@
"@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/precision-diffs": "0.3.2",
"@pierre/precision-diffs": "0.3.6",
"@solidjs/meta": "0.29.4",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.2",
@ -66,7 +66,7 @@
"license": "MIT",
"prettier": {
"semi": false,
"printWidth": 120
"printWidth": 100
},
"trustedDependencies": [
"esbuild",

View file

@ -7,7 +7,7 @@
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start",
"version": "0.15.20"
"version": "0.15.23"
},
"dependencies": {
"@ibm/plex": "6.4.1",

View file

@ -43,7 +43,7 @@ export async function handler(
})
const zenData = ZenData.list()
const modelInfo = validateModel(zenData, body.model)
const providerInfo = selectProvider(zenData, modelInfo)
const providerInfo = selectProvider(zenData, modelInfo, input.request.headers.get("x-real-ip") ?? "")
const authInfo = await authenticate(modelInfo, providerInfo)
validateBilling(modelInfo, authInfo)
validateModelSettings(authInfo)
@ -222,11 +222,15 @@ export async function handler(
return { id: modelId, ...modelData }
}
function selectProvider(zenData: ZenData, model: Awaited<ReturnType<typeof validateModel>>) {
function selectProvider(zenData: ZenData, model: Awaited<ReturnType<typeof validateModel>>, ip: string) {
const providers = model.providers
.filter((provider) => !provider.disabled)
.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
const provider = providers[Math.floor(Math.random() * providers.length)]
// Use last character of IP address to select a provider
const lastChar = ip.charCodeAt(ip.length - 1) || 0
const index = lastChar % providers.length
const provider = providers[index]
if (!(provider.id in zenData.providers)) {
throw new ModelError(`Provider ${provider.id} not supported`)

View file

@ -25,8 +25,8 @@ export async function GET(input: APIEvent) {
object: "list",
data: Object.entries(zenData.models)
.filter(([id]) => !disabledModels.includes(id))
.map(([id, model]) => ({
id: `opencode/${id}`,
.map(([id, _model]) => ({
id,
object: "model",
created: Math.floor(Date.now() / 1000),
owned_by: "opencode",
@ -50,7 +50,10 @@ export async function GET(input: APIEvent) {
})
.from(KeyTable)
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
.leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted)))
.leftJoin(
ModelTable,
and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted)),
)
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
.then((rows) => rows.map((row) => row.model)),
)

View file

@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "0.15.20",
"version": "0.15.23",
"private": true,
"type": "module",
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "0.15.20",
"version": "0.15.23",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",

View file

@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "0.15.20",
"version": "0.15.23",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",

View file

@ -7,7 +7,7 @@
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.svg" />
<title>OpenCode</title>
</head>
<body class="overscroll-none select-none text-12-regular">
<body class="antialiased overscroll-none select-none text-12-regular">
<!-- <script> -->
<!-- ;(function () { -->
<!-- const savedTheme = localStorage.getItem("theme") || "opencode" -->

View file

@ -1,6 +1,6 @@
{
"name": "@opencode-ai/desktop",
"version": "0.15.20",
"version": "0.15.23",
"description": "",
"type": "module",
"scripts": {
@ -26,7 +26,7 @@
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@pierre/precision-diffs": "0.3.5",
"@pierre/precision-diffs": "catalog:",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3",
"@solid-primitives/event-bus": "1.1.2",

View file

@ -1,11 +1,11 @@
import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart } from "@opencode-ai/sdk"
import type { Tool } from "opencode/tool/tool"
import type { ReadTool } from "opencode/tool/read"
import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart, Message } from "@opencode-ai/sdk"
import { children, Component, createMemo, For, Match, Show, Switch, type JSX } from "solid-js"
import { Dynamic } from "solid-js/web"
import { Markdown } from "./markdown"
import { Collapsible, Icon, IconProps } from "@opencode-ai/ui"
import { Checkbox, Collapsible, Diff, Icon, IconProps } from "@opencode-ai/ui"
import { getDirectory, getFilename } from "@/utils"
import type { Tool } from "opencode/tool/tool"
import type { ReadTool } from "opencode/tool/read"
import type { ListTool } from "opencode/tool/ls"
import type { GlobTool } from "opencode/tool/glob"
import type { GrepTool } from "opencode/tool/grep"
@ -14,41 +14,39 @@ import type { TaskTool } from "opencode/tool/task"
import type { BashTool } from "opencode/tool/bash"
import type { EditTool } from "opencode/tool/edit"
import type { WriteTool } from "opencode/tool/write"
import type { TodoWriteTool } from "opencode/tool/todo"
import { DiffChanges } from "./diff-changes"
export function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
const filteredParts = createMemo(() => {
return props.parts?.filter((x) => {
if (x.type === "reasoning") return false
return x.type !== "tool" || x.tool !== "todoread"
})
})
return (
<div class="w-full flex flex-col items-start gap-4">
<For each={props.parts}>
{(part) => {
const component = createMemo(() => PART_MAPPING[part.type as keyof typeof PART_MAPPING])
return (
<Show when={component()}>
<Dynamic component={component()} part={part as any} message={props.message} />
</Show>
)
}}
</For>
<For each={filteredParts()}>{(part) => <Part part={part} message={props.message} />}</For>
</div>
)
}
export function Part(props: { part: Part; message: Message; readonly?: boolean }) {
const component = createMemo(() => PART_MAPPING[props.part.type as keyof typeof PART_MAPPING])
return (
<Show when={component()}>
<Dynamic component={component()} part={props.part as any} message={props.message} readonly={props.readonly} />
</Show>
)
}
const PART_MAPPING = {
text: TextPart,
tool: ToolPart,
reasoning: ReasoningPart,
}
function ReasoningPart(props: { part: ReasoningPart; message: AssistantMessage }) {
return null
// return (
// <Show when={props.part.text.trim()}>
// <div>{props.part.text}</div>
// </Show>
// )
}
function TextPart(props: { part: TextPart; message: AssistantMessage }) {
function ReasoningPart(props: { part: ReasoningPart; message: Message }) {
return (
<Show when={props.part.text.trim()}>
<Markdown text={props.part.text.trim()} />
@ -56,30 +54,29 @@ function TextPart(props: { part: TextPart; message: AssistantMessage }) {
)
}
function ToolPart(props: { part: ToolPart; message: AssistantMessage }) {
// const sync = useSync()
function TextPart(props: { part: TextPart; message: Message }) {
return (
<Show when={props.part.text.trim()}>
<Markdown text={props.part.text.trim()} />
</Show>
)
}
function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean }) {
const component = createMemo(() => {
const render = ToolRegistry.render(props.part.tool) ?? GenericTool
const metadata = props.part.state.status === "pending" ? {} : (props.part.state.metadata ?? {})
const input = props.part.state.status === "completed" ? props.part.state.input : {}
// const permissions = sync.data.permission[props.message.sessionID] ?? []
// const permissionIndex = permissions.findIndex((x) => x.callID === props.part.callID)
// const permission = permissions[permissionIndex]
return (
<>
<Dynamic
component={render}
input={input}
tool={props.part.tool}
metadata={metadata}
// permission={permission?.metadata ?? {}}
output={props.part.state.status === "completed" ? props.part.state.output : undefined}
/>
{/* <Show when={props.part.state.status === "error"}>{props.part.state.error.replace("Error: ", "")}</Show> */}
</>
<Dynamic
component={render}
input={input}
tool={props.part.tool}
metadata={metadata}
output={props.part.state.status === "completed" ? props.part.state.output : undefined}
readonly={props.readonly}
/>
)
})
@ -88,8 +85,11 @@ function ToolPart(props: { part: ToolPart; message: AssistantMessage }) {
type TriggerTitle = {
title: string
titleClass?: string
subtitle?: string
subtitleClass?: string
args?: string[]
argsClass?: string
action?: JSX.Element
}
@ -97,59 +97,91 @@ const isTriggerTitle = (val: any): val is TriggerTitle => {
return typeof val === "object" && val !== null && "title" in val && !(val instanceof Node)
}
function BasicTool(props: { icon: IconProps["name"]; trigger: TriggerTitle | JSX.Element; children?: JSX.Element }) {
function BasicTool(props: {
icon: IconProps["name"]
trigger: TriggerTitle | JSX.Element
children?: JSX.Element
readonly?: boolean
}) {
const resolved = children(() => props.children)
return (
<Collapsible>
<Collapsible.Trigger>
<div class="w-full flex items-center self-stretch gap-5 justify-between">
<div class="w-full flex items-center self-stretch gap-5">
<Icon name={props.icon} size="small" />
<Switch>
<Match when={isTriggerTitle(props.trigger)}>
<div class="w-full flex items-center gap-2 justify-between">
<div class="flex items-center gap-2">
<span class="text-12-medium text-text-base capitalize">
{(props.trigger as TriggerTitle).title}
</span>
<Show when={(props.trigger as TriggerTitle).subtitle}>
<span class="text-12-medium text-text-weak">{(props.trigger as TriggerTitle).subtitle}</span>
</Show>
<Show when={(props.trigger as TriggerTitle).args?.length}>
<For each={(props.trigger as TriggerTitle).args}>
{(arg) => <span class="text-12-regular text-text-weaker">{arg}</span>}
</For>
</Show>
</div>
<Show when={(props.trigger as TriggerTitle).action}>{(props.trigger as TriggerTitle).action}</Show>
</div>
</Match>
<Match when={true}>{props.trigger as JSX.Element}</Match>
</Switch>
<Icon name={props.icon} size="small" class="shrink-0" />
<div class="grow min-w-0">
<Switch>
<Match when={isTriggerTitle(props.trigger) && props.trigger}>
{(trigger) => (
<div class="w-full flex items-center gap-2 justify-between">
<div class="flex items-center gap-2 whitespace-nowrap truncate">
<span
classList={{
"text-12-medium text-text-base": true,
[trigger().titleClass ?? ""]: !!trigger().titleClass,
}}
>
{trigger().title}
</span>
<Show when={trigger().subtitle}>
<span
classList={{
"text-12-medium text-text-weak": true,
[trigger().subtitleClass ?? ""]: !!trigger().subtitleClass,
}}
>
{trigger().subtitle}
</span>
</Show>
<Show when={trigger().args?.length}>
<For each={trigger().args}>
{(arg) => (
<span
classList={{
"text-12-regular text-text-weak": true,
[trigger().argsClass ?? ""]: !!trigger().argsClass,
}}
>
{arg}
</span>
)}
</For>
</Show>
</div>
<Show when={trigger().action}>{trigger().action}</Show>
</div>
)}
</Match>
<Match when={true}>{props.trigger as JSX.Element}</Match>
</Switch>
</div>
</div>
<Show when={resolved()}>
<Show when={resolved() && !props.readonly}>
<Collapsible.Arrow />
</Show>
</div>
</Collapsible.Trigger>
<Show when={props.children}>
<Collapsible.Content>{props.children}</Collapsible.Content>
<Show when={resolved() && !props.readonly}>
<Collapsible.Content>{resolved()}</Collapsible.Content>
</Show>
</Collapsible>
// <>
// <Show when={props.part.state.status === "error"}>{props.part.state.error.replace("Error: ", "")}</Show>
// </>
)
}
function GenericTool(props: ToolProps<any>) {
return <BasicTool icon="mcp" trigger={{ title: props.tool }} />
return <BasicTool icon="mcp" trigger={{ title: props.tool }} readonly={props.readonly} />
}
type ToolProps<T extends Tool.Info> = {
input: Partial<Tool.InferParameters<T>>
metadata: Partial<Tool.InferMetadata<T>>
// permission: Record<string, any>
tool: string
output?: string
readonly?: boolean
}
const ToolRegistry = (() => {
@ -178,7 +210,7 @@ ToolRegistry.register<typeof ReadTool>({
return (
<BasicTool
icon="glasses"
trigger={{ title: props.tool, subtitle: props.input.filePath ? getFilename(props.input.filePath) : "" }}
trigger={{ title: "Read", subtitle: props.input.filePath ? getFilename(props.input.filePath) : "" }}
/>
)
},
@ -188,7 +220,7 @@ ToolRegistry.register<typeof ListTool>({
name: "list",
render(props) {
return (
<BasicTool icon="bullet-list" trigger={{ title: props.tool, subtitle: props.input.path || "/" }}>
<BasicTool icon="bullet-list" trigger={{ title: "List", subtitle: getDirectory(props.input.path || "/") }}>
<Show when={false && props.output}>
<div class="whitespace-pre">{props.output}</div>
</Show>
@ -204,8 +236,8 @@ ToolRegistry.register<typeof GlobTool>({
<BasicTool
icon="magnifying-glass-menu"
trigger={{
title: props.tool,
subtitle: props.input.path || "/",
title: "Glob",
subtitle: getDirectory(props.input.path || "/"),
args: props.input.pattern ? ["pattern=" + props.input.pattern] : [],
}}
>
@ -227,8 +259,8 @@ ToolRegistry.register<typeof GrepTool>({
<BasicTool
icon="magnifying-glass-menu"
trigger={{
title: props.tool,
subtitle: props.input.path || "/",
title: "Grep",
subtitle: getDirectory(props.input.path || "/"),
args,
}}
>
@ -247,7 +279,7 @@ ToolRegistry.register<typeof WebFetchTool>({
<BasicTool
icon="window-cursor"
trigger={{
title: props.tool,
title: "Webfetch",
subtitle: props.input.url || "",
args: props.input.format ? ["format=" + props.input.format] : [],
action: (
@ -273,6 +305,7 @@ ToolRegistry.register<typeof TaskTool>({
icon="task"
trigger={{
title: `${props.input.subagent_type || props.tool} Agent`,
titleClass: "capitalize",
subtitle: props.input.description,
}}
>
@ -311,11 +344,49 @@ ToolRegistry.register<typeof EditTool>({
icon="code-lines"
trigger={
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-5">
<div class="flex items-center gap-2">
<div class="text-12-medium text-text-base capitalize">Edit</div>
<div class="flex">
<Show when={props.input.filePath?.includes("/")}>
<span class="text-text-weak">{getDirectory(props.input.filePath!)}/</span>
<span class="text-text-weak">{getDirectory(props.input.filePath!)}</span>
</Show>
<span class="text-text-strong">{getFilename(props.input.filePath ?? "")}</span>
</div>
</div>
<div class="flex gap-4 items-center justify-end">
<Show when={props.metadata.filediff}>
<DiffChanges diff={props.metadata.filediff} />
</Show>
</div>
</div>
}
>
<Show when={props.metadata.filediff}>
<div class="border-t border-border-weaker-base">
<Diff
before={{ name: getFilename(props.metadata.filediff.path), contents: props.metadata.filediff.before }}
after={{ name: getFilename(props.metadata.filediff.path), contents: props.metadata.filediff.after }}
/>
</div>
</Show>
</BasicTool>
)
},
})
ToolRegistry.register<typeof WriteTool>({
name: "write",
render(props) {
return (
<BasicTool
icon="code-lines"
trigger={
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-2">
<div class="text-12-medium text-text-base capitalize">Write</div>
<div class="flex">
<Show when={props.input.filePath?.includes("/")}>
<span class="text-text-weak">{getDirectory(props.input.filePath!)}</span>
</Show>
<span class="text-text-strong">{getFilename(props.input.filePath ?? "")}</span>
</div>
@ -332,29 +403,27 @@ ToolRegistry.register<typeof EditTool>({
},
})
ToolRegistry.register<typeof WriteTool>({
name: "write",
ToolRegistry.register<typeof TodoWriteTool>({
name: "todowrite",
render(props) {
return (
<BasicTool
icon="code-lines"
trigger={
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-5">
<div class="text-12-medium text-text-base capitalize">Write</div>
<div class="flex">
<Show when={props.input.filePath?.includes("/")}>
<span class="text-text-weak">{getDirectory(props.input.filePath!)}/</span>
</Show>
<span class="text-text-strong">{getFilename(props.input.filePath ?? "")}</span>
</div>
</div>
<div class="flex gap-4 items-center justify-end">{/* <DiffChanges diff={diff} /> */}</div>
</div>
}
icon="checklist"
trigger={{
title: "To-dos",
subtitle: `${props.input.todos?.filter((t) => t.status === "completed").length}/${props.input.todos?.length}`,
}}
>
<Show when={false && props.output}>
<div class="whitespace-pre">{props.output}</div>
<Show when={props.input.todos?.length}>
<div class="px-12 pt-2.5 pb-6 flex flex-col gap-2">
<For each={props.input.todos}>
{(todo) => (
<Checkbox readOnly checked={todo.status === "completed"}>
<div classList={{ "line-through text-text-weaker": todo.status === "completed" }}>{todo.content}</div>
</Checkbox>
)}
</For>
</div>
</Show>
</BasicTool>
)

View file

@ -10,13 +10,15 @@ import { DateTime } from "luxon"
interface PartBase {
content: string
start: number
end: number
}
interface TextPart extends PartBase {
export interface TextPart extends PartBase {
type: "text"
}
interface FileAttachmentPart extends PartBase {
export interface FileAttachmentPart extends PartBase {
type: "file"
path: string
selection?: TextSelection
@ -34,7 +36,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const local = useLocal()
let editorRef!: HTMLDivElement
const defaultParts = [{ type: "text", content: "" } as const]
const defaultParts = [{ type: "text", content: "", start: 0, end: 0 } as const]
const [store, setStore] = createStore<{
contentParts: ContentPart[]
popoverIsOpen: boolean
@ -51,7 +53,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
event.stopPropagation()
// @ts-expect-error
const plainText = (event.clipboardData || window.clipboardData)?.getData("text/plain") ?? ""
addPart({ type: "text", content: plainText })
addPart({ type: "text", content: plainText, start: 0, end: 0 })
}
onMount(() => {
@ -74,7 +76,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
key: (x) => x,
onSelect: (path) => {
if (!path) return
addPart({ type: "file", path, content: "@" + getFilename(path) })
addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 })
setStore("popoverIsOpen", false)
},
})
@ -117,17 +119,26 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const parseFromDOM = (): ContentPart[] => {
const newParts: ContentPart[] = []
let position = 0
editorRef.childNodes.forEach((node) => {
if (node.nodeType === Node.TEXT_NODE) {
if (node.textContent) newParts.push({ type: "text", content: node.textContent })
if (node.textContent) {
const content = node.textContent
newParts.push({ type: "text", content, start: position, end: position + content.length })
position += content.length
}
} else if (node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).dataset.type) {
switch ((node as HTMLElement).dataset.type) {
case "file":
const content = node.textContent!
newParts.push({
type: "file",
path: (node as HTMLElement).dataset.path!,
content: node.textContent!,
content,
start: position,
end: position + content.length,
})
position += content.length
break
default:
break
@ -163,17 +174,19 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const startIndex = atMatch ? atMatch.index! : cursorPosition
const endIndex = atMatch ? cursorPosition : cursorPosition
const pushText = (acc: { parts: ContentPart[] }, value: string) => {
const pushText = (acc: { parts: ContentPart[]; runningIndex: number }, value: string) => {
if (!value) return
const last = acc.parts[acc.parts.length - 1]
if (last && last.type === "text") {
acc.parts[acc.parts.length - 1] = {
type: "text",
content: last.content + value,
start: last.start,
end: last.end + value.length,
}
return
}
acc.parts.push({ type: "text", content: value })
acc.parts.push({ type: "text", content: value, start: acc.runningIndex, end: acc.runningIndex + value.length })
}
const {
@ -183,20 +196,20 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
} = store.contentParts.reduce(
(acc, item) => {
if (acc.inserted) {
acc.parts.push(item)
acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length })
acc.runningIndex += item.content.length
return acc
}
const nextIndex = acc.runningIndex + item.content.length
if (nextIndex <= startIndex) {
acc.parts.push(item)
acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length })
acc.runningIndex = nextIndex
return acc
}
if (item.type !== "text") {
acc.parts.push(item)
acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length })
acc.runningIndex = nextIndex
return acc
}
@ -207,24 +220,27 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const tail = item.content.slice(tailLength)
pushText(acc, head)
acc.runningIndex += head.length
if (part.type === "text") {
pushText(acc, part.content)
acc.runningIndex += part.content.length
}
if (part.type !== "text") {
acc.parts.push({ ...part })
acc.parts.push({ ...part, start: acc.runningIndex, end: acc.runningIndex + part.content.length })
acc.runningIndex += part.content.length
}
const needsGap = Boolean(atMatch)
const rest = needsGap ? (tail ? (/^\s/.test(tail) ? tail : ` ${tail}`) : " ") : tail
pushText(acc, rest)
acc.runningIndex += rest.length
const baseCursor = startIndex + part.content.length
const cursorAddition = needsGap && rest.length > 0 ? 1 : 0
acc.cursorPositionAfter = baseCursor + cursorAddition
acc.inserted = true
acc.runningIndex = nextIndex
return acc
},
{
@ -237,9 +253,18 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (!inserted) {
const baseParts = store.contentParts.filter((item) => !(item.type === "text" && item.content === ""))
const appendedAcc = { parts: [...baseParts] as ContentPart[] }
if (part.type === "text") pushText(appendedAcc, part.content)
if (part.type !== "text") appendedAcc.parts.push({ ...part })
const runningIndex = baseParts.reduce((sum, p) => sum + p.content.length, 0)
const appendedAcc = { parts: [...baseParts] as ContentPart[], runningIndex }
if (part.type === "text") {
pushText(appendedAcc, part.content)
}
if (part.type !== "text") {
appendedAcc.parts.push({
...part,
start: appendedAcc.runningIndex,
end: appendedAcc.runningIndex + part.content.length,
})
}
const next = appendedAcc.parts.length > 0 ? appendedAcc.parts : defaultParts
setStore("contentParts", next)
setStore("popoverIsOpen", false)
@ -289,7 +314,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
<div class="flex items-center text-14-regular">
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
{getDirectory(i)}/
{getDirectory(i)}
</span>
<span class="text-text-strong whitespace-nowrap">{getFilename(i)}</span>
</div>

View file

@ -429,13 +429,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
.sort((a, b) => b.id.localeCompare(a.id)),
)
const working = createMemo(() => {
const last = messages()[messages().length - 1]
if (!last) return false
if (last.role === "user") return true
return !last.time.completed
})
const cost = createMemo(() => {
const total = pipe(
messages(),
@ -487,6 +480,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const getMessageText = (message: Message | Message[] | undefined): string => {
if (!message) return ""
if (Array.isArray(message)) return message.map((m) => getMessageText(m)).join(" ")
const fileParts = sync.data.part[message.id]?.filter((p) => p.type === "file")
console.log(fileParts)
return sync.data.part[message.id]
?.filter((p) => p.type === "text")
?.filter((p) => !p.synthetic)
@ -506,7 +502,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
messages,
messagesWithValidParts,
userMessages,
working,
// working,
getMessageText,
setActive(sessionId: string | undefined) {
setStore("active", sessionId)

View file

@ -1,8 +1,19 @@
import { Button, List, SelectDialog, Tooltip, IconButton, Tabs, Icon, Accordion } from "@opencode-ai/ui"
import {
Button,
List,
SelectDialog,
Tooltip,
IconButton,
Tabs,
Icon,
Accordion,
Diff,
Collapsible,
} from "@opencode-ai/ui"
import { FileIcon } from "@/ui"
import FileTree from "@/components/file-tree"
import { For, onCleanup, onMount, Show, Match, Switch, createSignal, createEffect, createMemo } from "solid-js"
import { useLocal, type LocalFile, type TextSelection } from "@/context/local"
import { useLocal, type LocalFile } from "@/context/local"
import { createStore } from "solid-js/store"
import { getDirectory, getFilename } from "@/utils"
import { ContentPart, PromptInput } from "@/components/prompt-input"
@ -21,9 +32,8 @@ import type { JSX } from "solid-js"
import { Code } from "@/components/code"
import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk"
import { Diff } from "@/components/diff"
import { ProgressCircle } from "@/components/progress-circle"
import { AssistantMessage } from "@/components/assistant-message"
import { AssistantMessage, Part } from "@/components/assistant-message"
import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
import { DiffChanges } from "@/components/diff-changes"
@ -168,6 +178,8 @@ export default function Page() {
}
const handleDiffTriggerClick = (event: MouseEvent) => {
// disabling scroll to diff for now
return
const target = event.currentTarget as HTMLElement
queueMicrotask(() => {
if (target.getAttribute("aria-expanded") !== "true") return
@ -186,42 +198,10 @@ export default function Page() {
}
if (!session) return
interface SubmissionAttachment {
path: string
selection?: TextSelection
label: string
}
const createAttachmentKey = (path: string, selection?: TextSelection) => {
if (!selection) return path
return `${path}:${selection.startLine}:${selection.startChar}:${selection.endLine}:${selection.endChar}`
}
const formatAttachmentLabel = (path: string, selection?: TextSelection) => {
if (!selection) return getFilename(path)
return `${getFilename(path)} (${selection.startLine}-${selection.endLine})`
}
const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path))
const text = parts.map((part) => part.content).join("")
const attachments = new Map<string, SubmissionAttachment>()
const registerAttachment = (path: string, selection: TextSelection | undefined, label?: string) => {
if (!path) return
const key = createAttachmentKey(path, selection)
if (attachments.has(key)) return
attachments.set(key, {
path,
selection,
label: label ?? formatAttachmentLabel(path, selection),
})
}
const promptAttachments = parts.filter((part) => part.type === "file")
for (const part of promptAttachments) {
registerAttachment(part.path, part.selection, part.content)
}
const attachments = parts.filter((part) => part.type === "file")
// const activeFile = local.context.active()
// if (activeFile) {
@ -240,7 +220,7 @@ export default function Page() {
// )
// }
const attachmentParts = Array.from(attachments.values()).map((attachment) => {
const attachmentParts = attachments.map((attachment) => {
const absolute = toAbsolutePath(attachment.path)
const query = attachment.selection
? `?start=${attachment.selection.startLine}&end=${attachment.selection.endLine}`
@ -253,9 +233,9 @@ export default function Page() {
source: {
type: "file" as const,
text: {
value: `@${attachment.label}`,
start: 0,
end: 0,
value: attachment.content,
start: attachment.start,
end: attachment.end,
},
path: absolute,
},
@ -511,8 +491,8 @@ export default function Page() {
</Show>
</div>
</div>
<Tabs.Content value="chat" class="select-text flex flex-col flex-1 min-h-0">
<div class="p-6 pt-12 max-w-[904px] w-full mx-auto flex flex-col flex-1 min-h-0">
<Tabs.Content value="chat" class="select-text flex flex-col flex-1 min-h-0 overflow-y-hidden">
<div class="px-6 pt-12 max-w-[904px] w-full mx-auto flex flex-col flex-1 min-h-0">
<Show
when={local.session.active()}
fallback={
@ -521,7 +501,7 @@ export default function Page() {
<div class="flex justify-center items-center gap-3">
<Icon name="folder" size="small" />
<div class="text-12-medium text-text-weak">
{getDirectory(sync.data.path.directory)}/
{getDirectory(sync.data.path.directory)}
<span class="text-text-strong">{getFilename(sync.data.path.directory)}</span>
</div>
</div>
@ -538,7 +518,7 @@ export default function Page() {
}
>
{(activeSession) => (
<div class="py-3 flex flex-col flex-1 min-h-0">
<div class="pt-3 flex flex-col flex-1 min-h-0">
<div class="flex items-start gap-8 flex-1 min-h-0">
<Show when={local.session.userMessages().length > 1}>
<ul role="list" class="w-60 shrink-0 flex flex-col items-start gap-1">
@ -655,9 +635,10 @@ export default function Page() {
</ul>
</Show>
<div ref={messageScrollElement} class="grow min-w-0 h-full overflow-y-auto no-scrollbar">
<div class="flex flex-col items-start gap-50 pb-[800px]">
<div class="flex flex-col items-start gap-50 pb-50">
<For each={local.session.userMessages()}>
{(message) => {
const [expanded, setExpanded] = createSignal(false)
const title = createMemo(() => message.summary?.title)
const prompt = createMemo(() => local.session.getMessageText(message))
const summary = createMemo(() => message.summary?.body)
@ -666,54 +647,143 @@ export default function Page() {
(m) => m.role === "assistant" && m.parentID == message.id,
) as AssistantMessageType[]
})
const working = createMemo(() => {
const last = assistantMessages()[assistantMessages().length - 1]
if (!last) return false
return !last.time.completed
})
return (
<div
data-message={message.id}
class="flex flex-col items-start self-stretch gap-14 pt-1.5"
class="flex flex-col items-start self-stretch gap-8 min-h-[calc(100vh-15rem)]"
>
{/* Title */}
<div class="flex flex-col items-start gap-2 self-stretch">
<div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger">
<h1 class="text-14-medium text-text-strong overflow-hidden text-ellipsis min-w-0">
{title() ?? prompt()}
</h1>
<Show when={title}>
<div class="text-12-regular text-text-base">{prompt()}</div>
</div>
<Show when={title}>
<div class="-mt-8 text-12-regular text-text-base line-clamp-3">{prompt()}</div>
</Show>
{/* Response */}
<div class="w-full flex flex-col gap-2">
<Collapsible variant="ghost" open={expanded()} onOpenChange={setExpanded}>
<Collapsible.Trigger class="text-text-weak hover:text-text-strong">
<div class="flex items-center gap-1 self-stretch">
<h2 class="text-12-medium">
<Switch>
<Match when={expanded()}>Hide steps</Match>
<Match when={!expanded()}>Show steps</Match>
</Switch>
</h2>
<Collapsible.Arrow />
</div>
</Collapsible.Trigger>
<Collapsible.Content>
<div class="w-full flex flex-col items-start self-stretch gap-8">
<For each={assistantMessages()}>
{(assistantMessage) => {
const parts = createMemo(() => sync.data.part[assistantMessage.id])
return <AssistantMessage message={assistantMessage} parts={parts()} />
}}
</For>
</div>
</Collapsible.Content>
</Collapsible>
<Show when={working() && !expanded()}>
{(_) => {
const lastMessageWithText = createMemo(() =>
assistantMessages().findLast((m) => {
const parts = sync.data.part[m.id]
return parts?.find((p) => p.type === "text")
}),
)
const lastMessageWithReasoning = createMemo(() =>
assistantMessages().findLast((m) => {
const parts = sync.data.part[m.id]
return parts?.find((p) => p.type === "reasoning")
}),
)
const lastMessageWithTool = createMemo(() =>
assistantMessages().findLast((m) => {
const parts = sync.data.part[m.id]
return parts?.find(
(p) => p.type === "tool" && p.state.status === "completed",
)
}),
)
return (
<div class="w-full flex flex-col gap-2">
<Switch>
<Match when={lastMessageWithText()}>
{(last) => {
const lastTextPart = createMemo(() =>
sync.data.part[last().id].findLast((p) => p.type === "text"),
)
return <Part message={last()} part={lastTextPart()!} readonly />
}}
</Match>
<Match when={lastMessageWithReasoning()}>
{(last) => {
const lastReasoningPart = createMemo(() =>
sync.data.part[last().id].findLast(
(p) => p.type === "reasoning",
),
)
return (
<Part message={last()} part={lastReasoningPart()!} readonly />
)
}}
</Match>
</Switch>
<Show when={lastMessageWithTool()}>
{(last) => {
const lastToolPart = createMemo(() =>
sync.data.part[last().id].findLast(
(p) => p.type === "tool" && p.state.status === "completed",
),
)
return <Part message={last()} part={lastToolPart()!} readonly />
}}
</Show>
</div>
)
}}
</Show>
</div>
{/* Summary */}
<div class="w-full flex flex-col gap-6 items-start self-stretch">
<Show when={summary}>
<Show when={!working()}>
<div class="w-full flex flex-col gap-6 items-start self-stretch">
<div class="flex flex-col items-start gap-1 self-stretch">
<h2 class="text-12-medium text-text-weak">Summary</h2>
<div class="text-14-regular text-text-base self-stretch">{summary()}</div>
</div>
</Show>
<Show when={message.summary?.diffs.length}>
<Accordion class="w-full" multiple>
<For each={message.summary?.diffs || []}>
{(diff) => (
<Accordion.Item value={diff.file}>
<Accordion.Header>
<Accordion.Trigger onClick={handleDiffTriggerClick}>
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-5">
<div class="flex items-center justify-between w-full gap-5">
<div class="grow flex items-center gap-5 min-w-0">
<FileIcon
node={{ path: diff.file, type: "file" }}
class="shrink-0 size-4"
/>
<div class="flex">
<div class="flex grow min-w-0">
<Show when={diff.file.includes("/")}>
<span class="text-text-base">
{getDirectory(diff.file)}/
<span class="text-text-base truncate-start">
{getDirectory(diff.file)}&lrm;
</span>
</Show>
<span class="text-text-strong">
<span class="text-text-strong shrink-0">
{getFilename(diff.file)}
</span>
</div>
</div>
<div class="flex gap-4 items-center justify-end">
<div class="shrink-0 flex gap-4 items-center justify-end">
<DiffChanges diff={diff} />
<Icon name="chevron-grabber-vertical" size="small" />
</div>
@ -736,22 +806,8 @@ export default function Page() {
)}
</For>
</Accordion>
</Show>
</div>
{/* Response */}
<div data-todo="Response" class="w-full">
<div class="flex flex-col items-start gap-1 self-stretch">
<h2 class="text-12-medium text-text-weak">Response</h2>
</div>
<div class="w-full flex flex-col items-start self-stretch gap-8">
<For each={assistantMessages()}>
{(assistantMessage) => {
const parts = createMemo(() => sync.data.part[assistantMessage.id])
return <AssistantMessage message={assistantMessage} parts={parts()} />
}}
</For>
</div>
</div>
</Show>
</div>
)
}}
@ -858,7 +914,7 @@ export default function Page() {
<FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
<div class="flex items-center text-14-regular">
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
{getDirectory(i)}/
{getDirectory(i)}
</span>
<span class="text-text-strong whitespace-nowrap">{getFilename(i)}</span>
</div>

View file

@ -1,3 +1,5 @@
import { useSync } from "@/context/sync"
export function getFilename(path: string) {
if (!path) return ""
const trimmed = path.replace(/[\/]+$/, "")
@ -6,8 +8,10 @@ export function getFilename(path: string) {
}
export function getDirectory(path: string) {
const sync = useSync()
const parts = path.split("/")
return parts.slice(0, parts.length - 1).join("/")
const dir = parts.slice(0, parts.length - 1).join("/")
return dir ? sync.sanitize(dir + "/") : ""
}
export function getFileExtension(path: string) {

View file

@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "0.15.20",
"version": "0.15.23",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",

View file

@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "0.15.20",
"version": "0.15.23",
"name": "opencode",
"type": "module",
"private": true,

View file

@ -40,16 +40,33 @@ for (const [name] of Object.entries(binaries)) {
}
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${Script.channel}`
if (!Script.preview) {
const major = Script.version.split(".")[0]
const majorTag = `latest-${major}`
for (const [name] of Object.entries(binaries)) {
await $`cd dist/${name} && npm dist-tag add ${name}@${Script.version} ${majorTag}`
}
await $`cd ./dist/${pkg.name} && npm dist-tag add ${pkg.name}-ai@${Script.version} ${majorTag}`
}
if (!Script.preview) {
for (const key of Object.keys(binaries)) {
await $`cd dist/${key}/bin && zip -r ../../${key}.zip *`
}
// Calculate SHA values
const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.zip | cut -d' ' -f1`
.text()
.then((x) => x.trim())
const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.zip | cut -d' ' -f1`
.text()
.then((x) => x.trim())
const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`
.text()
.then((x) => x.trim())
const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`
.text()
.then((x) => x.trim())
const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2)

View file

@ -1,3 +1,4 @@
import { EOL } from "os"
import { Config } from "../../../config/config"
import { bootstrap } from "../../bootstrap"
import { cmd } from "../cmd"
@ -8,7 +9,7 @@ export const ConfigCommand = cmd({
async handler() {
await bootstrap(process.cwd(), async () => {
const config = await Config.get()
console.log(JSON.stringify(config, null, 2))
process.stdout.write(JSON.stringify(config, null, 2) + EOL)
})
},
})

View file

@ -14,7 +14,7 @@ const FileSearchCommand = cmd({
async handler(args) {
await bootstrap(process.cwd(), async () => {
const results = await File.search({ query: args.query })
console.log(results.join(EOL))
process.stdout.write(results.join(EOL) + EOL)
})
},
})
@ -30,7 +30,7 @@ const FileReadCommand = cmd({
async handler(args) {
await bootstrap(process.cwd(), async () => {
const content = await File.read(args.path)
console.log(content)
process.stdout.write(JSON.stringify(content, null, 2) + EOL)
})
},
})
@ -41,7 +41,7 @@ const FileStatusCommand = cmd({
async handler() {
await bootstrap(process.cwd(), async () => {
const status = await File.status()
console.log(JSON.stringify(status, null, 2))
process.stdout.write(JSON.stringify(status, null, 2) + EOL)
})
},
})
@ -57,7 +57,7 @@ const FileListCommand = cmd({
async handler(args) {
await bootstrap(process.cwd(), async () => {
const files = await File.list(args.path)
console.log(JSON.stringify(files, null, 2))
process.stdout.write(JSON.stringify(files, null, 2) + EOL)
})
},
})

View file

@ -2,6 +2,7 @@ import { LSP } from "../../../lsp"
import { bootstrap } from "../../bootstrap"
import { cmd } from "../cmd"
import { Log } from "../../../util/log"
import { EOL } from "os"
export const LSPCommand = cmd({
command: "lsp",
@ -16,7 +17,7 @@ const DiagnosticsCommand = cmd({
async handler(args) {
await bootstrap(process.cwd(), async () => {
await LSP.touchFile(args.file, true)
console.log(JSON.stringify(await LSP.diagnostics(), null, 2))
process.stdout.write(JSON.stringify(await LSP.diagnostics(), null, 2) + EOL)
})
},
})
@ -28,7 +29,7 @@ export const SymbolsCommand = cmd({
await bootstrap(process.cwd(), async () => {
using _ = Log.Default.time("symbols")
const results = await LSP.workspaceSymbol(args.query)
console.log(JSON.stringify(results, null, 2))
process.stdout.write(JSON.stringify(results, null, 2) + EOL)
})
},
})
@ -40,7 +41,7 @@ export const DocumentSymbolsCommand = cmd({
await bootstrap(process.cwd(), async () => {
using _ = Log.Default.time("document-symbols")
const results = await LSP.documentSymbol(args.uri)
console.log(JSON.stringify(results, null, 2))
process.stdout.write(JSON.stringify(results, null, 2) + EOL)
})
},
})

View file

@ -18,7 +18,7 @@ const TreeCommand = cmd({
}),
async handler(args) {
await bootstrap(process.cwd(), async () => {
console.log(await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit }))
process.stdout.write(await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit }) + EOL)
})
},
})
@ -49,7 +49,7 @@ const FilesCommand = cmd({
files.push(file)
if (args.limit && files.length >= args.limit) break
}
console.log(files.join(EOL))
process.stdout.write(files.join(EOL) + EOL)
})
},
})
@ -78,6 +78,6 @@ const SearchCommand = cmd({
glob: args.glob as string[] | undefined,
limit: args.limit,
})
console.log(JSON.stringify(results, null, 2))
process.stdout.write(JSON.stringify(results, null, 2) + EOL)
},
})

View file

@ -1,3 +1,4 @@
import { EOL } from "os"
import { Project } from "../../../project/project"
import { Log } from "../../../util/log"
import { cmd } from "../cmd"
@ -8,7 +9,7 @@ export const ScrapCommand = cmd({
async handler() {
const timer = Log.Default.time("scrap")
const list = await Project.list()
console.log(list)
process.stdout.write(JSON.stringify(list, null, 2) + EOL)
timer.stop()
},
})

View file

@ -1,12 +1,16 @@
import { Hono, type Context } from "hono"
import { describeRoute, resolver, validator } from "hono-openapi"
import { z } from "zod"
import { AsyncQueue } from "../util/queue"
interface Request {
path: string
body: any
}
const TuiRequest = z.object({
path: z.string(),
body: z.any(),
})
const request = new AsyncQueue<Request>()
type TuiRequest = z.infer<typeof TuiRequest>
const request = new AsyncQueue<TuiRequest>()
const response = new AsyncQueue<any>()
export async function callTui(ctx: Context) {
@ -19,12 +23,47 @@ export async function callTui(ctx: Context) {
}
export const TuiRoute = new Hono()
.get("/next", async (c) => {
const req = await request.next()
return c.json(req)
})
.post("/response", async (c) => {
const body = await c.req.json()
response.push(body)
return c.json(true)
})
.get(
"/next",
describeRoute({
description: "Get the next TUI request from the queue",
operationId: "tui.control.next",
responses: {
200: {
description: "Next TUI request",
content: {
"application/json": {
schema: resolver(TuiRequest),
},
},
},
},
}),
async (c) => {
const req = await request.next()
return c.json(req)
},
)
.post(
"/response",
describeRoute({
description: "Submit a response to the TUI request queue",
operationId: "tui.control.response",
responses: {
200: {
description: "Response submitted successfully",
content: {
"application/json": {
schema: resolver(z.boolean()),
},
},
},
},
}),
validator("json", z.any()),
async (c) => {
const body = c.req.valid("json")
response.push(body)
return c.json(true)
},
)

View file

@ -21,8 +21,8 @@ const parser = lazy(async () => {
p.setLanguage(Bash.language as any)
return p
} catch (e) {
const { Parser, Language } = await import("web-tree-sitter")
const { default: treeWasm } = await import("web-tree-sitter/web-tree-sitter.wasm" as string, {
const { default: Parser } = await import("web-tree-sitter")
const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, {
with: { type: "wasm" },
})
await Parser.init({
@ -53,6 +53,11 @@ export const BashTool = Tool.define("bash", {
),
}),
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))
@ -99,7 +104,10 @@ export const BashTool = Tool.define("bash", {
// always allow cd if it passes above check
if (command[0] !== "cd") {
const action = Wildcard.allStructured({ head: command[0], tail: command.slice(1) }, permissions)
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)}`,

View file

@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "0.15.20",
"version": "0.15.23",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",

View file

@ -4,10 +4,13 @@ if (process.versions.bun !== "1.3.0") {
throw new Error("This script requires bun@1.3.0")
}
const CHANNEL = process.env["OPENCODE_CHANNEL"] ?? (await $`git branch --show-current`.text().then((x) => x.trim()))
const CHANNEL =
process.env["OPENCODE_CHANNEL"] ??
(await $`git branch --show-current`.text().then((x) => x.trim()))
const IS_PREVIEW = CHANNEL !== "latest"
const VERSION = await (async () => {
if (IS_PREVIEW) return `0.0.0-${CHANNEL}-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
if (IS_PREVIEW)
return `0.0.0-${CHANNEL}-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
const version = await fetch("https://registry.npmjs.org/opencode-ai/latest")
.then((res) => {
if (!res.ok) throw new Error(res.statusText)

View file

@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "0.15.20",
"version": "0.15.23",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",

View file

@ -1,7 +1,12 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { ClientOptions } from "./types.gen.js"
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from "./client/index.js"
import {
type Config,
type ClientOptions as DefaultClientOptions,
createClient,
createConfig,
} from "./client/index.js"
/**
* The `createClientConfig()` function will be called on client initialization

View file

@ -107,7 +107,9 @@ export const createClient = (config: Config = {}): Client => {
}
const parseAs =
(opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json"
(opts.parseAs === "auto"
? getParseAs(response.headers.get("Content-Type"))
: opts.parseAs) ?? "json"
let data: any
switch (parseAs) {

View file

@ -1,7 +1,10 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { Auth } from "../core/auth.gen.js"
import type { ServerSentEventsOptions, ServerSentEventsResult } from "../core/serverSentEvents.gen.js"
import type {
ServerSentEventsOptions,
ServerSentEventsResult,
} from "../core/serverSentEvents.gen.js"
import type { Client as CoreClient, Config as CoreConfig } from "../core/types.gen.js"
import type { Middleware } from "./utils.gen.js"
@ -62,7 +65,11 @@ export interface RequestOptions<
}>,
Pick<
ServerSentEventsOptions<TData>,
"onSseError" | "onSseEvent" | "sseDefaultRetryDelay" | "sseMaxRetryAttempts" | "sseMaxRetryDelay"
| "onSseError"
| "onSseEvent"
| "sseDefaultRetryDelay"
| "sseMaxRetryAttempts"
| "sseMaxRetryDelay"
> {
/**
* Any body that you want to add to your request.
@ -202,7 +209,10 @@ export type Options<
ThrowOnError extends boolean = boolean,
TResponse = unknown,
TResponseStyle extends ResponseStyle = "fields",
> = OmitKeys<RequestOptions<TResponse, TResponseStyle, ThrowOnError>, "body" | "path" | "query" | "url"> &
> = OmitKeys<
RequestOptions<TResponse, TResponseStyle, ThrowOnError>,
"body" | "path" | "query" | "url"
> &
Omit<TData, "url">
export type OptionsLegacyParser<
@ -211,7 +221,8 @@ export type OptionsLegacyParser<
TResponseStyle extends ResponseStyle = "fields",
> = TData extends { body?: any }
? TData extends { headers?: any }
? OmitKeys<RequestOptions<unknown, TResponseStyle, ThrowOnError>, "body" | "headers" | "url"> & TData
? OmitKeys<RequestOptions<unknown, TResponseStyle, ThrowOnError>, "body" | "headers" | "url"> &
TData
: OmitKeys<RequestOptions<unknown, TResponseStyle, ThrowOnError>, "body" | "url"> &
TData &
Pick<RequestOptions<unknown, TResponseStyle, ThrowOnError>, "headers">

View file

@ -3,11 +3,19 @@
import { getAuthToken } from "../core/auth.gen.js"
import type { QuerySerializerOptions } from "../core/bodySerializer.gen.js"
import { jsonBodySerializer } from "../core/bodySerializer.gen.js"
import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer.gen.js"
import {
serializeArrayParam,
serializeObjectParam,
serializePrimitiveParam,
} from "../core/pathSerializer.gen.js"
import { getUrl } from "../core/utils.gen.js"
import type { Client, ClientOptions, Config, RequestOptions } from "./types.gen.js"
export const createQuerySerializer = <T = unknown>({ allowReserved, array, object }: QuerySerializerOptions = {}) => {
export const createQuerySerializer = <T = unknown>({
allowReserved,
array,
object,
}: QuerySerializerOptions = {}) => {
const querySerializer = (queryParams: T) => {
const search: string[] = []
if (queryParams && typeof queryParams === "object") {
@ -77,7 +85,9 @@ export const getParseAs = (contentType: string | null): Exclude<Config["parseAs"
return "formData"
}
if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
if (
["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))
) {
return "blob"
}
@ -97,7 +107,11 @@ const checkForExistence = (
if (!name) {
return false
}
if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
if (
options.headers.has(name) ||
options.query?.[name] ||
options.headers.get("Cookie")?.includes(`${name}=`)
) {
return true
}
return false
@ -162,7 +176,9 @@ export const mergeConfigs = (a: Config, b: Config): Config => {
return config
}
export const mergeHeaders = (...headers: Array<Required<Config>["headers"] | undefined>): Headers => {
export const mergeHeaders = (
...headers: Array<Required<Config>["headers"] | undefined>
): Headers => {
const mergedHeaders = new Headers()
for (const header of headers) {
if (!header || typeof header !== "object") {
@ -181,7 +197,10 @@ export const mergeHeaders = (...headers: Array<Required<Config>["headers"] | und
} else if (value !== undefined) {
// assume object headers are meant to be JSON stringified, i.e. their
// content value in OpenAPI specification is 'application/json'
mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : (value as string))
mergedHeaders.set(
key,
typeof value === "object" ? JSON.stringify(value) : (value as string),
)
}
}
}
@ -197,7 +216,11 @@ type ErrInterceptor<Err, Res, Req, Options> = (
type ReqInterceptor<Req, Options> = (request: Req, options: Options) => Req | Promise<Req>
type ResInterceptor<Res, Req, Options> = (response: Res, request: Req, options: Options) => Res | Promise<Res>
type ResInterceptor<Res, Req, Options> = (
response: Res,
request: Req,
options: Options,
) => Res | Promise<Res>
class Interceptors<Interceptor> {
_fns: (Interceptor | null)[]

View file

@ -31,7 +31,9 @@ const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value:
}
export const formDataBodySerializer = {
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): FormData => {
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(
body: T,
): FormData => {
const data = new FormData()
Object.entries(body).forEach(([key, value]) => {

View file

@ -74,9 +74,9 @@ export const serializeArrayParam = ({
value: unknown[]
}) => {
if (!explode) {
const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v as string))).join(
separatorArrayNoExplode(style),
)
const joinedValues = (
allowReserved ? value : value.map((v) => encodeURIComponent(v as string))
).join(separatorArrayNoExplode(style))
switch (style) {
case "label":
return `.${joinedValues}`
@ -106,7 +106,11 @@ export const serializeArrayParam = ({
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
}
export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => {
export const serializePrimitiveParam = ({
allowReserved,
name,
value,
}: SerializePrimitiveParam) => {
if (value === undefined || value === null) {
return ""
}

View file

@ -60,7 +60,11 @@ export interface StreamEvent<TData = unknown> {
}
export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unknown> = {
stream: AsyncGenerator<TData extends Record<string, unknown> ? TData[keyof TData] : TData, TReturn, TNext>
stream: AsyncGenerator<
TData extends Record<string, unknown> ? TData[keyof TData] : TData,
TReturn,
TNext
>
}
export const createSseClient = <TData = unknown>({

View file

@ -1,7 +1,11 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { Auth, AuthToken } from "./auth.gen.js"
import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen.js"
import type {
BodySerializer,
QuerySerializer,
QuerySerializerOptions,
} from "./bodySerializer.gen.js"
export interface Client<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never> {
/**
@ -41,7 +45,10 @@ export interface Config {
*/
headers?:
| RequestInit["headers"]
| Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>
| Record<
string,
string | number | boolean | (string | number | boolean)[] | null | undefined | unknown
>
/**
* The request method.
*

View file

@ -73,7 +73,9 @@ export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
continue
}
const replaceValue = encodeURIComponent(style === "label" ? `.${value as string}` : (value as string))
const replaceValue = encodeURIComponent(
style === "label" ? `.${value as string}` : (value as string),
)
url = url.replace(match, replaceValue)
}
}

View file

@ -138,10 +138,10 @@ import type {
} from "./types.gen.js"
import { client as _heyApiClient } from "./client.gen.js"
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<
TData,
ThrowOnError
> & {
export type Options<
TData extends TDataShape = TDataShape,
ThrowOnError extends boolean = boolean,
> = ClientOptions<TData, ThrowOnError> & {
/**
* You can provide a client instance returned by `createClient()` instead of
* individual options. This might be also useful if you want to implement a
@ -169,7 +169,9 @@ class Project extends _HeyApiClient {
/**
* List all projects
*/
public list<ThrowOnError extends boolean = false>(options?: Options<ProjectListData, ThrowOnError>) {
public list<ThrowOnError extends boolean = false>(
options?: Options<ProjectListData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<ProjectListResponses, unknown, ThrowOnError>({
url: "/project",
...options,
@ -179,7 +181,9 @@ class Project extends _HeyApiClient {
/**
* Get the current project
*/
public current<ThrowOnError extends boolean = false>(options?: Options<ProjectCurrentData, ThrowOnError>) {
public current<ThrowOnError extends boolean = false>(
options?: Options<ProjectCurrentData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<ProjectCurrentResponses, unknown, ThrowOnError>({
url: "/project/current",
...options,
@ -201,8 +205,14 @@ class Config extends _HeyApiClient {
/**
* Update config
*/
public update<ThrowOnError extends boolean = false>(options?: Options<ConfigUpdateData, ThrowOnError>) {
return (options?.client ?? this._client).patch<ConfigUpdateResponses, ConfigUpdateErrors, ThrowOnError>({
public update<ThrowOnError extends boolean = false>(
options?: Options<ConfigUpdateData, ThrowOnError>,
) {
return (options?.client ?? this._client).patch<
ConfigUpdateResponses,
ConfigUpdateErrors,
ThrowOnError
>({
url: "/config",
...options,
headers: {
@ -215,7 +225,9 @@ class Config extends _HeyApiClient {
/**
* List all providers
*/
public providers<ThrowOnError extends boolean = false>(options?: Options<ConfigProvidersData, ThrowOnError>) {
public providers<ThrowOnError extends boolean = false>(
options?: Options<ConfigProvidersData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<ConfigProvidersResponses, unknown, ThrowOnError>({
url: "/config/providers",
...options,
@ -261,7 +273,9 @@ class Session extends _HeyApiClient {
/**
* List all sessions
*/
public list<ThrowOnError extends boolean = false>(options?: Options<SessionListData, ThrowOnError>) {
public list<ThrowOnError extends boolean = false>(
options?: Options<SessionListData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<SessionListResponses, unknown, ThrowOnError>({
url: "/session",
...options,
@ -271,8 +285,14 @@ class Session extends _HeyApiClient {
/**
* Create a new session
*/
public create<ThrowOnError extends boolean = false>(options?: Options<SessionCreateData, ThrowOnError>) {
return (options?.client ?? this._client).post<SessionCreateResponses, SessionCreateErrors, ThrowOnError>({
public create<ThrowOnError extends boolean = false>(
options?: Options<SessionCreateData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<
SessionCreateResponses,
SessionCreateErrors,
ThrowOnError
>({
url: "/session",
...options,
headers: {
@ -285,8 +305,14 @@ class Session extends _HeyApiClient {
/**
* Delete a session and all its data
*/
public delete<ThrowOnError extends boolean = false>(options: Options<SessionDeleteData, ThrowOnError>) {
return (options.client ?? this._client).delete<SessionDeleteResponses, SessionDeleteErrors, ThrowOnError>({
public delete<ThrowOnError extends boolean = false>(
options: Options<SessionDeleteData, ThrowOnError>,
) {
return (options.client ?? this._client).delete<
SessionDeleteResponses,
SessionDeleteErrors,
ThrowOnError
>({
url: "/session/{id}",
...options,
})
@ -296,7 +322,11 @@ class Session extends _HeyApiClient {
* Get session
*/
public get<ThrowOnError extends boolean = false>(options: Options<SessionGetData, ThrowOnError>) {
return (options.client ?? this._client).get<SessionGetResponses, SessionGetErrors, ThrowOnError>({
return (options.client ?? this._client).get<
SessionGetResponses,
SessionGetErrors,
ThrowOnError
>({
url: "/session/{id}",
...options,
})
@ -305,8 +335,14 @@ class Session extends _HeyApiClient {
/**
* Update session properties
*/
public update<ThrowOnError extends boolean = false>(options: Options<SessionUpdateData, ThrowOnError>) {
return (options.client ?? this._client).patch<SessionUpdateResponses, SessionUpdateErrors, ThrowOnError>({
public update<ThrowOnError extends boolean = false>(
options: Options<SessionUpdateData, ThrowOnError>,
) {
return (options.client ?? this._client).patch<
SessionUpdateResponses,
SessionUpdateErrors,
ThrowOnError
>({
url: "/session/{id}",
...options,
headers: {
@ -319,8 +355,14 @@ class Session extends _HeyApiClient {
/**
* Get a session's children
*/
public children<ThrowOnError extends boolean = false>(options: Options<SessionChildrenData, ThrowOnError>) {
return (options.client ?? this._client).get<SessionChildrenResponses, SessionChildrenErrors, ThrowOnError>({
public children<ThrowOnError extends boolean = false>(
options: Options<SessionChildrenData, ThrowOnError>,
) {
return (options.client ?? this._client).get<
SessionChildrenResponses,
SessionChildrenErrors,
ThrowOnError
>({
url: "/session/{id}/children",
...options,
})
@ -329,8 +371,14 @@ class Session extends _HeyApiClient {
/**
* Get the todo list for a session
*/
public todo<ThrowOnError extends boolean = false>(options: Options<SessionTodoData, ThrowOnError>) {
return (options.client ?? this._client).get<SessionTodoResponses, SessionTodoErrors, ThrowOnError>({
public todo<ThrowOnError extends boolean = false>(
options: Options<SessionTodoData, ThrowOnError>,
) {
return (options.client ?? this._client).get<
SessionTodoResponses,
SessionTodoErrors,
ThrowOnError
>({
url: "/session/{id}/todo",
...options,
})
@ -339,8 +387,14 @@ class Session extends _HeyApiClient {
/**
* Analyze the app and create an AGENTS.md file
*/
public init<ThrowOnError extends boolean = false>(options: Options<SessionInitData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionInitResponses, SessionInitErrors, ThrowOnError>({
public init<ThrowOnError extends boolean = false>(
options: Options<SessionInitData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionInitResponses,
SessionInitErrors,
ThrowOnError
>({
url: "/session/{id}/init",
...options,
headers: {
@ -353,7 +407,9 @@ class Session extends _HeyApiClient {
/**
* Fork an existing session at a specific message
*/
public fork<ThrowOnError extends boolean = false>(options: Options<SessionForkData, ThrowOnError>) {
public fork<ThrowOnError extends boolean = false>(
options: Options<SessionForkData, ThrowOnError>,
) {
return (options.client ?? this._client).post<SessionForkResponses, unknown, ThrowOnError>({
url: "/session/{id}/fork",
...options,
@ -367,8 +423,14 @@ class Session extends _HeyApiClient {
/**
* Abort a session
*/
public abort<ThrowOnError extends boolean = false>(options: Options<SessionAbortData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionAbortResponses, SessionAbortErrors, ThrowOnError>({
public abort<ThrowOnError extends boolean = false>(
options: Options<SessionAbortData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionAbortResponses,
SessionAbortErrors,
ThrowOnError
>({
url: "/session/{id}/abort",
...options,
})
@ -377,8 +439,14 @@ class Session extends _HeyApiClient {
/**
* Unshare the session
*/
public unshare<ThrowOnError extends boolean = false>(options: Options<SessionUnshareData, ThrowOnError>) {
return (options.client ?? this._client).delete<SessionUnshareResponses, SessionUnshareErrors, ThrowOnError>({
public unshare<ThrowOnError extends boolean = false>(
options: Options<SessionUnshareData, ThrowOnError>,
) {
return (options.client ?? this._client).delete<
SessionUnshareResponses,
SessionUnshareErrors,
ThrowOnError
>({
url: "/session/{id}/share",
...options,
})
@ -387,8 +455,14 @@ class Session extends _HeyApiClient {
/**
* Share a session
*/
public share<ThrowOnError extends boolean = false>(options: Options<SessionShareData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionShareResponses, SessionShareErrors, ThrowOnError>({
public share<ThrowOnError extends boolean = false>(
options: Options<SessionShareData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionShareResponses,
SessionShareErrors,
ThrowOnError
>({
url: "/session/{id}/share",
...options,
})
@ -397,7 +471,9 @@ class Session extends _HeyApiClient {
/**
* Get the diff that resulted from this user message
*/
public diff<ThrowOnError extends boolean = false>(options: Options<SessionDiffData, ThrowOnError>) {
public diff<ThrowOnError extends boolean = false>(
options: Options<SessionDiffData, ThrowOnError>,
) {
return (options.client ?? this._client).get<SessionDiffResponses, unknown, ThrowOnError>({
url: "/session/{id}/diff",
...options,
@ -407,8 +483,14 @@ class Session extends _HeyApiClient {
/**
* Summarize the session
*/
public summarize<ThrowOnError extends boolean = false>(options: Options<SessionSummarizeData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionSummarizeResponses, SessionSummarizeErrors, ThrowOnError>({
public summarize<ThrowOnError extends boolean = false>(
options: Options<SessionSummarizeData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionSummarizeResponses,
SessionSummarizeErrors,
ThrowOnError
>({
url: "/session/{id}/summarize",
...options,
headers: {
@ -421,8 +503,14 @@ class Session extends _HeyApiClient {
/**
* List messages for a session
*/
public messages<ThrowOnError extends boolean = false>(options: Options<SessionMessagesData, ThrowOnError>) {
return (options.client ?? this._client).get<SessionMessagesResponses, SessionMessagesErrors, ThrowOnError>({
public messages<ThrowOnError extends boolean = false>(
options: Options<SessionMessagesData, ThrowOnError>,
) {
return (options.client ?? this._client).get<
SessionMessagesResponses,
SessionMessagesErrors,
ThrowOnError
>({
url: "/session/{id}/message",
...options,
})
@ -431,8 +519,14 @@ class Session extends _HeyApiClient {
/**
* Create and send a new message to a session
*/
public prompt<ThrowOnError extends boolean = false>(options: Options<SessionPromptData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionPromptResponses, SessionPromptErrors, ThrowOnError>({
public prompt<ThrowOnError extends boolean = false>(
options: Options<SessionPromptData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionPromptResponses,
SessionPromptErrors,
ThrowOnError
>({
url: "/session/{id}/message",
...options,
headers: {
@ -445,8 +539,14 @@ class Session extends _HeyApiClient {
/**
* Get a message from a session
*/
public message<ThrowOnError extends boolean = false>(options: Options<SessionMessageData, ThrowOnError>) {
return (options.client ?? this._client).get<SessionMessageResponses, SessionMessageErrors, ThrowOnError>({
public message<ThrowOnError extends boolean = false>(
options: Options<SessionMessageData, ThrowOnError>,
) {
return (options.client ?? this._client).get<
SessionMessageResponses,
SessionMessageErrors,
ThrowOnError
>({
url: "/session/{id}/message/{messageID}",
...options,
})
@ -455,8 +555,14 @@ class Session extends _HeyApiClient {
/**
* Send a new command to a session
*/
public command<ThrowOnError extends boolean = false>(options: Options<SessionCommandData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionCommandResponses, SessionCommandErrors, ThrowOnError>({
public command<ThrowOnError extends boolean = false>(
options: Options<SessionCommandData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionCommandResponses,
SessionCommandErrors,
ThrowOnError
>({
url: "/session/{id}/command",
...options,
headers: {
@ -469,8 +575,14 @@ class Session extends _HeyApiClient {
/**
* Run a shell command
*/
public shell<ThrowOnError extends boolean = false>(options: Options<SessionShellData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionShellResponses, SessionShellErrors, ThrowOnError>({
public shell<ThrowOnError extends boolean = false>(
options: Options<SessionShellData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionShellResponses,
SessionShellErrors,
ThrowOnError
>({
url: "/session/{id}/shell",
...options,
headers: {
@ -483,8 +595,14 @@ class Session extends _HeyApiClient {
/**
* Revert a message
*/
public revert<ThrowOnError extends boolean = false>(options: Options<SessionRevertData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionRevertResponses, SessionRevertErrors, ThrowOnError>({
public revert<ThrowOnError extends boolean = false>(
options: Options<SessionRevertData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionRevertResponses,
SessionRevertErrors,
ThrowOnError
>({
url: "/session/{id}/revert",
...options,
headers: {
@ -497,8 +615,14 @@ class Session extends _HeyApiClient {
/**
* Restore all reverted messages
*/
public unrevert<ThrowOnError extends boolean = false>(options: Options<SessionUnrevertData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionUnrevertResponses, SessionUnrevertErrors, ThrowOnError>({
public unrevert<ThrowOnError extends boolean = false>(
options: Options<SessionUnrevertData, ThrowOnError>,
) {
return (options.client ?? this._client).post<
SessionUnrevertResponses,
SessionUnrevertErrors,
ThrowOnError
>({
url: "/session/{id}/unrevert",
...options,
})
@ -509,7 +633,9 @@ class Command extends _HeyApiClient {
/**
* List all commands
*/
public list<ThrowOnError extends boolean = false>(options?: Options<CommandListData, ThrowOnError>) {
public list<ThrowOnError extends boolean = false>(
options?: Options<CommandListData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<CommandListResponses, unknown, ThrowOnError>({
url: "/command",
...options,
@ -531,7 +657,9 @@ class Find extends _HeyApiClient {
/**
* Find files
*/
public files<ThrowOnError extends boolean = false>(options: Options<FindFilesData, ThrowOnError>) {
public files<ThrowOnError extends boolean = false>(
options: Options<FindFilesData, ThrowOnError>,
) {
return (options.client ?? this._client).get<FindFilesResponses, unknown, ThrowOnError>({
url: "/find/file",
...options,
@ -541,7 +669,9 @@ class Find extends _HeyApiClient {
/**
* Find workspace symbols
*/
public symbols<ThrowOnError extends boolean = false>(options: Options<FindSymbolsData, ThrowOnError>) {
public symbols<ThrowOnError extends boolean = false>(
options: Options<FindSymbolsData, ThrowOnError>,
) {
return (options.client ?? this._client).get<FindSymbolsResponses, unknown, ThrowOnError>({
url: "/find/symbol",
...options,
@ -573,7 +703,9 @@ class File extends _HeyApiClient {
/**
* Get file status
*/
public status<ThrowOnError extends boolean = false>(options?: Options<FileStatusData, ThrowOnError>) {
public status<ThrowOnError extends boolean = false>(
options?: Options<FileStatusData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<FileStatusResponses, unknown, ThrowOnError>({
url: "/file/status",
...options,
@ -599,7 +731,9 @@ class App extends _HeyApiClient {
/**
* List all agents
*/
public agents<ThrowOnError extends boolean = false>(options?: Options<AppAgentsData, ThrowOnError>) {
public agents<ThrowOnError extends boolean = false>(
options?: Options<AppAgentsData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<AppAgentsResponses, unknown, ThrowOnError>({
url: "/agent",
...options,
@ -611,7 +745,9 @@ class Mcp extends _HeyApiClient {
/**
* Get MCP server status
*/
public status<ThrowOnError extends boolean = false>(options?: Options<McpStatusData, ThrowOnError>) {
public status<ThrowOnError extends boolean = false>(
options?: Options<McpStatusData, ThrowOnError>,
) {
return (options?.client ?? this._client).get<McpStatusResponses, unknown, ThrowOnError>({
url: "/mcp",
...options,
@ -635,8 +771,14 @@ class Tui extends _HeyApiClient {
/**
* Append prompt to the TUI
*/
public appendPrompt<ThrowOnError extends boolean = false>(options?: Options<TuiAppendPromptData, ThrowOnError>) {
return (options?.client ?? this._client).post<TuiAppendPromptResponses, TuiAppendPromptErrors, ThrowOnError>({
public appendPrompt<ThrowOnError extends boolean = false>(
options?: Options<TuiAppendPromptData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<
TuiAppendPromptResponses,
TuiAppendPromptErrors,
ThrowOnError
>({
url: "/tui/append-prompt",
...options,
headers: {
@ -649,7 +791,9 @@ class Tui extends _HeyApiClient {
/**
* Open the help dialog
*/
public openHelp<ThrowOnError extends boolean = false>(options?: Options<TuiOpenHelpData, ThrowOnError>) {
public openHelp<ThrowOnError extends boolean = false>(
options?: Options<TuiOpenHelpData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<TuiOpenHelpResponses, unknown, ThrowOnError>({
url: "/tui/open-help",
...options,
@ -659,7 +803,9 @@ class Tui extends _HeyApiClient {
/**
* Open the session dialog
*/
public openSessions<ThrowOnError extends boolean = false>(options?: Options<TuiOpenSessionsData, ThrowOnError>) {
public openSessions<ThrowOnError extends boolean = false>(
options?: Options<TuiOpenSessionsData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<TuiOpenSessionsResponses, unknown, ThrowOnError>({
url: "/tui/open-sessions",
...options,
@ -669,7 +815,9 @@ class Tui extends _HeyApiClient {
/**
* Open the theme dialog
*/
public openThemes<ThrowOnError extends boolean = false>(options?: Options<TuiOpenThemesData, ThrowOnError>) {
public openThemes<ThrowOnError extends boolean = false>(
options?: Options<TuiOpenThemesData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<TuiOpenThemesResponses, unknown, ThrowOnError>({
url: "/tui/open-themes",
...options,
@ -679,7 +827,9 @@ class Tui extends _HeyApiClient {
/**
* Open the model dialog
*/
public openModels<ThrowOnError extends boolean = false>(options?: Options<TuiOpenModelsData, ThrowOnError>) {
public openModels<ThrowOnError extends boolean = false>(
options?: Options<TuiOpenModelsData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<TuiOpenModelsResponses, unknown, ThrowOnError>({
url: "/tui/open-models",
...options,
@ -689,7 +839,9 @@ class Tui extends _HeyApiClient {
/**
* Submit the prompt
*/
public submitPrompt<ThrowOnError extends boolean = false>(options?: Options<TuiSubmitPromptData, ThrowOnError>) {
public submitPrompt<ThrowOnError extends boolean = false>(
options?: Options<TuiSubmitPromptData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<TuiSubmitPromptResponses, unknown, ThrowOnError>({
url: "/tui/submit-prompt",
...options,
@ -699,7 +851,9 @@ class Tui extends _HeyApiClient {
/**
* Clear the prompt
*/
public clearPrompt<ThrowOnError extends boolean = false>(options?: Options<TuiClearPromptData, ThrowOnError>) {
public clearPrompt<ThrowOnError extends boolean = false>(
options?: Options<TuiClearPromptData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<TuiClearPromptResponses, unknown, ThrowOnError>({
url: "/tui/clear-prompt",
...options,
@ -709,8 +863,14 @@ class Tui extends _HeyApiClient {
/**
* Execute a TUI command (e.g. agent_cycle)
*/
public executeCommand<ThrowOnError extends boolean = false>(options?: Options<TuiExecuteCommandData, ThrowOnError>) {
return (options?.client ?? this._client).post<TuiExecuteCommandResponses, TuiExecuteCommandErrors, ThrowOnError>({
public executeCommand<ThrowOnError extends boolean = false>(
options?: Options<TuiExecuteCommandData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<
TuiExecuteCommandResponses,
TuiExecuteCommandErrors,
ThrowOnError
>({
url: "/tui/execute-command",
...options,
headers: {
@ -723,7 +883,9 @@ class Tui extends _HeyApiClient {
/**
* Show a toast notification in the TUI
*/
public showToast<ThrowOnError extends boolean = false>(options?: Options<TuiShowToastData, ThrowOnError>) {
public showToast<ThrowOnError extends boolean = false>(
options?: Options<TuiShowToastData, ThrowOnError>,
) {
return (options?.client ?? this._client).post<TuiShowToastResponses, unknown, ThrowOnError>({
url: "/tui/show-toast",
...options,
@ -769,8 +931,14 @@ class Event extends _HeyApiClient {
/**
* Get events
*/
public subscribe<ThrowOnError extends boolean = false>(options?: Options<EventSubscribeData, ThrowOnError>) {
return (options?.client ?? this._client).get.sse<EventSubscribeResponses, unknown, ThrowOnError>({
public subscribe<ThrowOnError extends boolean = false>(
options?: Options<EventSubscribeData, ThrowOnError>,
) {
return (options?.client ?? this._client).get.sse<
EventSubscribeResponses,
unknown,
ThrowOnError
>({
url: "/event",
...options,
})

View file

@ -517,6 +517,10 @@ export type Config = {
}
}>
}
/**
* Number of retries for chat completions on failure
*/
chatMaxRetries?: number
disable_paste_summary?: boolean
}
}
@ -670,7 +674,12 @@ export type AssistantMessage = {
created: number
completed?: number
}
error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError
error?:
| ProviderAuthError
| UnknownError
| MessageOutputLengthError
| MessageAbortedError
| ApiError
system: Array<string>
parentID: string
modelID: string
@ -1309,7 +1318,12 @@ export type EventSessionError = {
type: "session.error"
properties: {
sessionID?: string
error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError
error?:
| ProviderAuthError
| UnknownError
| MessageOutputLengthError
| MessageAbortedError
| ApiError
}
}

22
packages/sdk/python/.gitignore vendored Normal file
View file

@ -0,0 +1,22 @@
__pycache__/
*.py[cod]
*.egg-info/
.build/
build/
dist/
.coverage
htmlcov/
.mypy_cache/
.pytest_cache/
.ruff_cache/
.venv/
.conda/
.env
.DS_Store
openapi.json
site/
# IDE
.vscode/
.idea/

View file

@ -0,0 +1,82 @@
# Opencode Python SDK
This package provides a Python SDK for the Opencode API. It is generated using openapi-python-client (not Stainless).
Documentation
- Full docs: see `mkdocs` site under `packages/sdk/python/docs/`
- Preview locally:
```bash
uv run --project packages/sdk/python mkdocs serve -f packages/sdk/python/mkdocs.yml
```
Badges
- PyPI: https://img.shields.io/pypi/v/opencode-ai?style=flat-square
Requirements
- Python 3.8+
- uv (recommended) -> https://docs.astral.sh/uv/
- openapi-python-client (invoked via `uvx`)
Install uv
```bash
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Set up the environment (from this directory)
```bash
uv sync --dev
```
Generate client code (from CLI-generated spec)
```bash
# From repository root OR from this directory
uv run python packages/sdk/python/scripts/generate.py --source cli
```
Alternatively, fetch spec from a running server
```bash
uv run python packages/sdk/python/scripts/generate.py --source server --server-url http://localhost:4096/doc
```
This will:
1) Produce an OpenAPI spec from the local CLI or a running server
2) Run openapi-python-client (via `uvx`) to generate client code
3) Copy the generated Python package into src/opencode_ai
Usage (after generation)
```python
from opencode_ai import OpenCodeClient
client = OpenCodeClient(base_url="http://localhost:4096")
print(client.get_config())
# See examples/basic_usage.py for more details
# Streaming events (sync)
for event in client.subscribe_events():
print(event)
break
# Error handling and retries
# Set retries>0 to enable exponential backoff for transient errors like 429/5xx
client = OpenCodeClient(retries=2, backoff_factor=0.1)
# Async usage example
# uv run --project packages/sdk/python python - <<'PY'
# import asyncio
# from opencode_ai import OpenCodeClient
# async def main():
# client = OpenCodeClient()
# async for event in client.subscribe_events_async():
# print(event)
# break
# asyncio.run(main())
# PY
```
Notes
- We intentionally do not use Stainless for the Python SDK.
- The generator targets OpenAPI 3.1 emitted by the opencode server at /doc.
- See scripts/generate.py for details and customization points.

View file

@ -0,0 +1,19 @@
# Generation workflow
The SDK is generated from the Opencode server's OpenAPI 3.1 spec.
Two source modes are supported:
- CLI (default): runs `bun dev generate` to emit the OpenAPI JSON
- Server: fetches `http://localhost:4096/doc` from a running server
Generator command
```bash
# From repo root
uv run --project packages/sdk/python python packages/sdk/python/scripts/generate.py --source cli
# Or
uv run --project packages/sdk/python python packages/sdk/python/scripts/generate.py --source server --server-url http://localhost:4096/doc
```
Post-generation
- The generator injects `extras.py` (OpenCodeClient) and patches `__init__.py` to export it
- Code is formatted with `ruff` (imports) and `black`

View file

@ -0,0 +1,11 @@
# Opencode Python SDK
The official Python client for the Opencode API, generated from the OpenAPI spec and extended with ergonomic helpers.
Highlights
- Provider-agnostic client generated from OpenAPI 3.1
- Thin convenience wrapper (OpenCodeClient) for common tasks
- Sync and async SSE streaming for live event feeds
- First-class uv support for development
If you're new, start with Quickstart or Installation in the navigation.

View file

@ -0,0 +1,27 @@
# Installation
Requirements
- Python 3.8+
- uv (recommended) -> https://docs.astral.sh/uv/
Install uv
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Project setup
```bash
# From repo root or this directory
uv sync --dev --project packages/sdk/python
```
Using pip (alternative)
```bash
pip install opencode-ai
```
Preview docs locally
```bash
# From repo root
uv run --project packages/sdk/python mkdocs serve -f packages/sdk/python/mkdocs.yml
```

View file

@ -0,0 +1,24 @@
# Publishing (maintainers)
Automated publishing runs on GitHub Releases.
Workflow
- Create a new Release (the tag value becomes the package version)
- The `publish-python-sdk` workflow will:
- Generate the SDK from OpenAPI (CLI path)
- Set the version in `pyproject.toml` and generator config
- Build wheel/sdist and upload to PyPI
Prerequisites
- Repository secret: `PYPI_API_TOKEN`
Manual publish
```bash
# TestPyPI
REPOSITORY=testpypi PYPI_TOKEN=$TEST_PYPI_API_TOKEN \
uv run --project packages/sdk/python python packages/sdk/python/scripts/publish.py
# PyPI
REPOSITORY=pypi PYPI_TOKEN=$PYPI_API_TOKEN \
uv run --project packages/sdk/python python packages/sdk/python/scripts/publish.py
```

View file

@ -0,0 +1,22 @@
# Quickstart
Create a client and make your first calls.
```python
from opencode_ai import OpenCodeClient
client = OpenCodeClient(base_url="http://localhost:4096")
# List projects
for p in client.list_projects() or []:
print(p.id, p.directory)
# Get path info
path = client.get_path()
print(path.directory)
# Stream events (sync)
for event in client.subscribe_events():
print(event)
break
```

View file

@ -0,0 +1,15 @@
# Testing
Run unit, mock, and integration tests.
```bash
# Sync dev dependencies
uv sync --dev --project packages/sdk/python
# Run tests
uv run --project packages/sdk/python pytest -q
```
Notes
- Integration test starts a headless opencode server via Bun in a subprocess
- SSE behavior is validated using real streaming from the server

View file

@ -0,0 +1,21 @@
# Configuration
OpenCodeClient accepts common options for auth, timeouts, and retries.
```python
from opencode_ai import OpenCodeClient
client = OpenCodeClient(
base_url="http://localhost:4096",
token="pypi-or-other-token",
auth_header_name="Authorization",
auth_prefix="Bearer",
timeout=30.0, # seconds
retries=2,
backoff_factor=0.2, # exponential backoff
)
```
- Auth: sets the header `{auth_header_name}: {auth_prefix} {token}` when `token` is provided
- Retries: retry on transient httpx.RequestError and 429/5xx
- Timeouts: passed to httpx.Timeout

View file

@ -0,0 +1,22 @@
# Files & Projects
Access file status and project information.
```python
from opencode_ai import OpenCodeClient
client = OpenCodeClient()
# Projects
for p in client.list_projects() or []:
print(p.id, p.directory)
# Current path
pinfo = client.get_path()
print(pinfo.directory)
# File status
files = client.file_status() or []
for f in files:
print(f.path, f.type)
```

View file

@ -0,0 +1,18 @@
# Sessions
List sessions and inspect them. The wrapper exposes a convenience method while the generated API remains available under `opencode_ai.api.default`.
```python
from opencode_ai import OpenCodeClient
from opencode_ai.api.default import session_list as generated
client = OpenCodeClient()
# Wrapper
sessions = client.list_sessions() or []
# Generated function
sessions2 = generated.sync(client=client.client)
print(len(sessions), len(sessions2))
```

View file

@ -0,0 +1,29 @@
# Streaming (SSE)
Subscribe to the event stream. The wrapper provides both sync and async interfaces.
```python
from opencode_ai import OpenCodeClient
client = OpenCodeClient()
# Sync streaming
for event in client.subscribe_events():
print(event)
break
```
Async variant:
```python
import asyncio
from opencode_ai import OpenCodeClient
async def main():
client = OpenCodeClient()
async for event in client.subscribe_events_async():
print(event)
break
asyncio.run(main())
```

View file

@ -0,0 +1,19 @@
# Basic usage example (placeholder)
# After generating the client, this should reflect actual client entrypoints.
try:
from opencode_ai import client # type: ignore
except Exception: # pragma: no cover
client = None
def main() -> None:
if client is None:
print("Client not generated yet. Run the generator first:")
print(" uv run python packages/sdk/python/scripts/generate.py")
return
print("Replace this with real example code once the client is generated.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,6 @@
from opencode_ai import OpenCodeClient
client = OpenCodeClient()
files = client.file_status() or []
for f in files:
print(f.path, f.type)

View file

@ -0,0 +1,4 @@
from opencode_ai import OpenCodeClient
client = OpenCodeClient()
print([s.id for s in client.list_sessions() or []])

View file

@ -0,0 +1,29 @@
site_name: Opencode Python SDK
site_description: Official Python SDK for the Opencode API
site_url: https://opencode.ai
repo_url: https://github.com/sst/opencode
repo_name: sst/opencode
edit_uri: ''
theme:
name: material
features:
- navigation.tabs
- navigation.sections
- content.code.copy
markdown_extensions:
- admonition
- codehilite
- toc:
permalink: true
nav:
- Overview: index.md
- Installation: installation.md
- Quickstart: quickstart.md
- Usage:
- Configuration: usage/configuration.md
- Sessions: usage/sessions.md
- Files & Projects: usage/files_projects.md
- Streaming (SSE): usage/streaming.md
- Generation: generation.md
- Testing: testing.md
- Publishing (maintainers): publishing.md

View file

@ -0,0 +1,5 @@
# Configuration for openapi-python-client
# Ensures consistent project and package names and version when generating.
project_name_override: opencode-ai
package_name_override: opencode_ai
package_version_override: 0.1.0

View file

@ -0,0 +1,56 @@
[build-system]
requires = ["hatchling>=1.17.0"]
build-backend = "hatchling.build"
[project]
name = "opencode-ai"
version = "0.1.0"
description = "Python client for the Opencode API (generated via openapi-python-client)"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{ name = "Opencode Authors", email = "support@sst.dev" }
]
dependencies = [
"httpx>=0.27.0",
"pydantic>=2.0.0",
"python-dateutil>=2.8.2"
]
[project.urls]
Homepage = "https://opencode.ai"
Repository = "https://github.com/sst/opencode"
[tool.uv]
# Development-time dependencies installed with `uv sync --dev`
dev-dependencies = [
"openapi-python-client",
"black",
"isort",
"ruff",
"pytest",
"pytest-asyncio",
"sseclient-py",
"build",
"twine",
"mkdocs",
"mkdocs-material",
]
[tool.black]
line-length = 120
target-version = ["py38", "py39", "py310", "py311", "py312"]
[tool.isort]
profile = "black"
line_length = 120
[tool.ruff]
line-length = 120
select = ["E", "F", "I", "UP"]
ignore = []
[tool.pytest.ini_options]
addopts = "-q"
pythonpath = ["src"]

View file

@ -0,0 +1,210 @@
#!/usr/bin/env python3
"""
Generate the Opencode Python SDK using openapi-python-client and place it under src/opencode_ai.
Steps:
- Generate OpenAPI JSON from the local CLI (bun dev generate)
- Run openapi-python-client (via `uvx` if available, else fallback to PATH)
- Copy the generated module into src/opencode_ai
Requires:
- Bun installed (for `bun dev generate`)
- uv installed (recommended) to run `uvx openapi-python-client`
"""
from __future__ import annotations
import argparse
import json
import shutil
import subprocess
import sys
from pathlib import Path
from urllib.request import urlopen
def run(cmd: list[str], cwd: Path | None = None) -> subprocess.CompletedProcess:
print("$", " ".join(cmd))
return subprocess.run(cmd, cwd=str(cwd) if cwd else None, check=True, capture_output=True, text=True)
def find_repo_root(start: Path) -> Path:
p = start
for _ in range(10):
if (p / ".git").exists() or (p / "sst.config.ts").exists():
return p
if p.parent == p:
break
p = p.parent
# Fallback: assume 4 levels up from scripts/
return start.parents[4]
def write_json(path: Path, content: str) -> None:
# Validate JSON before writing
json.loads(content)
path.write_text(content)
def main() -> int:
parser = argparse.ArgumentParser(description="Generate the Opencode Python SDK from OpenAPI spec.")
parser.add_argument(
"--source", choices=["cli", "server"], default="cli", help="Where to fetch the OpenAPI spec from"
)
parser.add_argument(
"--server-url",
default="http://localhost:4096/doc",
help="OpenAPI document URL when --source=server",
)
parser.add_argument(
"--out-spec",
default=None,
help="Output path for the OpenAPI spec (defaults to packages/sdk/python/openapi.json)",
)
parser.add_argument(
"--only-spec",
action="store_true",
help="Only fetch and write the OpenAPI spec without generating the client",
)
args = parser.parse_args()
script_dir = Path(__file__).resolve().parent
sdk_dir = script_dir.parent
repo_root = find_repo_root(script_dir)
opencode_dir = repo_root / "packages" / "opencode"
openapi_json = Path(args.out_spec) if args.out_spec else (sdk_dir / "openapi.json")
build_dir = sdk_dir / ".build"
out_pkg_dir = sdk_dir / "src" / "opencode_ai"
build_dir.mkdir(parents=True, exist_ok=True)
(sdk_dir / "src").mkdir(parents=True, exist_ok=True)
# 1) Obtain OpenAPI spec
if args.source == "server":
print(f"Fetching OpenAPI spec from {args.server_url} ...")
try:
with urlopen(args.server_url) as resp:
if resp.status != 200:
print(f"ERROR: GET {args.server_url} -> HTTP {resp.status}", file=sys.stderr)
return 1
text = resp.read().decode("utf-8")
except Exception as e:
print(f"ERROR: Failed to fetch from server: {e}", file=sys.stderr)
return 1
try:
write_json(openapi_json, text)
except json.JSONDecodeError as je:
print("ERROR: Response from server was not valid JSON:", file=sys.stderr)
print(str(je), file=sys.stderr)
return 1
print(f"Wrote OpenAPI spec to {openapi_json}")
else:
print("Generating OpenAPI spec via 'bun dev generate' ...")
try:
proc = run(["bun", "dev", "generate"], cwd=opencode_dir)
except subprocess.CalledProcessError as e:
print(e.stdout)
print(e.stderr, file=sys.stderr)
print(
"ERROR: Failed to run 'bun dev generate'. Ensure Bun is installed and available in PATH.",
file=sys.stderr,
)
return 1
try:
write_json(openapi_json, proc.stdout)
except json.JSONDecodeError as je:
print("ERROR: Output from 'bun dev generate' was not valid JSON:", file=sys.stderr)
print(str(je), file=sys.stderr)
return 1
print(f"Wrote OpenAPI spec to {openapi_json}")
if args.only_spec:
print("Spec written; skipping client generation (--only-spec).")
return 0
# 2) Run openapi-python-client
print("Running openapi-python-client generate ...")
# Prefer uvx if available
use_uvx = shutil.which("uvx") is not None
cmd = (["uvx", "openapi-python-client", "generate"] if use_uvx else ["openapi-python-client", "generate"]) + [
"--path",
str(openapi_json),
"--output-path",
str(build_dir),
"--overwrite",
"--config",
str(sdk_dir / "openapi-python-client.yaml"),
]
try:
run(cmd, cwd=sdk_dir)
except subprocess.CalledProcessError as e:
print(e.stdout)
print(e.stderr, file=sys.stderr)
print(
"ERROR: Failed to run openapi-python-client. Install uv and try again: curl -LsSf https://astral.sh/uv/install.sh | sh",
file=sys.stderr,
)
return 1
# 3) Locate generated module directory and copy to src/opencode_ai
generated_module: Path | None = None
for candidate in build_dir.rglob("__init__.py"):
if candidate.parent.name.startswith("."):
continue
siblings = {p.name for p in candidate.parent.glob("*.py")}
if "client.py" in siblings or "api_client.py" in siblings:
generated_module = candidate.parent
break
if not generated_module:
print("ERROR: Could not locate generated module directory in .build", file=sys.stderr)
return 1
print(f"Found generated module at {generated_module}")
# Clean target then copy
if out_pkg_dir.exists():
shutil.rmtree(out_pkg_dir)
shutil.copytree(generated_module, out_pkg_dir)
# Inject local extras from template if present
extras_template = sdk_dir / "templates" / "extras.py"
if extras_template.exists():
(out_pkg_dir / "extras.py").write_text(extras_template.read_text())
# Patch __init__ to export OpenCodeClient if present
init_path = out_pkg_dir / "__init__.py"
if init_path.exists() and (out_pkg_dir / "extras.py").exists():
init_text = (
'"""A client library for accessing opencode\n\n'
"This package is generated by openapi-python-client.\n"
"A thin convenience wrapper `OpenCodeClient` is also provided.\n"
'"""\n\n'
"from .client import AuthenticatedClient, Client\n"
"from .extras import OpenCodeClient\n\n"
"__all__ = (\n"
' "AuthenticatedClient",\n'
' "Client",\n'
' "OpenCodeClient",\n'
")\n"
)
init_path.write_text(init_text)
print(f"Copied generated client to {out_pkg_dir}")
# 4) Format generated code
try:
run(["uv", "run", "--project", str(sdk_dir), "ruff", "check", "--select", "I", "--fix", str(out_pkg_dir)])
run(["uv", "run", "--project", str(sdk_dir), "black", str(out_pkg_dir)])
except subprocess.CalledProcessError as e:
print("WARNING: formatting failed; continuing", file=sys.stderr)
print(e.stdout)
print(e.stderr, file=sys.stderr)
print("Done.")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""
Python SDK publishing helper.
- Builds sdist and wheel using `python -m build` into dist/
- Uploads using twine. Configure either TestPyPI or PyPI via environment:
Environment variables:
REPOSITORY : "pypi" (default) or "testpypi"
PYPI_TOKEN : API token (e.g., pypi-XXXX). For TestPyPI, use the TestPyPI token.
Examples:
REPOSITORY=testpypi PYPI_TOKEN=${{TEST_PYPI_API_TOKEN}} uv run --project packages/sdk/python python packages/sdk/python/scripts/publish.py
"""
from __future__ import annotations
import os
import subprocess
from pathlib import Path
def run(cmd: list[str], cwd: Path | None = None) -> None:
print("$", " ".join(cmd))
subprocess.run(cmd, cwd=str(cwd) if cwd else None, check=True)
def main() -> int:
sdk_dir = Path(__file__).resolve().parent.parent
repo = os.environ.get("REPOSITORY", "pypi").strip()
token = os.environ.get("PYPI_TOKEN")
if not token:
print("ERROR: PYPI_TOKEN not set", flush=True)
return 1
dist = sdk_dir / "dist"
if dist.exists():
for f in dist.iterdir():
f.unlink()
# Build
run(["python", "-m", "build"], cwd=sdk_dir)
# Upload
repo_url = {
"pypi": "https://upload.pypi.org/legacy/",
"testpypi": "https://test.pypi.org/legacy/",
}.get(repo, repo)
env = os.environ.copy()
env["TWINE_USERNAME"] = "__token__"
env["TWINE_PASSWORD"] = token
print(f"Uploading to {repo_url}")
subprocess.run(
["python", "-m", "twine", "check", "dist/*"], cwd=sdk_dir, check=True
)
subprocess.run(
["python", "-m", "twine", "upload", "--repository-url", repo_url, "dist/*"],
cwd=sdk_dir,
check=True,
env=env,
)
print("Publish complete")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,14 @@
"""A client library for accessing opencode
This package is generated by openapi-python-client.
A thin convenience wrapper `OpenCodeClient` is also provided.
"""
from .client import AuthenticatedClient, Client
from .extras import OpenCodeClient
__all__ = (
"AuthenticatedClient",
"Client",
"OpenCodeClient",
)

View file

@ -0,0 +1 @@
"""Contains methods for accessing the API"""

View file

@ -0,0 +1 @@
"""Contains endpoint functions for accessing the API"""

View file

@ -0,0 +1,160 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.agent import Agent
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/agent",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[list["Agent"]]:
if response.status_code == 200:
response_200 = []
_response_200 = response.json()
for response_200_item_data in _response_200:
response_200_item = Agent.from_dict(response_200_item_data)
response_200.append(response_200_item)
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[list["Agent"]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Agent"]]:
"""List all agents
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Agent']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Agent"]]:
"""List all agents
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Agent']
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Agent"]]:
"""List all agents
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Agent']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Agent"]]:
"""List all agents
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Agent']
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,164 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.command import Command
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/command",
"params": params,
}
return _kwargs
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[list["Command"]]:
if response.status_code == 200:
response_200 = []
_response_200 = response.json()
for response_200_item_data in _response_200:
response_200_item = Command.from_dict(response_200_item_data)
response_200.append(response_200_item)
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[list["Command"]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Command"]]:
"""List all commands
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Command']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Command"]]:
"""List all commands
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Command']
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Command"]]:
"""List all commands
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Command']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Command"]]:
"""List all commands
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Command']
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,155 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.config import Config
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/config",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Config]:
if response.status_code == 200:
response_200 = Config.from_dict(response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Config]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Config]:
"""Get config info
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Config]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Config]:
"""Get config info
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Config
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Config]:
"""Get config info
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Config]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Config]:
"""Get config info
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Config
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,159 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.config_providers_response_200 import ConfigProvidersResponse200
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/config/providers",
"params": params,
}
return _kwargs
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[ConfigProvidersResponse200]:
if response.status_code == 200:
response_200 = ConfigProvidersResponse200.from_dict(response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[ConfigProvidersResponse200]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[ConfigProvidersResponse200]:
"""List all providers
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[ConfigProvidersResponse200]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[ConfigProvidersResponse200]:
"""List all providers
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
ConfigProvidersResponse200
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[ConfigProvidersResponse200]:
"""List all providers
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[ConfigProvidersResponse200]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[ConfigProvidersResponse200]:
"""List all providers
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
ConfigProvidersResponse200
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,447 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.event_file_edited import EventFileEdited
from ...models.event_file_watcher_updated import EventFileWatcherUpdated
from ...models.event_ide_installed import EventIdeInstalled
from ...models.event_installation_updated import EventInstallationUpdated
from ...models.event_lsp_client_diagnostics import EventLspClientDiagnostics
from ...models.event_message_part_removed import EventMessagePartRemoved
from ...models.event_message_part_updated import EventMessagePartUpdated
from ...models.event_message_removed import EventMessageRemoved
from ...models.event_message_updated import EventMessageUpdated
from ...models.event_permission_replied import EventPermissionReplied
from ...models.event_permission_updated import EventPermissionUpdated
from ...models.event_server_connected import EventServerConnected
from ...models.event_session_compacted import EventSessionCompacted
from ...models.event_session_deleted import EventSessionDeleted
from ...models.event_session_error import EventSessionError
from ...models.event_session_idle import EventSessionIdle
from ...models.event_session_updated import EventSessionUpdated
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/event",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[
Union[
"EventFileEdited",
"EventFileWatcherUpdated",
"EventIdeInstalled",
"EventInstallationUpdated",
"EventLspClientDiagnostics",
"EventMessagePartRemoved",
"EventMessagePartUpdated",
"EventMessageRemoved",
"EventMessageUpdated",
"EventPermissionReplied",
"EventPermissionUpdated",
"EventServerConnected",
"EventSessionCompacted",
"EventSessionDeleted",
"EventSessionError",
"EventSessionIdle",
"EventSessionUpdated",
]
]:
if response.status_code == 200:
def _parse_response_200(
data: object,
) -> Union[
"EventFileEdited",
"EventFileWatcherUpdated",
"EventIdeInstalled",
"EventInstallationUpdated",
"EventLspClientDiagnostics",
"EventMessagePartRemoved",
"EventMessagePartUpdated",
"EventMessageRemoved",
"EventMessageUpdated",
"EventPermissionReplied",
"EventPermissionUpdated",
"EventServerConnected",
"EventSessionCompacted",
"EventSessionDeleted",
"EventSessionError",
"EventSessionIdle",
"EventSessionUpdated",
]:
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_0 = EventInstallationUpdated.from_dict(data)
return componentsschemas_event_type_0
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_1 = EventLspClientDiagnostics.from_dict(data)
return componentsschemas_event_type_1
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_2 = EventMessageUpdated.from_dict(data)
return componentsschemas_event_type_2
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_3 = EventMessageRemoved.from_dict(data)
return componentsschemas_event_type_3
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_4 = EventMessagePartUpdated.from_dict(data)
return componentsschemas_event_type_4
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_5 = EventMessagePartRemoved.from_dict(data)
return componentsschemas_event_type_5
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_6 = EventSessionCompacted.from_dict(data)
return componentsschemas_event_type_6
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_7 = EventPermissionUpdated.from_dict(data)
return componentsschemas_event_type_7
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_8 = EventPermissionReplied.from_dict(data)
return componentsschemas_event_type_8
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_9 = EventFileEdited.from_dict(data)
return componentsschemas_event_type_9
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_10 = EventSessionIdle.from_dict(data)
return componentsschemas_event_type_10
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_11 = EventSessionUpdated.from_dict(data)
return componentsschemas_event_type_11
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_12 = EventSessionDeleted.from_dict(data)
return componentsschemas_event_type_12
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_13 = EventSessionError.from_dict(data)
return componentsschemas_event_type_13
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_14 = EventFileWatcherUpdated.from_dict(data)
return componentsschemas_event_type_14
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_15 = EventServerConnected.from_dict(data)
return componentsschemas_event_type_15
except: # noqa: E722
pass
if not isinstance(data, dict):
raise TypeError()
componentsschemas_event_type_16 = EventIdeInstalled.from_dict(data)
return componentsschemas_event_type_16
response_200 = _parse_response_200(response.text)
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[
Union[
"EventFileEdited",
"EventFileWatcherUpdated",
"EventIdeInstalled",
"EventInstallationUpdated",
"EventLspClientDiagnostics",
"EventMessagePartRemoved",
"EventMessagePartUpdated",
"EventMessageRemoved",
"EventMessageUpdated",
"EventPermissionReplied",
"EventPermissionUpdated",
"EventServerConnected",
"EventSessionCompacted",
"EventSessionDeleted",
"EventSessionError",
"EventSessionIdle",
"EventSessionUpdated",
]
]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[
Union[
"EventFileEdited",
"EventFileWatcherUpdated",
"EventIdeInstalled",
"EventInstallationUpdated",
"EventLspClientDiagnostics",
"EventMessagePartRemoved",
"EventMessagePartUpdated",
"EventMessageRemoved",
"EventMessageUpdated",
"EventPermissionReplied",
"EventPermissionUpdated",
"EventServerConnected",
"EventSessionCompacted",
"EventSessionDeleted",
"EventSessionError",
"EventSessionIdle",
"EventSessionUpdated",
]
]:
"""Get events
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union['EventFileEdited', 'EventFileWatcherUpdated', 'EventIdeInstalled', 'EventInstallationUpdated', 'EventLspClientDiagnostics', 'EventMessagePartRemoved', 'EventMessagePartUpdated', 'EventMessageRemoved', 'EventMessageUpdated', 'EventPermissionReplied', 'EventPermissionUpdated', 'EventServerConnected', 'EventSessionCompacted', 'EventSessionDeleted', 'EventSessionError', 'EventSessionIdle', 'EventSessionUpdated']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[
Union[
"EventFileEdited",
"EventFileWatcherUpdated",
"EventIdeInstalled",
"EventInstallationUpdated",
"EventLspClientDiagnostics",
"EventMessagePartRemoved",
"EventMessagePartUpdated",
"EventMessageRemoved",
"EventMessageUpdated",
"EventPermissionReplied",
"EventPermissionUpdated",
"EventServerConnected",
"EventSessionCompacted",
"EventSessionDeleted",
"EventSessionError",
"EventSessionIdle",
"EventSessionUpdated",
]
]:
"""Get events
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union['EventFileEdited', 'EventFileWatcherUpdated', 'EventIdeInstalled', 'EventInstallationUpdated', 'EventLspClientDiagnostics', 'EventMessagePartRemoved', 'EventMessagePartUpdated', 'EventMessageRemoved', 'EventMessageUpdated', 'EventPermissionReplied', 'EventPermissionUpdated', 'EventServerConnected', 'EventSessionCompacted', 'EventSessionDeleted', 'EventSessionError', 'EventSessionIdle', 'EventSessionUpdated']
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[
Union[
"EventFileEdited",
"EventFileWatcherUpdated",
"EventIdeInstalled",
"EventInstallationUpdated",
"EventLspClientDiagnostics",
"EventMessagePartRemoved",
"EventMessagePartUpdated",
"EventMessageRemoved",
"EventMessageUpdated",
"EventPermissionReplied",
"EventPermissionUpdated",
"EventServerConnected",
"EventSessionCompacted",
"EventSessionDeleted",
"EventSessionError",
"EventSessionIdle",
"EventSessionUpdated",
]
]:
"""Get events
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union['EventFileEdited', 'EventFileWatcherUpdated', 'EventIdeInstalled', 'EventInstallationUpdated', 'EventLspClientDiagnostics', 'EventMessagePartRemoved', 'EventMessagePartUpdated', 'EventMessageRemoved', 'EventMessageUpdated', 'EventPermissionReplied', 'EventPermissionUpdated', 'EventServerConnected', 'EventSessionCompacted', 'EventSessionDeleted', 'EventSessionError', 'EventSessionIdle', 'EventSessionUpdated']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[
Union[
"EventFileEdited",
"EventFileWatcherUpdated",
"EventIdeInstalled",
"EventInstallationUpdated",
"EventLspClientDiagnostics",
"EventMessagePartRemoved",
"EventMessagePartUpdated",
"EventMessageRemoved",
"EventMessageUpdated",
"EventPermissionReplied",
"EventPermissionUpdated",
"EventServerConnected",
"EventSessionCompacted",
"EventSessionDeleted",
"EventSessionError",
"EventSessionIdle",
"EventSessionUpdated",
]
]:
"""Get events
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union['EventFileEdited', 'EventFileWatcherUpdated', 'EventIdeInstalled', 'EventInstallationUpdated', 'EventLspClientDiagnostics', 'EventMessagePartRemoved', 'EventMessagePartUpdated', 'EventMessageRemoved', 'EventMessageUpdated', 'EventPermissionReplied', 'EventPermissionUpdated', 'EventServerConnected', 'EventSessionCompacted', 'EventSessionDeleted', 'EventSessionError', 'EventSessionIdle', 'EventSessionUpdated']
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,160 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.file import File
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/file/status",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[list["File"]]:
if response.status_code == 200:
response_200 = []
_response_200 = response.json()
for response_200_item_data in _response_200:
response_200_item = File.from_dict(response_200_item_data)
response_200.append(response_200_item)
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[list["File"]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["File"]]:
"""Get file status
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['File']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["File"]]:
"""Get file status
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['File']
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["File"]]:
"""Get file status
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['File']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["File"]]:
"""Get file status
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['File']
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,155 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.path import Path
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/path",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Path]:
if response.status_code == 200:
response_200 = Path.from_dict(response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Path]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Path]:
"""Get the current path
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Path]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Path]:
"""Get the current path
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Path
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Path]:
"""Get the current path
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Path]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Path]:
"""Get the current path
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Path
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,155 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.project import Project
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/project/current",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Project]:
if response.status_code == 200:
response_200 = Project.from_dict(response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Project]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Project]:
"""Get the current project
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Project]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Project]:
"""Get the current project
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Project
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Project]:
"""Get the current project
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Project]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Project]:
"""Get the current project
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Project
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,164 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.project import Project
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/project",
"params": params,
}
return _kwargs
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[list["Project"]]:
if response.status_code == 200:
response_200 = []
_response_200 = response.json()
for response_200_item_data in _response_200:
response_200_item = Project.from_dict(response_200_item_data)
response_200.append(response_200_item)
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[list["Project"]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Project"]]:
"""List all projects
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Project']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Project"]]:
"""List all projects
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Project']
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Project"]]:
"""List all projects
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Project']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Project"]]:
"""List all projects
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Project']
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,164 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.session import Session
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/session",
"params": params,
}
return _kwargs
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[list["Session"]]:
if response.status_code == 200:
response_200 = []
_response_200 = response.json()
for response_200_item_data in _response_200:
response_200_item = Session.from_dict(response_200_item_data)
response_200.append(response_200_item)
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[list["Session"]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Session"]]:
"""List all sessions
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Session']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Session"]]:
"""List all sessions
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Session']
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[list["Session"]]:
"""List all sessions
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[list['Session']]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[list["Session"]]:
"""List all sessions
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
list['Session']
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,164 @@
from http import HTTPStatus
from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.error import Error
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "get",
"url": "/experimental/tool/ids",
"params": params,
}
return _kwargs
def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[Error, list[str]]]:
if response.status_code == 200:
response_200 = cast(list[str], response.json())
return response_200
if response.status_code == 400:
response_400 = Error.from_dict(response.json())
return response_400
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[Union[Error, list[str]]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Union[Error, list[str]]]:
"""List all tool IDs (including built-in and dynamically registered)
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union[Error, list[str]]]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Union[Error, list[str]]]:
"""List all tool IDs (including built-in and dynamically registered)
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union[Error, list[str]]
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[Union[Error, list[str]]]:
"""List all tool IDs (including built-in and dynamically registered)
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union[Error, list[str]]]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[Union[Error, list[str]]]:
"""List all tool IDs (including built-in and dynamically registered)
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union[Error, list[str]]
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,153 @@
from http import HTTPStatus
from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "post",
"url": "/tui/clear-prompt",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[bool]:
if response.status_code == 200:
response_200 = cast(bool, response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[bool]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Clear the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Clear the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Clear the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Clear the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,153 @@
from http import HTTPStatus
from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "post",
"url": "/tui/open-help",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[bool]:
if response.status_code == 200:
response_200 = cast(bool, response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[bool]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the help dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the help dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the help dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the help dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,153 @@
from http import HTTPStatus
from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "post",
"url": "/tui/open-models",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[bool]:
if response.status_code == 200:
response_200 = cast(bool, response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[bool]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the model dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the model dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the model dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the model dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,153 @@
from http import HTTPStatus
from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "post",
"url": "/tui/open-sessions",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[bool]:
if response.status_code == 200:
response_200 = cast(bool, response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[bool]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the session dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the session dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the session dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the session dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,153 @@
from http import HTTPStatus
from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "post",
"url": "/tui/open-themes",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[bool]:
if response.status_code == 200:
response_200 = cast(bool, response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[bool]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the theme dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the theme dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Open the theme dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Open the theme dialog
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,153 @@
from http import HTTPStatus
from typing import Any, Optional, Union, cast
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...types import UNSET, Response, Unset
def _get_kwargs(
*,
directory: Union[Unset, str] = UNSET,
) -> dict[str, Any]:
params: dict[str, Any] = {}
params["directory"] = directory
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
_kwargs: dict[str, Any] = {
"method": "post",
"url": "/tui/submit-prompt",
"params": params,
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[bool]:
if response.status_code == 200:
response_200 = cast(bool, response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[bool]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Submit the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Submit the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return sync_detailed(
client=client,
directory=directory,
).parsed
async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Response[bool]:
"""Submit the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[bool]
"""
kwargs = _get_kwargs(
directory=directory,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
directory: Union[Unset, str] = UNSET,
) -> Optional[bool]:
"""Submit the prompt
Args:
directory (Union[Unset, str]):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
bool
"""
return (
await asyncio_detailed(
client=client,
directory=directory,
)
).parsed

View file

@ -0,0 +1,268 @@
import ssl
from typing import Any, Optional, Union
import httpx
from attrs import define, evolve, field
@define
class Client:
"""A class for keeping track of data related to the API
The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
``base_url``: The base URL for the API, all requests are made to a relative path to this URL
``cookies``: A dictionary of cookies to be sent with every request
``headers``: A dictionary of headers to be sent with every request
``timeout``: The maximum amount of a time a request can take. API functions will raise
httpx.TimeoutException if this is exceeded.
``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
but can be set to False for testing purposes.
``follow_redirects``: Whether or not to follow redirects. Default value is False.
``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
Attributes:
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
argument to the constructor.
"""
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
_base_url: str = field(alias="base_url")
_cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
_headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout")
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl")
_follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
_httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
_client: Optional[httpx.Client] = field(default=None, init=False)
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
def with_headers(self, headers: dict[str, str]) -> "Client":
"""Get a new client matching this one with additional headers"""
if self._client is not None:
self._client.headers.update(headers)
if self._async_client is not None:
self._async_client.headers.update(headers)
return evolve(self, headers={**self._headers, **headers})
def with_cookies(self, cookies: dict[str, str]) -> "Client":
"""Get a new client matching this one with additional cookies"""
if self._client is not None:
self._client.cookies.update(cookies)
if self._async_client is not None:
self._async_client.cookies.update(cookies)
return evolve(self, cookies={**self._cookies, **cookies})
def with_timeout(self, timeout: httpx.Timeout) -> "Client":
"""Get a new client matching this one with a new timeout (in seconds)"""
if self._client is not None:
self._client.timeout = timeout
if self._async_client is not None:
self._async_client.timeout = timeout
return evolve(self, timeout=timeout)
def set_httpx_client(self, client: httpx.Client) -> "Client":
"""Manually set the underlying httpx.Client
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
"""
self._client = client
return self
def get_httpx_client(self) -> httpx.Client:
"""Get the underlying httpx.Client, constructing a new one if not previously set"""
if self._client is None:
self._client = httpx.Client(
base_url=self._base_url,
cookies=self._cookies,
headers=self._headers,
timeout=self._timeout,
verify=self._verify_ssl,
follow_redirects=self._follow_redirects,
**self._httpx_args,
)
return self._client
def __enter__(self) -> "Client":
"""Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
self.get_httpx_client().__enter__()
return self
def __exit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for internal httpx.Client (see httpx docs)"""
self.get_httpx_client().__exit__(*args, **kwargs)
def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client":
"""Manually the underlying httpx.AsyncClient
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
"""
self._async_client = async_client
return self
def get_async_httpx_client(self) -> httpx.AsyncClient:
"""Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
if self._async_client is None:
self._async_client = httpx.AsyncClient(
base_url=self._base_url,
cookies=self._cookies,
headers=self._headers,
timeout=self._timeout,
verify=self._verify_ssl,
follow_redirects=self._follow_redirects,
**self._httpx_args,
)
return self._async_client
async def __aenter__(self) -> "Client":
"""Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
await self.get_async_httpx_client().__aenter__()
return self
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
await self.get_async_httpx_client().__aexit__(*args, **kwargs)
@define
class AuthenticatedClient:
"""A Client which has been authenticated for use on secured endpoints
The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
``base_url``: The base URL for the API, all requests are made to a relative path to this URL
``cookies``: A dictionary of cookies to be sent with every request
``headers``: A dictionary of headers to be sent with every request
``timeout``: The maximum amount of a time a request can take. API functions will raise
httpx.TimeoutException if this is exceeded.
``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
but can be set to False for testing purposes.
``follow_redirects``: Whether or not to follow redirects. Default value is False.
``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
Attributes:
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
argument to the constructor.
token: The token to use for authentication
prefix: The prefix to use for the Authorization header
auth_header_name: The name of the Authorization header
"""
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
_base_url: str = field(alias="base_url")
_cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
_headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True, alias="timeout")
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True, alias="verify_ssl")
_follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
_httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
_client: Optional[httpx.Client] = field(default=None, init=False)
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
token: str
prefix: str = "Bearer"
auth_header_name: str = "Authorization"
def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient":
"""Get a new client matching this one with additional headers"""
if self._client is not None:
self._client.headers.update(headers)
if self._async_client is not None:
self._async_client.headers.update(headers)
return evolve(self, headers={**self._headers, **headers})
def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient":
"""Get a new client matching this one with additional cookies"""
if self._client is not None:
self._client.cookies.update(cookies)
if self._async_client is not None:
self._async_client.cookies.update(cookies)
return evolve(self, cookies={**self._cookies, **cookies})
def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient":
"""Get a new client matching this one with a new timeout (in seconds)"""
if self._client is not None:
self._client.timeout = timeout
if self._async_client is not None:
self._async_client.timeout = timeout
return evolve(self, timeout=timeout)
def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient":
"""Manually set the underlying httpx.Client
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
"""
self._client = client
return self
def get_httpx_client(self) -> httpx.Client:
"""Get the underlying httpx.Client, constructing a new one if not previously set"""
if self._client is None:
self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
self._client = httpx.Client(
base_url=self._base_url,
cookies=self._cookies,
headers=self._headers,
timeout=self._timeout,
verify=self._verify_ssl,
follow_redirects=self._follow_redirects,
**self._httpx_args,
)
return self._client
def __enter__(self) -> "AuthenticatedClient":
"""Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
self.get_httpx_client().__enter__()
return self
def __exit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for internal httpx.Client (see httpx docs)"""
self.get_httpx_client().__exit__(*args, **kwargs)
def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient":
"""Manually the underlying httpx.AsyncClient
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
"""
self._async_client = async_client
return self
def get_async_httpx_client(self) -> httpx.AsyncClient:
"""Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
if self._async_client is None:
self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
self._async_client = httpx.AsyncClient(
base_url=self._base_url,
cookies=self._cookies,
headers=self._headers,
timeout=self._timeout,
verify=self._verify_ssl,
follow_redirects=self._follow_redirects,
**self._httpx_args,
)
return self._async_client
async def __aenter__(self) -> "AuthenticatedClient":
"""Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
await self.get_async_httpx_client().__aenter__()
return self
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
await self.get_async_httpx_client().__aexit__(*args, **kwargs)

View file

@ -0,0 +1,16 @@
"""Contains shared errors types that can be raised from API functions"""
class UnexpectedStatus(Exception):
"""Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True"""
def __init__(self, status_code: int, content: bytes):
self.status_code = status_code
self.content = content
super().__init__(
f"Unexpected status code: {status_code}\n\nResponse content:\n{content.decode(errors='ignore')}"
)
__all__ = ["UnexpectedStatus"]

View file

@ -0,0 +1,186 @@
from __future__ import annotations
import time
from typing import AsyncIterator, Dict, Iterator, Optional
import httpx
from .api.default import (
app_agents,
command_list,
config_get,
config_providers,
file_status,
path_get,
project_current,
project_list,
session_list,
tool_ids,
)
from .client import Client
from .types import UNSET, Unset
class OpenCodeClient:
"""High-level convenience wrapper around the generated Client.
Provides sensible defaults and a couple of helper methods, with optional retries.
"""
def __init__(
self,
base_url: str = "http://localhost:4096",
*,
headers: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None,
verify_ssl: bool | str | httpx.URLTypes | None = True,
token: Optional[str] = None,
auth_header_name: str = "Authorization",
auth_prefix: str = "Bearer",
retries: int = 0,
backoff_factor: float = 0.5,
status_forcelist: tuple[int, ...] = (429, 500, 502, 503, 504),
) -> None:
httpx_timeout = None if timeout is None else httpx.Timeout(timeout)
all_headers = dict(headers or {})
if token:
all_headers[auth_header_name] = f"{auth_prefix} {token}".strip()
self._client = Client(
base_url=base_url,
headers=all_headers,
timeout=httpx_timeout,
verify_ssl=verify_ssl if isinstance(verify_ssl, bool) else True,
)
self._retries = max(0, int(retries))
self._backoff = float(backoff_factor)
self._status_forcelist = set(status_forcelist)
@property
def client(self) -> Client:
return self._client
# ---- Internal retry helper ----
def _call_with_retries(self, fn, *args, **kwargs):
attempt = 0
while True:
try:
return fn(*args, **kwargs)
except httpx.RequestError:
pass
except httpx.HTTPStatusError as e:
if e.response is None or e.response.status_code not in self._status_forcelist:
raise
if attempt >= self._retries:
# re-raise last exception if we have one
raise
sleep = self._backoff * (2**attempt)
time.sleep(sleep)
attempt += 1
# ---- Convenience wrappers over generated endpoints ----
def list_sessions(self, *, directory: str | Unset = UNSET):
"""Return sessions in the current project.
Wraps GET /session. Pass `directory` to target a specific project/directory if needed.
"""
return self._call_with_retries(session_list.sync, client=self._client, directory=directory)
def get_config(self, *, directory: str | Unset = UNSET):
"""Return opencode configuration for the current project (GET /config)."""
return self._call_with_retries(config_get.sync, client=self._client, directory=directory)
def list_agents(self, *, directory: str | Unset = UNSET):
"""List configured agents (GET /agent)."""
return self._call_with_retries(app_agents.sync, client=self._client, directory=directory)
def list_projects(self, *, directory: str | Unset = UNSET):
"""List known projects (GET /project)."""
return self._call_with_retries(project_list.sync, client=self._client, directory=directory)
def current_project(self, *, directory: str | Unset = UNSET):
"""Return current project (GET /project/current)."""
return self._call_with_retries(project_current.sync, client=self._client, directory=directory)
def file_status(self, *, directory: str | Unset = UNSET):
"""Return file status list (GET /file/status)."""
return self._call_with_retries(file_status.sync, client=self._client, directory=directory)
def get_path(self, *, directory: str | Unset = UNSET):
"""Return opencode path info (GET /path)."""
return self._call_with_retries(path_get.sync, client=self._client, directory=directory)
def config_providers(self, *, directory: str | Unset = UNSET):
"""Return configured providers (GET /config/providers)."""
return self._call_with_retries(config_providers.sync, client=self._client, directory=directory)
def tool_ids(self, *, directory: str | Unset = UNSET):
"""Return tool identifiers for a provider/model pair (GET /experimental/tool)."""
return self._call_with_retries(tool_ids.sync, client=self._client, directory=directory)
def list_commands(self, *, directory: str | Unset = UNSET):
"""List commands (GET /command)."""
return self._call_with_retries(command_list.sync, client=self._client, directory=directory)
# ---- Server-Sent Events (SSE) streaming ----
def subscribe_events(self, *, directory: str | Unset = UNSET) -> Iterator[dict]:
"""Subscribe to /event SSE endpoint and yield parsed JSON events.
This is a blocking generator which yields one event dict per message.
"""
client = self._client.get_httpx_client()
params: dict[str, str] = {}
if directory is not UNSET and directory is not None:
params["directory"] = str(directory)
with client.stream("GET", "/event", headers={"Accept": "text/event-stream"}, params=params) as r:
r.raise_for_status()
buf = ""
for line_bytes in r.iter_lines():
line = line_bytes.decode("utf-8") if isinstance(line_bytes, (bytes, bytearray)) else str(line_bytes)
if line.startswith(":"):
# comment/heartbeat
continue
if line == "":
if buf:
# end of event
for part in buf.split("\n"):
if part.startswith("data:"):
data = part[5:].strip()
if data:
try:
yield httpx._models.jsonlib.loads(data) # type: ignore[attr-defined]
except Exception:
# fall back: skip malformed
pass
buf = ""
continue
buf += line + "\n"
async def subscribe_events_async(self, *, directory: str | Unset = UNSET) -> AsyncIterator[dict]:
"""Async variant of subscribe_events using httpx.AsyncClient."""
aclient = self._client.get_async_httpx_client()
params: dict[str, str] = {}
if directory is not UNSET and directory is not None:
params["directory"] = str(directory)
async with aclient.stream("GET", "/event", headers={"Accept": "text/event-stream"}, params=params) as r:
r.raise_for_status()
buf = ""
async for line_bytes in r.aiter_lines():
line = line_bytes
if line.startswith(":"):
continue
if line == "":
if buf:
for part in buf.split("\n"):
if part.startswith("data:"):
data = part[5:].strip()
if data:
try:
yield httpx._models.jsonlib.loads(data) # type: ignore[attr-defined]
except Exception:
pass
buf = ""
continue
buf += line + "\n"

View file

@ -0,0 +1,367 @@
"""Contains all the data models used in inputs/outputs"""
from .agent import Agent
from .agent_config import AgentConfig
from .agent_config_permission import AgentConfigPermission
from .agent_config_permission_bash_type_1 import AgentConfigPermissionBashType1
from .agent_config_tools import AgentConfigTools
from .agent_model import AgentModel
from .agent_options import AgentOptions
from .agent_part import AgentPart
from .agent_part_input import AgentPartInput
from .agent_part_input_source import AgentPartInputSource
from .agent_part_source import AgentPartSource
from .agent_permission import AgentPermission
from .agent_permission_bash import AgentPermissionBash
from .agent_tools import AgentTools
from .api_auth import ApiAuth
from .assistant_message import AssistantMessage
from .assistant_message_path import AssistantMessagePath
from .assistant_message_time import AssistantMessageTime
from .assistant_message_tokens import AssistantMessageTokens
from .assistant_message_tokens_cache import AssistantMessageTokensCache
from .command import Command
from .config import Config
from .config_agent import ConfigAgent
from .config_command import ConfigCommand
from .config_command_additional_property import ConfigCommandAdditionalProperty
from .config_experimental import ConfigExperimental
from .config_experimental_hook import ConfigExperimentalHook
from .config_experimental_hook_file_edited import ConfigExperimentalHookFileEdited
from .config_experimental_hook_file_edited_additional_property_item import (
ConfigExperimentalHookFileEditedAdditionalPropertyItem,
)
from .config_experimental_hook_file_edited_additional_property_item_environment import (
ConfigExperimentalHookFileEditedAdditionalPropertyItemEnvironment,
)
from .config_experimental_hook_session_completed_item import ConfigExperimentalHookSessionCompletedItem
from .config_experimental_hook_session_completed_item_environment import (
ConfigExperimentalHookSessionCompletedItemEnvironment,
)
from .config_formatter import ConfigFormatter
from .config_formatter_additional_property import ConfigFormatterAdditionalProperty
from .config_formatter_additional_property_environment import ConfigFormatterAdditionalPropertyEnvironment
from .config_lsp import ConfigLsp
from .config_lsp_additional_property_type_0 import ConfigLspAdditionalPropertyType0
from .config_lsp_additional_property_type_1 import ConfigLspAdditionalPropertyType1
from .config_lsp_additional_property_type_1_env import ConfigLspAdditionalPropertyType1Env
from .config_lsp_additional_property_type_1_initialization import ConfigLspAdditionalPropertyType1Initialization
from .config_mcp import ConfigMcp
from .config_mode import ConfigMode
from .config_permission import ConfigPermission
from .config_permission_bash_type_1 import ConfigPermissionBashType1
from .config_provider import ConfigProvider
from .config_provider_additional_property import ConfigProviderAdditionalProperty
from .config_provider_additional_property_models import ConfigProviderAdditionalPropertyModels
from .config_provider_additional_property_models_additional_property import (
ConfigProviderAdditionalPropertyModelsAdditionalProperty,
)
from .config_provider_additional_property_models_additional_property_cost import (
ConfigProviderAdditionalPropertyModelsAdditionalPropertyCost,
)
from .config_provider_additional_property_models_additional_property_limit import (
ConfigProviderAdditionalPropertyModelsAdditionalPropertyLimit,
)
from .config_provider_additional_property_models_additional_property_options import (
ConfigProviderAdditionalPropertyModelsAdditionalPropertyOptions,
)
from .config_provider_additional_property_models_additional_property_provider import (
ConfigProviderAdditionalPropertyModelsAdditionalPropertyProvider,
)
from .config_provider_additional_property_options import ConfigProviderAdditionalPropertyOptions
from .config_providers_response_200 import ConfigProvidersResponse200
from .config_providers_response_200_default import ConfigProvidersResponse200Default
from .config_share import ConfigShare
from .config_tools import ConfigTools
from .config_tui import ConfigTui
from .config_watcher import ConfigWatcher
from .error import Error
from .error_data import ErrorData
from .event_file_edited import EventFileEdited
from .event_file_edited_properties import EventFileEditedProperties
from .event_file_watcher_updated import EventFileWatcherUpdated
from .event_file_watcher_updated_properties import EventFileWatcherUpdatedProperties
from .event_ide_installed import EventIdeInstalled
from .event_ide_installed_properties import EventIdeInstalledProperties
from .event_installation_updated import EventInstallationUpdated
from .event_installation_updated_properties import EventInstallationUpdatedProperties
from .event_lsp_client_diagnostics import EventLspClientDiagnostics
from .event_lsp_client_diagnostics_properties import EventLspClientDiagnosticsProperties
from .event_message_part_removed import EventMessagePartRemoved
from .event_message_part_removed_properties import EventMessagePartRemovedProperties
from .event_message_part_updated import EventMessagePartUpdated
from .event_message_part_updated_properties import EventMessagePartUpdatedProperties
from .event_message_removed import EventMessageRemoved
from .event_message_removed_properties import EventMessageRemovedProperties
from .event_message_updated import EventMessageUpdated
from .event_message_updated_properties import EventMessageUpdatedProperties
from .event_permission_replied import EventPermissionReplied
from .event_permission_replied_properties import EventPermissionRepliedProperties
from .event_permission_updated import EventPermissionUpdated
from .event_server_connected import EventServerConnected
from .event_server_connected_properties import EventServerConnectedProperties
from .event_session_compacted import EventSessionCompacted
from .event_session_compacted_properties import EventSessionCompactedProperties
from .event_session_deleted import EventSessionDeleted
from .event_session_deleted_properties import EventSessionDeletedProperties
from .event_session_error import EventSessionError
from .event_session_error_properties import EventSessionErrorProperties
from .event_session_idle import EventSessionIdle
from .event_session_idle_properties import EventSessionIdleProperties
from .event_session_updated import EventSessionUpdated
from .event_session_updated_properties import EventSessionUpdatedProperties
from .file import File
from .file_content import FileContent
from .file_content_patch import FileContentPatch
from .file_content_patch_hunks_item import FileContentPatchHunksItem
from .file_node import FileNode
from .file_node_type import FileNodeType
from .file_part import FilePart
from .file_part_input import FilePartInput
from .file_part_source_text import FilePartSourceText
from .file_source import FileSource
from .file_status import FileStatus
from .keybinds_config import KeybindsConfig
from .layout_config import LayoutConfig
from .mcp_local_config import McpLocalConfig
from .mcp_local_config_environment import McpLocalConfigEnvironment
from .mcp_remote_config import McpRemoteConfig
from .mcp_remote_config_headers import McpRemoteConfigHeaders
from .message_aborted_error import MessageAbortedError
from .message_aborted_error_data import MessageAbortedErrorData
from .message_output_length_error import MessageOutputLengthError
from .message_output_length_error_data import MessageOutputLengthErrorData
from .model import Model
from .model_cost import ModelCost
from .model_limit import ModelLimit
from .model_options import ModelOptions
from .model_provider import ModelProvider
from .o_auth import OAuth
from .patch_part import PatchPart
from .path import Path
from .permission import Permission
from .permission_metadata import PermissionMetadata
from .permission_time import PermissionTime
from .project import Project
from .project_time import ProjectTime
from .provider import Provider
from .provider_auth_error import ProviderAuthError
from .provider_auth_error_data import ProviderAuthErrorData
from .provider_models import ProviderModels
from .range_ import Range
from .range_end import RangeEnd
from .range_start import RangeStart
from .reasoning_part import ReasoningPart
from .reasoning_part_metadata import ReasoningPartMetadata
from .reasoning_part_time import ReasoningPartTime
from .session import Session
from .session_revert import SessionRevert
from .session_share import SessionShare
from .session_time import SessionTime
from .snapshot_part import SnapshotPart
from .step_finish_part import StepFinishPart
from .step_finish_part_tokens import StepFinishPartTokens
from .step_finish_part_tokens_cache import StepFinishPartTokensCache
from .step_start_part import StepStartPart
from .symbol import Symbol
from .symbol_location import SymbolLocation
from .symbol_source import SymbolSource
from .text_part import TextPart
from .text_part_input import TextPartInput
from .text_part_input_time import TextPartInputTime
from .text_part_time import TextPartTime
from .tool_list_item import ToolListItem
from .tool_part import ToolPart
from .tool_state_completed import ToolStateCompleted
from .tool_state_completed_input import ToolStateCompletedInput
from .tool_state_completed_metadata import ToolStateCompletedMetadata
from .tool_state_completed_time import ToolStateCompletedTime
from .tool_state_error import ToolStateError
from .tool_state_error_input import ToolStateErrorInput
from .tool_state_error_metadata import ToolStateErrorMetadata
from .tool_state_error_time import ToolStateErrorTime
from .tool_state_pending import ToolStatePending
from .tool_state_running import ToolStateRunning
from .tool_state_running_metadata import ToolStateRunningMetadata
from .tool_state_running_time import ToolStateRunningTime
from .unknown_error import UnknownError
from .unknown_error_data import UnknownErrorData
from .user_message import UserMessage
from .user_message_time import UserMessageTime
from .well_known_auth import WellKnownAuth
__all__ = (
"Agent",
"AgentConfig",
"AgentConfigPermission",
"AgentConfigPermissionBashType1",
"AgentConfigTools",
"AgentModel",
"AgentOptions",
"AgentPart",
"AgentPartInput",
"AgentPartInputSource",
"AgentPartSource",
"AgentPermission",
"AgentPermissionBash",
"AgentTools",
"ApiAuth",
"AssistantMessage",
"AssistantMessagePath",
"AssistantMessageTime",
"AssistantMessageTokens",
"AssistantMessageTokensCache",
"Command",
"Config",
"ConfigAgent",
"ConfigCommand",
"ConfigCommandAdditionalProperty",
"ConfigExperimental",
"ConfigExperimentalHook",
"ConfigExperimentalHookFileEdited",
"ConfigExperimentalHookFileEditedAdditionalPropertyItem",
"ConfigExperimentalHookFileEditedAdditionalPropertyItemEnvironment",
"ConfigExperimentalHookSessionCompletedItem",
"ConfigExperimentalHookSessionCompletedItemEnvironment",
"ConfigFormatter",
"ConfigFormatterAdditionalProperty",
"ConfigFormatterAdditionalPropertyEnvironment",
"ConfigLsp",
"ConfigLspAdditionalPropertyType0",
"ConfigLspAdditionalPropertyType1",
"ConfigLspAdditionalPropertyType1Env",
"ConfigLspAdditionalPropertyType1Initialization",
"ConfigMcp",
"ConfigMode",
"ConfigPermission",
"ConfigPermissionBashType1",
"ConfigProvider",
"ConfigProviderAdditionalProperty",
"ConfigProviderAdditionalPropertyModels",
"ConfigProviderAdditionalPropertyModelsAdditionalProperty",
"ConfigProviderAdditionalPropertyModelsAdditionalPropertyCost",
"ConfigProviderAdditionalPropertyModelsAdditionalPropertyLimit",
"ConfigProviderAdditionalPropertyModelsAdditionalPropertyOptions",
"ConfigProviderAdditionalPropertyModelsAdditionalPropertyProvider",
"ConfigProviderAdditionalPropertyOptions",
"ConfigProvidersResponse200",
"ConfigProvidersResponse200Default",
"ConfigShare",
"ConfigTools",
"ConfigTui",
"ConfigWatcher",
"Error",
"ErrorData",
"EventFileEdited",
"EventFileEditedProperties",
"EventFileWatcherUpdated",
"EventFileWatcherUpdatedProperties",
"EventIdeInstalled",
"EventIdeInstalledProperties",
"EventInstallationUpdated",
"EventInstallationUpdatedProperties",
"EventLspClientDiagnostics",
"EventLspClientDiagnosticsProperties",
"EventMessagePartRemoved",
"EventMessagePartRemovedProperties",
"EventMessagePartUpdated",
"EventMessagePartUpdatedProperties",
"EventMessageRemoved",
"EventMessageRemovedProperties",
"EventMessageUpdated",
"EventMessageUpdatedProperties",
"EventPermissionReplied",
"EventPermissionRepliedProperties",
"EventPermissionUpdated",
"EventServerConnected",
"EventServerConnectedProperties",
"EventSessionCompacted",
"EventSessionCompactedProperties",
"EventSessionDeleted",
"EventSessionDeletedProperties",
"EventSessionError",
"EventSessionErrorProperties",
"EventSessionIdle",
"EventSessionIdleProperties",
"EventSessionUpdated",
"EventSessionUpdatedProperties",
"File",
"FileContent",
"FileContentPatch",
"FileContentPatchHunksItem",
"FileNode",
"FileNodeType",
"FilePart",
"FilePartInput",
"FilePartSourceText",
"FileSource",
"FileStatus",
"KeybindsConfig",
"LayoutConfig",
"McpLocalConfig",
"McpLocalConfigEnvironment",
"McpRemoteConfig",
"McpRemoteConfigHeaders",
"MessageAbortedError",
"MessageAbortedErrorData",
"MessageOutputLengthError",
"MessageOutputLengthErrorData",
"Model",
"ModelCost",
"ModelLimit",
"ModelOptions",
"ModelProvider",
"OAuth",
"PatchPart",
"Path",
"Permission",
"PermissionMetadata",
"PermissionTime",
"Project",
"ProjectTime",
"Provider",
"ProviderAuthError",
"ProviderAuthErrorData",
"ProviderModels",
"Range",
"RangeEnd",
"RangeStart",
"ReasoningPart",
"ReasoningPartMetadata",
"ReasoningPartTime",
"Session",
"SessionRevert",
"SessionShare",
"SessionTime",
"SnapshotPart",
"StepFinishPart",
"StepFinishPartTokens",
"StepFinishPartTokensCache",
"StepStartPart",
"Symbol",
"SymbolLocation",
"SymbolSource",
"TextPart",
"TextPartInput",
"TextPartInputTime",
"TextPartTime",
"ToolListItem",
"ToolPart",
"ToolStateCompleted",
"ToolStateCompletedInput",
"ToolStateCompletedMetadata",
"ToolStateCompletedTime",
"ToolStateError",
"ToolStateErrorInput",
"ToolStateErrorMetadata",
"ToolStateErrorTime",
"ToolStatePending",
"ToolStateRunning",
"ToolStateRunningMetadata",
"ToolStateRunningTime",
"UnknownError",
"UnknownErrorData",
"UserMessage",
"UserMessageTime",
"WellKnownAuth",
)

View file

@ -0,0 +1,180 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
from ..types import UNSET, Unset
if TYPE_CHECKING:
from ..models.agent_model import AgentModel
from ..models.agent_options import AgentOptions
from ..models.agent_permission import AgentPermission
from ..models.agent_tools import AgentTools
T = TypeVar("T", bound="Agent")
@_attrs_define
class Agent:
"""
Attributes:
name (str):
mode (Union[Literal['all'], Literal['primary'], Literal['subagent']]):
built_in (bool):
permission (AgentPermission):
tools (AgentTools):
options (AgentOptions):
description (Union[Unset, str]):
top_p (Union[Unset, float]):
temperature (Union[Unset, float]):
model (Union[Unset, AgentModel]):
prompt (Union[Unset, str]):
"""
name: str
mode: Union[Literal["all"], Literal["primary"], Literal["subagent"]]
built_in: bool
permission: "AgentPermission"
tools: "AgentTools"
options: "AgentOptions"
description: Union[Unset, str] = UNSET
top_p: Union[Unset, float] = UNSET
temperature: Union[Unset, float] = UNSET
model: Union[Unset, "AgentModel"] = UNSET
prompt: Union[Unset, str] = UNSET
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
name = self.name
mode: Union[Literal["all"], Literal["primary"], Literal["subagent"]]
mode = self.mode
built_in = self.built_in
permission = self.permission.to_dict()
tools = self.tools.to_dict()
options = self.options.to_dict()
description = self.description
top_p = self.top_p
temperature = self.temperature
model: Union[Unset, dict[str, Any]] = UNSET
if not isinstance(self.model, Unset):
model = self.model.to_dict()
prompt = self.prompt
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"name": name,
"mode": mode,
"builtIn": built_in,
"permission": permission,
"tools": tools,
"options": options,
}
)
if description is not UNSET:
field_dict["description"] = description
if top_p is not UNSET:
field_dict["topP"] = top_p
if temperature is not UNSET:
field_dict["temperature"] = temperature
if model is not UNSET:
field_dict["model"] = model
if prompt is not UNSET:
field_dict["prompt"] = prompt
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.agent_model import AgentModel
from ..models.agent_options import AgentOptions
from ..models.agent_permission import AgentPermission
from ..models.agent_tools import AgentTools
d = dict(src_dict)
name = d.pop("name")
def _parse_mode(data: object) -> Union[Literal["all"], Literal["primary"], Literal["subagent"]]:
mode_type_0 = cast(Literal["subagent"], data)
if mode_type_0 != "subagent":
raise ValueError(f"mode_type_0 must match const 'subagent', got '{mode_type_0}'")
return mode_type_0
mode_type_1 = cast(Literal["primary"], data)
if mode_type_1 != "primary":
raise ValueError(f"mode_type_1 must match const 'primary', got '{mode_type_1}'")
return mode_type_1
mode_type_2 = cast(Literal["all"], data)
if mode_type_2 != "all":
raise ValueError(f"mode_type_2 must match const 'all', got '{mode_type_2}'")
return mode_type_2
mode = _parse_mode(d.pop("mode"))
built_in = d.pop("builtIn")
permission = AgentPermission.from_dict(d.pop("permission"))
tools = AgentTools.from_dict(d.pop("tools"))
options = AgentOptions.from_dict(d.pop("options"))
description = d.pop("description", UNSET)
top_p = d.pop("topP", UNSET)
temperature = d.pop("temperature", UNSET)
_model = d.pop("model", UNSET)
model: Union[Unset, AgentModel]
if isinstance(_model, Unset):
model = UNSET
else:
model = AgentModel.from_dict(_model)
prompt = d.pop("prompt", UNSET)
agent = cls(
name=name,
mode=mode,
built_in=built_in,
permission=permission,
tools=tools,
options=options,
description=description,
top_p=top_p,
temperature=temperature,
model=model,
prompt=prompt,
)
agent.additional_properties = d
return agent
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,173 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
from ..types import UNSET, Unset
if TYPE_CHECKING:
from ..models.agent_config_permission import AgentConfigPermission
from ..models.agent_config_tools import AgentConfigTools
T = TypeVar("T", bound="AgentConfig")
@_attrs_define
class AgentConfig:
"""
Attributes:
model (Union[Unset, str]):
temperature (Union[Unset, float]):
top_p (Union[Unset, float]):
prompt (Union[Unset, str]):
tools (Union[Unset, AgentConfigTools]):
disable (Union[Unset, bool]):
description (Union[Unset, str]): Description of when to use the agent
mode (Union[Literal['all'], Literal['primary'], Literal['subagent'], Unset]):
permission (Union[Unset, AgentConfigPermission]):
"""
model: Union[Unset, str] = UNSET
temperature: Union[Unset, float] = UNSET
top_p: Union[Unset, float] = UNSET
prompt: Union[Unset, str] = UNSET
tools: Union[Unset, "AgentConfigTools"] = UNSET
disable: Union[Unset, bool] = UNSET
description: Union[Unset, str] = UNSET
mode: Union[Literal["all"], Literal["primary"], Literal["subagent"], Unset] = UNSET
permission: Union[Unset, "AgentConfigPermission"] = UNSET
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
model = self.model
temperature = self.temperature
top_p = self.top_p
prompt = self.prompt
tools: Union[Unset, dict[str, Any]] = UNSET
if not isinstance(self.tools, Unset):
tools = self.tools.to_dict()
disable = self.disable
description = self.description
mode: Union[Literal["all"], Literal["primary"], Literal["subagent"], Unset]
if isinstance(self.mode, Unset):
mode = UNSET
else:
mode = self.mode
permission: Union[Unset, dict[str, Any]] = UNSET
if not isinstance(self.permission, Unset):
permission = self.permission.to_dict()
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
if model is not UNSET:
field_dict["model"] = model
if temperature is not UNSET:
field_dict["temperature"] = temperature
if top_p is not UNSET:
field_dict["top_p"] = top_p
if prompt is not UNSET:
field_dict["prompt"] = prompt
if tools is not UNSET:
field_dict["tools"] = tools
if disable is not UNSET:
field_dict["disable"] = disable
if description is not UNSET:
field_dict["description"] = description
if mode is not UNSET:
field_dict["mode"] = mode
if permission is not UNSET:
field_dict["permission"] = permission
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.agent_config_permission import AgentConfigPermission
from ..models.agent_config_tools import AgentConfigTools
d = dict(src_dict)
model = d.pop("model", UNSET)
temperature = d.pop("temperature", UNSET)
top_p = d.pop("top_p", UNSET)
prompt = d.pop("prompt", UNSET)
_tools = d.pop("tools", UNSET)
tools: Union[Unset, AgentConfigTools]
if isinstance(_tools, Unset):
tools = UNSET
else:
tools = AgentConfigTools.from_dict(_tools)
disable = d.pop("disable", UNSET)
description = d.pop("description", UNSET)
def _parse_mode(data: object) -> Union[Literal["all"], Literal["primary"], Literal["subagent"], Unset]:
if isinstance(data, Unset):
return data
mode_type_0 = cast(Literal["subagent"], data)
if mode_type_0 != "subagent":
raise ValueError(f"mode_type_0 must match const 'subagent', got '{mode_type_0}'")
return mode_type_0
mode_type_1 = cast(Literal["primary"], data)
if mode_type_1 != "primary":
raise ValueError(f"mode_type_1 must match const 'primary', got '{mode_type_1}'")
return mode_type_1
mode_type_2 = cast(Literal["all"], data)
if mode_type_2 != "all":
raise ValueError(f"mode_type_2 must match const 'all', got '{mode_type_2}'")
return mode_type_2
mode = _parse_mode(d.pop("mode", UNSET))
_permission = d.pop("permission", UNSET)
permission: Union[Unset, AgentConfigPermission]
if isinstance(_permission, Unset):
permission = UNSET
else:
permission = AgentConfigPermission.from_dict(_permission)
agent_config = cls(
model=model,
temperature=temperature,
top_p=top_p,
prompt=prompt,
tools=tools,
disable=disable,
description=description,
mode=mode,
permission=permission,
)
agent_config.additional_properties = d
return agent_config
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,155 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
from ..types import UNSET, Unset
if TYPE_CHECKING:
from ..models.agent_config_permission_bash_type_1 import AgentConfigPermissionBashType1
T = TypeVar("T", bound="AgentConfigPermission")
@_attrs_define
class AgentConfigPermission:
"""
Attributes:
edit (Union[Literal['allow'], Literal['ask'], Literal['deny'], Unset]):
bash (Union['AgentConfigPermissionBashType1', Literal['allow'], Literal['ask'], Literal['deny'], Unset]):
webfetch (Union[Literal['allow'], Literal['ask'], Literal['deny'], Unset]):
"""
edit: Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset] = UNSET
bash: Union["AgentConfigPermissionBashType1", Literal["allow"], Literal["ask"], Literal["deny"], Unset] = UNSET
webfetch: Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset] = UNSET
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
from ..models.agent_config_permission_bash_type_1 import AgentConfigPermissionBashType1
edit: Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset]
if isinstance(self.edit, Unset):
edit = UNSET
else:
edit = self.edit
bash: Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset, dict[str, Any]]
if isinstance(self.bash, Unset):
bash = UNSET
elif isinstance(self.bash, AgentConfigPermissionBashType1):
bash = self.bash.to_dict()
else:
bash = self.bash
webfetch: Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset]
if isinstance(self.webfetch, Unset):
webfetch = UNSET
else:
webfetch = self.webfetch
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
if edit is not UNSET:
field_dict["edit"] = edit
if bash is not UNSET:
field_dict["bash"] = bash
if webfetch is not UNSET:
field_dict["webfetch"] = webfetch
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.agent_config_permission_bash_type_1 import AgentConfigPermissionBashType1
d = dict(src_dict)
def _parse_edit(data: object) -> Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset]:
if isinstance(data, Unset):
return data
edit_type_0 = cast(Literal["ask"], data)
if edit_type_0 != "ask":
raise ValueError(f"edit_type_0 must match const 'ask', got '{edit_type_0}'")
return edit_type_0
edit_type_1 = cast(Literal["allow"], data)
if edit_type_1 != "allow":
raise ValueError(f"edit_type_1 must match const 'allow', got '{edit_type_1}'")
return edit_type_1
edit_type_2 = cast(Literal["deny"], data)
if edit_type_2 != "deny":
raise ValueError(f"edit_type_2 must match const 'deny', got '{edit_type_2}'")
return edit_type_2
edit = _parse_edit(d.pop("edit", UNSET))
def _parse_bash(
data: object,
) -> Union["AgentConfigPermissionBashType1", Literal["allow"], Literal["ask"], Literal["deny"], Unset]:
if isinstance(data, Unset):
return data
bash_type_0_type_0 = cast(Literal["ask"], data)
if bash_type_0_type_0 != "ask":
raise ValueError(f"bash_type_0_type_0 must match const 'ask', got '{bash_type_0_type_0}'")
return bash_type_0_type_0
bash_type_0_type_1 = cast(Literal["allow"], data)
if bash_type_0_type_1 != "allow":
raise ValueError(f"bash_type_0_type_1 must match const 'allow', got '{bash_type_0_type_1}'")
return bash_type_0_type_1
bash_type_0_type_2 = cast(Literal["deny"], data)
if bash_type_0_type_2 != "deny":
raise ValueError(f"bash_type_0_type_2 must match const 'deny', got '{bash_type_0_type_2}'")
return bash_type_0_type_2
if not isinstance(data, dict):
raise TypeError()
bash_type_1 = AgentConfigPermissionBashType1.from_dict(data)
return bash_type_1
bash = _parse_bash(d.pop("bash", UNSET))
def _parse_webfetch(data: object) -> Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset]:
if isinstance(data, Unset):
return data
webfetch_type_0 = cast(Literal["ask"], data)
if webfetch_type_0 != "ask":
raise ValueError(f"webfetch_type_0 must match const 'ask', got '{webfetch_type_0}'")
return webfetch_type_0
webfetch_type_1 = cast(Literal["allow"], data)
if webfetch_type_1 != "allow":
raise ValueError(f"webfetch_type_1 must match const 'allow', got '{webfetch_type_1}'")
return webfetch_type_1
webfetch_type_2 = cast(Literal["deny"], data)
if webfetch_type_2 != "deny":
raise ValueError(f"webfetch_type_2 must match const 'deny', got '{webfetch_type_2}'")
return webfetch_type_2
webfetch = _parse_webfetch(d.pop("webfetch", UNSET))
agent_config_permission = cls(
edit=edit,
bash=bash,
webfetch=webfetch,
)
agent_config_permission.additional_properties = d
return agent_config_permission
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,74 @@
from collections.abc import Mapping
from typing import Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentConfigPermissionBashType1")
@_attrs_define
class AgentConfigPermissionBashType1:
""" """
additional_properties: dict[str, Union[Literal["allow"], Literal["ask"], Literal["deny"]]] = _attrs_field(
init=False, factory=dict
)
def to_dict(self) -> dict[str, Any]:
field_dict: dict[str, Any] = {}
for prop_name, prop in self.additional_properties.items():
field_dict[prop_name] = prop
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
agent_config_permission_bash_type_1 = cls()
additional_properties = {}
for prop_name, prop_dict in d.items():
def _parse_additional_property(data: object) -> Union[Literal["allow"], Literal["ask"], Literal["deny"]]:
additional_property_type_0 = cast(Literal["ask"], data)
if additional_property_type_0 != "ask":
raise ValueError(
f"AdditionalProperty_type_0 must match const 'ask', got '{additional_property_type_0}'"
)
return additional_property_type_0
additional_property_type_1 = cast(Literal["allow"], data)
if additional_property_type_1 != "allow":
raise ValueError(
f"AdditionalProperty_type_1 must match const 'allow', got '{additional_property_type_1}'"
)
return additional_property_type_1
additional_property_type_2 = cast(Literal["deny"], data)
if additional_property_type_2 != "deny":
raise ValueError(
f"AdditionalProperty_type_2 must match const 'deny', got '{additional_property_type_2}'"
)
return additional_property_type_2
additional_property = _parse_additional_property(prop_dict)
additional_properties[prop_name] = additional_property
agent_config_permission_bash_type_1.additional_properties = additional_properties
return agent_config_permission_bash_type_1
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Union[Literal["allow"], Literal["ask"], Literal["deny"]]:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Union[Literal["allow"], Literal["ask"], Literal["deny"]]) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,44 @@
from collections.abc import Mapping
from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentConfigTools")
@_attrs_define
class AgentConfigTools:
""" """
additional_properties: dict[str, bool] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
agent_config_tools = cls()
agent_config_tools.additional_properties = d
return agent_config_tools
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> bool:
return self.additional_properties[key]
def __setitem__(self, key: str, value: bool) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,67 @@
from collections.abc import Mapping
from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentModel")
@_attrs_define
class AgentModel:
"""
Attributes:
model_id (str):
provider_id (str):
"""
model_id: str
provider_id: str
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
model_id = self.model_id
provider_id = self.provider_id
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"modelID": model_id,
"providerID": provider_id,
}
)
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
model_id = d.pop("modelID")
provider_id = d.pop("providerID")
agent_model = cls(
model_id=model_id,
provider_id=provider_id,
)
agent_model.additional_properties = d
return agent_model
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,44 @@
from collections.abc import Mapping
from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentOptions")
@_attrs_define
class AgentOptions:
""" """
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
agent_options = cls()
agent_options.additional_properties = d
return agent_options
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,117 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
from ..types import UNSET, Unset
if TYPE_CHECKING:
from ..models.agent_part_source import AgentPartSource
T = TypeVar("T", bound="AgentPart")
@_attrs_define
class AgentPart:
"""
Attributes:
id (str):
session_id (str):
message_id (str):
type_ (Literal['agent']):
name (str):
source (Union[Unset, AgentPartSource]):
"""
id: str
session_id: str
message_id: str
type_: Literal["agent"]
name: str
source: Union[Unset, "AgentPartSource"] = UNSET
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
id = self.id
session_id = self.session_id
message_id = self.message_id
type_ = self.type_
name = self.name
source: Union[Unset, dict[str, Any]] = UNSET
if not isinstance(self.source, Unset):
source = self.source.to_dict()
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"id": id,
"sessionID": session_id,
"messageID": message_id,
"type": type_,
"name": name,
}
)
if source is not UNSET:
field_dict["source"] = source
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.agent_part_source import AgentPartSource
d = dict(src_dict)
id = d.pop("id")
session_id = d.pop("sessionID")
message_id = d.pop("messageID")
type_ = cast(Literal["agent"], d.pop("type"))
if type_ != "agent":
raise ValueError(f"type must match const 'agent', got '{type_}'")
name = d.pop("name")
_source = d.pop("source", UNSET)
source: Union[Unset, AgentPartSource]
if isinstance(_source, Unset):
source = UNSET
else:
source = AgentPartSource.from_dict(_source)
agent_part = cls(
id=id,
session_id=session_id,
message_id=message_id,
type_=type_,
name=name,
source=source,
)
agent_part.additional_properties = d
return agent_part
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,102 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
from ..types import UNSET, Unset
if TYPE_CHECKING:
from ..models.agent_part_input_source import AgentPartInputSource
T = TypeVar("T", bound="AgentPartInput")
@_attrs_define
class AgentPartInput:
"""
Attributes:
type_ (Literal['agent']):
name (str):
id (Union[Unset, str]):
source (Union[Unset, AgentPartInputSource]):
"""
type_: Literal["agent"]
name: str
id: Union[Unset, str] = UNSET
source: Union[Unset, "AgentPartInputSource"] = UNSET
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
type_ = self.type_
name = self.name
id = self.id
source: Union[Unset, dict[str, Any]] = UNSET
if not isinstance(self.source, Unset):
source = self.source.to_dict()
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"type": type_,
"name": name,
}
)
if id is not UNSET:
field_dict["id"] = id
if source is not UNSET:
field_dict["source"] = source
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.agent_part_input_source import AgentPartInputSource
d = dict(src_dict)
type_ = cast(Literal["agent"], d.pop("type"))
if type_ != "agent":
raise ValueError(f"type must match const 'agent', got '{type_}'")
name = d.pop("name")
id = d.pop("id", UNSET)
_source = d.pop("source", UNSET)
source: Union[Unset, AgentPartInputSource]
if isinstance(_source, Unset):
source = UNSET
else:
source = AgentPartInputSource.from_dict(_source)
agent_part_input = cls(
type_=type_,
name=name,
id=id,
source=source,
)
agent_part_input.additional_properties = d
return agent_part_input
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,75 @@
from collections.abc import Mapping
from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentPartInputSource")
@_attrs_define
class AgentPartInputSource:
"""
Attributes:
value (str):
start (int):
end (int):
"""
value: str
start: int
end: int
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
value = self.value
start = self.start
end = self.end
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"value": value,
"start": start,
"end": end,
}
)
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
value = d.pop("value")
start = d.pop("start")
end = d.pop("end")
agent_part_input_source = cls(
value=value,
start=start,
end=end,
)
agent_part_input_source.additional_properties = d
return agent_part_input_source
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,75 @@
from collections.abc import Mapping
from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentPartSource")
@_attrs_define
class AgentPartSource:
"""
Attributes:
value (str):
start (int):
end (int):
"""
value: str
start: int
end: int
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
value = self.value
start = self.start
end = self.end
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"value": value,
"start": start,
"end": end,
}
)
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
value = d.pop("value")
start = d.pop("start")
end = d.pop("end")
agent_part_source = cls(
value=value,
start=start,
end=end,
)
agent_part_source.additional_properties = d
return agent_part_source
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,120 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
from ..types import UNSET, Unset
if TYPE_CHECKING:
from ..models.agent_permission_bash import AgentPermissionBash
T = TypeVar("T", bound="AgentPermission")
@_attrs_define
class AgentPermission:
"""
Attributes:
edit (Union[Literal['allow'], Literal['ask'], Literal['deny']]):
bash (AgentPermissionBash):
webfetch (Union[Literal['allow'], Literal['ask'], Literal['deny'], Unset]):
"""
edit: Union[Literal["allow"], Literal["ask"], Literal["deny"]]
bash: "AgentPermissionBash"
webfetch: Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset] = UNSET
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
edit: Union[Literal["allow"], Literal["ask"], Literal["deny"]]
edit = self.edit
bash = self.bash.to_dict()
webfetch: Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset]
if isinstance(self.webfetch, Unset):
webfetch = UNSET
else:
webfetch = self.webfetch
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"edit": edit,
"bash": bash,
}
)
if webfetch is not UNSET:
field_dict["webfetch"] = webfetch
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
from ..models.agent_permission_bash import AgentPermissionBash
d = dict(src_dict)
def _parse_edit(data: object) -> Union[Literal["allow"], Literal["ask"], Literal["deny"]]:
edit_type_0 = cast(Literal["ask"], data)
if edit_type_0 != "ask":
raise ValueError(f"edit_type_0 must match const 'ask', got '{edit_type_0}'")
return edit_type_0
edit_type_1 = cast(Literal["allow"], data)
if edit_type_1 != "allow":
raise ValueError(f"edit_type_1 must match const 'allow', got '{edit_type_1}'")
return edit_type_1
edit_type_2 = cast(Literal["deny"], data)
if edit_type_2 != "deny":
raise ValueError(f"edit_type_2 must match const 'deny', got '{edit_type_2}'")
return edit_type_2
edit = _parse_edit(d.pop("edit"))
bash = AgentPermissionBash.from_dict(d.pop("bash"))
def _parse_webfetch(data: object) -> Union[Literal["allow"], Literal["ask"], Literal["deny"], Unset]:
if isinstance(data, Unset):
return data
webfetch_type_0 = cast(Literal["ask"], data)
if webfetch_type_0 != "ask":
raise ValueError(f"webfetch_type_0 must match const 'ask', got '{webfetch_type_0}'")
return webfetch_type_0
webfetch_type_1 = cast(Literal["allow"], data)
if webfetch_type_1 != "allow":
raise ValueError(f"webfetch_type_1 must match const 'allow', got '{webfetch_type_1}'")
return webfetch_type_1
webfetch_type_2 = cast(Literal["deny"], data)
if webfetch_type_2 != "deny":
raise ValueError(f"webfetch_type_2 must match const 'deny', got '{webfetch_type_2}'")
return webfetch_type_2
webfetch = _parse_webfetch(d.pop("webfetch", UNSET))
agent_permission = cls(
edit=edit,
bash=bash,
webfetch=webfetch,
)
agent_permission.additional_properties = d
return agent_permission
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,74 @@
from collections.abc import Mapping
from typing import Any, Literal, TypeVar, Union, cast
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentPermissionBash")
@_attrs_define
class AgentPermissionBash:
""" """
additional_properties: dict[str, Union[Literal["allow"], Literal["ask"], Literal["deny"]]] = _attrs_field(
init=False, factory=dict
)
def to_dict(self) -> dict[str, Any]:
field_dict: dict[str, Any] = {}
for prop_name, prop in self.additional_properties.items():
field_dict[prop_name] = prop
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
agent_permission_bash = cls()
additional_properties = {}
for prop_name, prop_dict in d.items():
def _parse_additional_property(data: object) -> Union[Literal["allow"], Literal["ask"], Literal["deny"]]:
additional_property_type_0 = cast(Literal["ask"], data)
if additional_property_type_0 != "ask":
raise ValueError(
f"AdditionalProperty_type_0 must match const 'ask', got '{additional_property_type_0}'"
)
return additional_property_type_0
additional_property_type_1 = cast(Literal["allow"], data)
if additional_property_type_1 != "allow":
raise ValueError(
f"AdditionalProperty_type_1 must match const 'allow', got '{additional_property_type_1}'"
)
return additional_property_type_1
additional_property_type_2 = cast(Literal["deny"], data)
if additional_property_type_2 != "deny":
raise ValueError(
f"AdditionalProperty_type_2 must match const 'deny', got '{additional_property_type_2}'"
)
return additional_property_type_2
additional_property = _parse_additional_property(prop_dict)
additional_properties[prop_name] = additional_property
agent_permission_bash.additional_properties = additional_properties
return agent_permission_bash
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Union[Literal["allow"], Literal["ask"], Literal["deny"]]:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Union[Literal["allow"], Literal["ask"], Literal["deny"]]) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -0,0 +1,44 @@
from collections.abc import Mapping
from typing import Any, TypeVar
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="AgentTools")
@_attrs_define
class AgentTools:
""" """
additional_properties: dict[str, bool] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
agent_tools = cls()
agent_tools.additional_properties = d
return agent_tools
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> bool:
return self.additional_properties[key]
def __setitem__(self, key: str, value: bool) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

Some files were not shown because too many files have changed in this diff Show more