mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-03 16:56:33 -04:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5d89ca567 | |||
| 4ae70d4b0d | |||
| 935cd7481b | |||
| 5553efea5e | |||
| 0ff73ed8a6 | |||
| 5e792d7ac5 | |||
| 4a77e94e3c | |||
| 4c563ea405 | |||
| 5875257462 | |||
| 9701891e94 | |||
| a2ab37c1b6 | |||
| 4d6e2d8efc | |||
| 4407d5d96f | |||
| 244945c0e7 | |||
| c652b2b4e8 | |||
| aabeeb1431 | |||
| 0fbedc5e19 | |||
| 12782fff14 | |||
| ca463a2346 | |||
| 7265cdf817 | |||
| 7baa751351 | |||
| 5b86fa9109 | |||
| aa7e008fe1 | |||
| 792664071c | |||
| a0541ba57a | |||
| 4994bf1b46 | |||
| 1e24514d61 | |||
| 4b1c6300a0 | |||
| db3fb9d316 | |||
| cd79676b42 | |||
| 09e7e0ab70 | |||
| 0e60f66604 | |||
| fc8db6cdf9 | |||
| 5cc37c4ea0 | |||
| 46ad456718 | |||
| 832ffd2303 | |||
| b261430880 | |||
| 545f345848 | |||
| 77ae0b527e | |||
| c1278109c9 | |||
| a7a88d01ef | |||
| 4e0ab6b634 | |||
| d36485b7af | |||
| 1da24f6adb | |||
| e29dd27632 | |||
| 37380e1f94 | |||
| 1309ca7a81 | |||
| c1515316f5 | |||
| b66e7b6fce | |||
| eb398f1951 | |||
| 643c22d21f | |||
| 74acd08ead |
@@ -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
|
||||
@@ -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 }}
|
||||
|
||||
@@ -17,6 +17,7 @@ If you are unsure if a PR would be accepted, feel free to ask a maintainer or lo
|
||||
- [`help wanted`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted)
|
||||
- [`good first issue`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)
|
||||
- [`bug`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug)
|
||||
- [`perf`](https://github.com/sst/opencode/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22perf%22)
|
||||
|
||||
> [!NOTE]
|
||||
> PRs that ignore these guardrails will likely be closed.
|
||||
|
||||
@@ -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) |
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -64,7 +64,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -88,7 +88,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -109,12 +109,12 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"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",
|
||||
@@ -150,7 +150,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
@@ -166,7 +166,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -217,6 +217,10 @@
|
||||
"@ai-sdk/google-vertex": "3.0.16",
|
||||
"@octokit/webhooks-types": "7.6.1",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@parcel/watcher-darwin-arm64": "2.5.1",
|
||||
"@parcel/watcher-darwin-x64": "2.5.1",
|
||||
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-x64-glibc": "2.5.1",
|
||||
"@parcel/watcher-win32-x64": "2.5.1",
|
||||
"@standard-schema/spec": "1.0.0",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
@@ -230,7 +234,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -250,7 +254,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -261,7 +265,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -274,7 +278,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/precision-diffs": "catalog:",
|
||||
@@ -297,7 +301,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -344,7 +348,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",
|
||||
@@ -938,7 +942,7 @@
|
||||
|
||||
"@petamoriken/float16": ["@petamoriken/float16@3.9.2", "", {}, "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -3498,8 +3502,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=="],
|
||||
@@ -3736,8 +3738,6 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"opencode/ulid": ["ulid@3.0.1", "", { "bin": { "ulid": "dist/cli.js" } }, "sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q=="],
|
||||
|
||||
"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=="],
|
||||
@@ -4072,10 +4072,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=="],
|
||||
@@ -4242,10 +4238,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=="],
|
||||
@@ -4426,18 +4418,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=="],
|
||||
@@ -4460,18 +4440,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=="],
|
||||
|
||||
+2
-2
@@ -25,7 +25,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",
|
||||
@@ -63,7 +63,7 @@
|
||||
"license": "MIT",
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"printWidth": 120
|
||||
"printWidth": 100
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"esbuild",
|
||||
|
||||
@@ -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.28"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
||||
@@ -36,6 +36,9 @@ export function Header(props: { zen?: boolean }) {
|
||||
<li>
|
||||
<a href="/docs">Docs</a>
|
||||
</li>
|
||||
<li>
|
||||
<A href="/enterprise">Enterprise</A>
|
||||
</li>
|
||||
<li>
|
||||
<Switch>
|
||||
<Match when={props.zen}>
|
||||
@@ -107,6 +110,9 @@ export function Header(props: { zen?: boolean }) {
|
||||
<li>
|
||||
<a href="/docs">Docs</a>
|
||||
</li>
|
||||
<li>
|
||||
<A href="/enterprise">Enterprise</A>
|
||||
</li>
|
||||
<li>
|
||||
<Switch>
|
||||
<Match when={props.zen}>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { AWS } from "@opencode-ai/console-core/aws.js"
|
||||
|
||||
interface EnterpriseFormData {
|
||||
name: string
|
||||
role: string
|
||||
email: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export async function POST(event: APIEvent) {
|
||||
try {
|
||||
const body = (await event.request.json()) as EnterpriseFormData
|
||||
|
||||
// Validate required fields
|
||||
if (!body.name || !body.role || !body.email || !body.message) {
|
||||
return Response.json({ error: "All fields are required" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Validate email format
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(body.email)) {
|
||||
return Response.json({ error: "Invalid email format" }, { status: 400 })
|
||||
}
|
||||
|
||||
// Create email content
|
||||
const emailContent = `
|
||||
New Enterprise Inquiry
|
||||
|
||||
Name: ${body.name}
|
||||
Role: ${body.role}
|
||||
Email: ${body.email}
|
||||
|
||||
Message:
|
||||
${body.message}
|
||||
`.trim()
|
||||
|
||||
// Send email using AWS SES
|
||||
await AWS.sendEmail({
|
||||
to: "enterprise@opencode.ai",
|
||||
subject: `Enterprise Inquiry from ${body.name}`,
|
||||
body: emailContent,
|
||||
})
|
||||
|
||||
return Response.json({ success: true, message: "Form submitted successfully" }, { status: 200 })
|
||||
} catch (error) {
|
||||
console.error("Error processing enterprise form:", error)
|
||||
return Response.json({ error: "Internal server error" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,544 @@
|
||||
::selection {
|
||||
background: var(--color-background-interactive);
|
||||
color: var(--color-text-strong);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background: var(--color-background-interactive);
|
||||
color: var(--color-text-inverted);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[data-page="enterprise"] {
|
||||
--color-background: hsl(0, 20%, 99%);
|
||||
--color-background-weak: hsl(0, 8%, 97%);
|
||||
--color-background-weak-hover: hsl(0, 8%, 94%);
|
||||
--color-background-strong: hsl(0, 5%, 12%);
|
||||
--color-background-strong-hover: hsl(0, 5%, 18%);
|
||||
--color-background-interactive: hsl(62, 84%, 88%);
|
||||
--color-background-interactive-weaker: hsl(64, 74%, 95%);
|
||||
|
||||
--color-text: hsl(0, 1%, 39%);
|
||||
--color-text-weak: hsl(0, 1%, 60%);
|
||||
--color-text-weaker: hsl(30, 2%, 81%);
|
||||
--color-text-strong: hsl(0, 5%, 12%);
|
||||
--color-text-inverted: hsl(0, 20%, 99%);
|
||||
--color-text-success: hsl(119, 100%, 35%);
|
||||
|
||||
--color-border: hsl(30, 2%, 81%);
|
||||
--color-border-weak: hsl(0, 1%, 85%);
|
||||
|
||||
--color-icon: hsl(0, 1%, 55%);
|
||||
--color-success: hsl(142, 76%, 36%);
|
||||
|
||||
background: var(--color-background);
|
||||
font-family: var(--font-mono);
|
||||
color: var(--color-text);
|
||||
padding-bottom: 5rem;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
--color-background: hsl(0, 9%, 7%);
|
||||
--color-background-weak: hsl(0, 6%, 10%);
|
||||
--color-background-weak-hover: hsl(0, 6%, 15%);
|
||||
--color-background-strong: hsl(0, 15%, 94%);
|
||||
--color-background-strong-hover: hsl(0, 15%, 97%);
|
||||
--color-background-interactive: hsl(62, 100%, 90%);
|
||||
--color-background-interactive-weaker: hsl(60, 20%, 8%);
|
||||
|
||||
--color-text: hsl(0, 4%, 71%);
|
||||
--color-text-weak: hsl(0, 2%, 49%);
|
||||
--color-text-weaker: hsl(0, 3%, 28%);
|
||||
--color-text-strong: hsl(0, 15%, 94%);
|
||||
--color-text-inverted: hsl(0, 9%, 7%);
|
||||
--color-text-success: hsl(119, 60%, 72%);
|
||||
|
||||
|
||||
--color-border: hsl(0, 3%, 28%);
|
||||
--color-border-weak: hsl(0, 4%, 23%);
|
||||
|
||||
--color-icon: hsl(10, 3%, 43%);
|
||||
--color-success: hsl(142, 76%, 46%);
|
||||
}
|
||||
|
||||
/* Header and Footer styles - copied from index.css */
|
||||
[data-component="top"] {
|
||||
padding: 24px 5rem;
|
||||
height: 80px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: var(--color-background);
|
||||
border-bottom: 1px solid var(--color-border-weak);
|
||||
z-index: 10;
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
padding: 24px 1.5rem;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 34px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
[data-component="nav-desktop"] {
|
||||
ul {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 48px;
|
||||
li {
|
||||
display: inline-block;
|
||||
a {
|
||||
text-decoration: none;
|
||||
span {
|
||||
color: var(--color-text-weak);
|
||||
}
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-thickness: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="nav-mobile"] {
|
||||
button > svg {
|
||||
color: var(--color-icon);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="nav-mobile-toggle"] {
|
||||
border: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
cursor: pointer;
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
[data-component="nav-mobile-toggle"]:hover {
|
||||
background: var(--color-background-weak);
|
||||
}
|
||||
|
||||
[data-component="nav-mobile"] {
|
||||
display: none;
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
display: block;
|
||||
|
||||
[data-component="nav-mobile-icon"] {
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[data-component="nav-mobile-menu-list"] {
|
||||
position: fixed;
|
||||
background: var(--color-background);
|
||||
top: 80px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 100vh;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 20px 0;
|
||||
|
||||
li {
|
||||
a {
|
||||
text-decoration: none;
|
||||
padding: 20px;
|
||||
display: block;
|
||||
|
||||
span {
|
||||
color: var(--color-text-weak);
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: var(--color-background-weak);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="logo dark"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
[data-slot="logo light"] {
|
||||
display: none;
|
||||
}
|
||||
[data-slot="logo dark"] {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="footer"] {
|
||||
border-top: 1px solid var(--color-border-weak);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@media (max-width: 65rem) {
|
||||
border-bottom: 1px solid var(--color-border-weak);
|
||||
}
|
||||
|
||||
[data-slot="cell"] {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
padding: 2rem 0;
|
||||
width: 100%;
|
||||
display: block;
|
||||
|
||||
span {
|
||||
color: var(--color-text-weak);
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: var(--color-background-weak);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-thickness: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="cell"] + [data-slot="cell"] {
|
||||
border-left: 1px solid var(--color-border-weak);
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile: third column on its own row */
|
||||
@media (max-width: 25rem) {
|
||||
flex-wrap: wrap;
|
||||
|
||||
[data-slot="cell"] {
|
||||
flex: 1 0 100%;
|
||||
border-left: none;
|
||||
border-top: 1px solid var(--color-border-weak);
|
||||
}
|
||||
|
||||
[data-slot="cell"]:nth-child(1) {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="container"] {
|
||||
max-width: 67.5rem;
|
||||
margin: 0 auto;
|
||||
border: 1px solid var(--color-border-weak);
|
||||
border-top: none;
|
||||
|
||||
@media (max-width: 65rem) {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="content"] {
|
||||
}
|
||||
|
||||
[data-component="enterprise-content"] {
|
||||
padding: 4rem 0;
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="enterprise-columns"] {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
padding: 4rem 5rem;
|
||||
|
||||
@media (max-width: 80rem) {
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 3rem;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="enterprise-column-1"] {
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-strong);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-strong);
|
||||
margin: 2rem 0 1rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
[data-component="testimonial"] {
|
||||
margin-top: 4rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-strong);
|
||||
|
||||
[data-component="quotation"] {
|
||||
svg {
|
||||
margin-bottom: 1rem;
|
||||
opacity: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="testimonial-logo"] {
|
||||
svg {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="enterprise-column-2"] {
|
||||
[data-component="enterprise-form"] {
|
||||
padding: 0;
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-strong);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
[data-component="form-group"] {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-weak);
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
input:-webkit-autofill:active {
|
||||
transition: background-color 5000000s ease-in-out 0s;
|
||||
}
|
||||
|
||||
input:-webkit-autofill {
|
||||
-webkit-text-fill-color: var(--color-text-strong) !important;
|
||||
}
|
||||
|
||||
input:-moz-autofill {
|
||||
-moz-text-fill-color: var(--color-text-strong) !important;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--color-border-weak);
|
||||
border-radius: 4px;
|
||||
background: var(--color-background-weak);
|
||||
color: var(--color-text-strong);
|
||||
font-family: inherit;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-weak);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: var(--color-background-interactive-weaker);
|
||||
outline: none;
|
||||
border: none;
|
||||
color: var(--color-text-strong);
|
||||
border: 1px solid var(--color-background-strong);
|
||||
box-shadow: 0 0 0 3px var(--color-background-interactive);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--color-background-interactive)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="submit-button"] {
|
||||
padding: 0.5rem 1.5rem;
|
||||
background: var(--color-background-strong);
|
||||
color: var(--color-text-inverted);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--color-background-strong-hover);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="success-message"] {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem 0;
|
||||
color: var(--color-text-success);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="faq"] {
|
||||
border-top: 1px solid var(--color-border-weak);
|
||||
padding: 4rem 5rem;
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
[data-slot="section-title"] {
|
||||
margin-bottom: 24px;
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-strong);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 12px;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
margin-bottom: 24px;
|
||||
line-height: 200%;
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
line-height: 180%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="faq-question"] {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
color: var(--color-text-strong);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
[data-slot="faq-icon-plus"] {
|
||||
flex-shrink: 0;
|
||||
color: var(--color-text-weak);
|
||||
margin-top: 2px;
|
||||
|
||||
[data-closed] & {
|
||||
display: block;
|
||||
}
|
||||
[data-expanded] & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
[data-slot="faq-icon-minus"] {
|
||||
flex-shrink: 0;
|
||||
color: var(--color-text-weak);
|
||||
margin-top: 2px;
|
||||
|
||||
[data-closed] & {
|
||||
display: none;
|
||||
}
|
||||
[data-expanded] & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
[data-slot="faq-question-text"] {
|
||||
flex-grow: 1;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="faq-answer"] {
|
||||
margin-left: 40px;
|
||||
margin-bottom: 32px;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="legal"] {
|
||||
color: var(--color-text-weak);
|
||||
text-align: center;
|
||||
padding: 2rem 5rem;
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text-weak);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text-strong);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-thickness: 1px;
|
||||
|
||||
&:hover {
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import "./index.css"
|
||||
import { Title, Meta } from "@solidjs/meta"
|
||||
import { createSignal } from "solid-js"
|
||||
import { Header } from "~/component/header"
|
||||
import { Footer } from "~/component/footer"
|
||||
import { Legal } from "~/component/legal"
|
||||
import { Faq } from "~/component/faq"
|
||||
|
||||
export default function Enterprise() {
|
||||
const [formData, setFormData] = createSignal({
|
||||
name: "",
|
||||
role: "",
|
||||
email: "",
|
||||
message: "",
|
||||
})
|
||||
const [isSubmitting, setIsSubmitting] = createSignal(false)
|
||||
const [showSuccess, setShowSuccess] = createSignal(false)
|
||||
|
||||
const handleInputChange = (field: string) => (e: Event) => {
|
||||
const target = e.target as HTMLInputElement | HTMLTextAreaElement
|
||||
setFormData((prev) => ({ ...prev, [field]: target.value }))
|
||||
}
|
||||
|
||||
const handleSubmit = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
setIsSubmitting(true)
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/enterprise", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData()),
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
setShowSuccess(true)
|
||||
setFormData({
|
||||
name: "",
|
||||
role: "",
|
||||
email: "",
|
||||
message: "",
|
||||
})
|
||||
setTimeout(() => setShowSuccess(false), 5000)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to submit form:", error)
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main data-page="enterprise">
|
||||
<Title>OpenCode | Enterprise solutions for your organisation</Title>
|
||||
<Meta name="description" content="Contact OpenCode for enterprise solutions" />
|
||||
<div data-component="container">
|
||||
<Header />
|
||||
|
||||
<div data-component="content">
|
||||
<section data-component="enterprise-content">
|
||||
<div data-component="enterprise-columns">
|
||||
<div data-component="enterprise-column-1">
|
||||
<h2>Your code is yours</h2>
|
||||
<p>
|
||||
OpenCode operates securely inside your organization with no data or context stored and no licensing restrictions or ownership claims. Start a trial with your team today, then scale confidently with enterprise-grade features including SSO, private registries, and self-hosting.
|
||||
</p>
|
||||
<p>
|
||||
Let us know and how we can help.
|
||||
</p>
|
||||
|
||||
<div data-component="testimonial">
|
||||
<div data-component="quotation">
|
||||
<svg width="20" height="17" viewBox="0 0 20 17" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M19.4118 0L16.5882 9.20833H20V17H12.2353V10.0938L16 0H19.4118ZM7.17647 0L4.35294 9.20833H7.76471V17H0V10.0938L3.76471 0H7.17647Z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
|
||||
Thanks to OpenCode, we found a way to create software to track all our assets — even the imaginary ones.
|
||||
|
||||
<div data-component="testimonial-logo">
|
||||
<svg width="80" height="79" viewBox="0 0 80 79" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M0 39.3087L10.0579 29.251L15.6862 34.7868L13.7488 36.7248L10.3345 33.2186L8.48897 35.0639L11.8111 38.4781L9.96557 40.4156L6.55181 37.0018L4.06028 39.4928L7.56674 42.9991L5.62884 44.845L0 39.3087Z"
|
||||
fill="#0083C6" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M17.7182 36.8164L20.2094 39.4003L16.6108 46.9666L22.2393 41.3374L24.3615 43.46L14.2118 53.5179L11.9047 51.1187L15.4112 43.3677L9.78254 49.0888L7.66016 46.9666L17.7182 36.8164Z"
|
||||
fill="#0083C6" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M42.8139 61.915L45.3055 64.4064L41.6145 71.9731L47.243 66.3441L49.3652 68.4663L39.3077 78.5244L36.9088 76.1252L40.5072 68.374L34.7866 74.0953L32.6641 71.9731L42.8139 61.915Z"
|
||||
fill="#0083C6" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M16.4258 55.7324L26.4833 45.582L28.6061 47.7042C31.0049 50.1034 32.3892 51.9497 30.1746 54.1642C28.7902 55.548 27.6831 56.0094 26.1145 54.9016L26.0222 54.994C27.2218 56.1941 26.9448 57.1162 25.4688 58.5931L23.9 60.1615C23.4383 60.6232 22.8847 61.2693 22.7927 62.0067L20.6705 59.8845C20.7625 59.146 21.3161 58.5008 21.778 58.1316L23.5307 56.3788C24.269 55.6403 23.715 54.2555 23.254 53.8872L22.8847 53.4256L18.548 57.7623L16.4258 55.7324ZM24.3611 51.9495C25.4689 53.0563 26.4833 53.3332 27.4984 52.3178C28.5134 51.3957 28.2367 50.3802 27.1295 49.1812L24.3611 51.9495Z"
|
||||
fill="#0083C6" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M33.4952 66.9899C31.096 69.3891 28.8815 68.4659 27.4047 66.9899C26.021 65.6062 25.0978 63.3907 27.4972 60.9003L31.8336 56.6548C34.2333 54.2556 36.4478 55.0864 37.9241 56.5635C39.308 58.0396 40.2311 60.2541 37.8315 62.6531L33.4952 66.9899ZM29.0659 63.5752C28.6048 64.0369 28.6048 64.7753 29.1583 65.3292C29.6196 65.8821 30.4502 65.7897 30.8194 65.4215L36.2633 59.9769C36.7246 59.6076 36.7246 58.7779 36.171 58.3164C35.7097 57.7626 34.8791 57.7626 34.5101 58.2241L29.0659 63.5752Z"
|
||||
fill="#0083C6" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M78.5267 39.308L68.2845 29.0654L47.5231 49.735L49.6453 51.8572L68.2845 33.2179L74.3746 39.308L47.2461 66.3435L49.3683 68.4657L78.5267 39.308Z"
|
||||
fill="#0083C6" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M49.6443 51.8577L43.3695 45.4902L64.0386 24.8215L53.7969 14.4873L33.0352 35.2482L35.1574 37.3705L53.7969 18.7315L59.7947 24.8215L39.1251 45.4902L47.5221 53.9799L49.6443 51.8577Z"
|
||||
fill="#2D9C5C" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M35.1564 37.3706L28.7896 31.0038L49.5515 10.3347L39.3088 0L10.0586 29.2507L12.1804 31.2804L39.3088 4.24476L45.3066 10.3347L24.6377 31.0038L33.0342 39.4008L35.1564 37.3706Z"
|
||||
fill="#E92A35" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M77.2332 52.4105C76.0336 52.4105 75.111 51.4884 75.111 50.196C75.111 48.9046 76.0336 47.9814 77.2332 47.9814C78.3405 47.9814 79.263 48.9046 79.263 50.196C79.263 51.4884 78.3405 52.4105 77.2332 52.4105ZM77.2332 52.9643C78.7098 52.9643 80.0015 51.6729 80.0015 50.196C80.0015 48.6276 78.7096 47.4287 77.2332 47.4287C75.6644 47.4287 74.4648 48.6278 74.4648 50.196C74.4647 51.6731 75.6643 52.9643 77.2332 52.9643ZM76.1259 51.7653H76.6797V50.3804H77.0485L77.8788 51.7653H78.4332L77.6023 50.3804C78.1558 50.2881 78.4332 50.0122 78.4332 49.5507C78.4332 48.9046 78.0633 48.6276 77.3253 48.6276H76.1257V51.7653H76.1259ZM76.6797 49.0892H77.2332C77.5102 49.0892 77.8788 49.0892 77.8788 49.4586C77.8788 49.9202 77.6023 49.9202 77.2332 49.9202H76.6797V49.0892Z"
|
||||
fill="#0083C6" />
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-component="enterprise-column-2">
|
||||
<div data-component="enterprise-form">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div data-component="form-group">
|
||||
<label for="name">Full name</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
required
|
||||
value={formData().name}
|
||||
onInput={handleInputChange('name')}
|
||||
placeholder="Jeff Bezos"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="role">Role</label>
|
||||
<input
|
||||
id="role"
|
||||
type="text"
|
||||
required
|
||||
value={formData().role}
|
||||
onInput={handleInputChange('role')}
|
||||
placeholder="Executive Chairman"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="email">Company email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
value={formData().email}
|
||||
onInput={handleInputChange('email')}
|
||||
placeholder="jeff@amazon.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="message">What problem are you trying to
|
||||
solve?</label>
|
||||
<textarea
|
||||
id="message"
|
||||
required
|
||||
rows={5}
|
||||
value={formData().message}
|
||||
onInput={handleInputChange('message')}
|
||||
placeholder="We need help with..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit" disabled={isSubmitting()}
|
||||
data-component="submit-button">
|
||||
{isSubmitting() ? 'Sending...' : 'Send'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{showSuccess() && (
|
||||
<div data-component="success-message">
|
||||
Message sent, we'll be in touch soon.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-component="faq">
|
||||
<div data-slot="section-title">
|
||||
<h3>FAQ</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<Faq question="Does Opencode store our code or context data?">
|
||||
No. OpenCode never stores your code or context data. All
|
||||
processing happens locally or directly with your AI provider.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Who owns the code generated with OpenCode?">
|
||||
You do. All code produced is yours, with no licensing
|
||||
restrictions or ownership claims.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq
|
||||
question="How can we trial OpenCode inside our organization?">
|
||||
Simply install and run an internal trial with your team. Since
|
||||
OpenCode doesn’t store any data, your developers can get
|
||||
started right away.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq
|
||||
question="What happens if someone uses the `/share` feature?">
|
||||
By default, sharing is disabled. If enabled, conversations are
|
||||
sent to our share service and cached through our CDN. For
|
||||
enterprise use, we recommend disabling or self-hosting this
|
||||
feature.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Can OpenCode integrate with our company’s SSO?">
|
||||
Yes. Enterprise deployments can include SSO integration so all
|
||||
sessions and shared conversations are protected by your
|
||||
authentication system.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Can OpenCode be self-hosted?">
|
||||
Absolutely. You can fully self-host OpenCode, including the
|
||||
share feature, ensuring that data and pages are accessible
|
||||
only after authentication.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq
|
||||
question="How do we get started with enterprise deployment?">
|
||||
Contact us to discuss pricing, implementation, and enterprise
|
||||
options like SSO, private registries, and self-hosting.
|
||||
</Faq>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
<Legal />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -827,12 +827,8 @@ body {
|
||||
outline: none;
|
||||
border: none;
|
||||
color: var(--color-text-strong);
|
||||
|
||||
border: 1px solid var(--color-background-strong); /* Tailwind blue-600 as example */
|
||||
|
||||
/* Tailwind-style ring */
|
||||
border: 1px solid var(--color-background-strong);
|
||||
box-shadow: 0 0 0 3px var(--color-background-interactive);
|
||||
/* mimics "ring-2 ring-blue-600/50" */
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
box-shadow: none;
|
||||
|
||||
@@ -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`)
|
||||
|
||||
@@ -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)),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -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" -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"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",
|
||||
|
||||
@@ -1,362 +0,0 @@
|
||||
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 { 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 { getDirectory, getFilename } from "@/utils"
|
||||
import type { ListTool } from "opencode/tool/ls"
|
||||
import type { GlobTool } from "opencode/tool/glob"
|
||||
import type { GrepTool } from "opencode/tool/grep"
|
||||
import type { WebFetchTool } from "opencode/tool/webfetch"
|
||||
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 { DiffChanges } from "./diff-changes"
|
||||
|
||||
export function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
|
||||
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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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 }) {
|
||||
return (
|
||||
<Show when={props.part.text.trim()}>
|
||||
<Markdown text={props.part.text.trim()} />
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
function ToolPart(props: { part: ToolPart; message: AssistantMessage }) {
|
||||
// const sync = useSync()
|
||||
|
||||
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> */}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
return <Show when={component()}>{component()}</Show>
|
||||
}
|
||||
|
||||
type TriggerTitle = {
|
||||
title: string
|
||||
subtitle?: string
|
||||
args?: string[]
|
||||
action?: JSX.Element
|
||||
}
|
||||
|
||||
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 }) {
|
||||
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>
|
||||
</div>
|
||||
<Show when={resolved()}>
|
||||
<Collapsible.Arrow />
|
||||
</Show>
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
<Show when={props.children}>
|
||||
<Collapsible.Content>{props.children}</Collapsible.Content>
|
||||
</Show>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
|
||||
function GenericTool(props: ToolProps<any>) {
|
||||
return <BasicTool icon="mcp" trigger={{ title: props.tool }} />
|
||||
}
|
||||
|
||||
type ToolProps<T extends Tool.Info> = {
|
||||
input: Partial<Tool.InferParameters<T>>
|
||||
metadata: Partial<Tool.InferMetadata<T>>
|
||||
// permission: Record<string, any>
|
||||
tool: string
|
||||
output?: string
|
||||
}
|
||||
|
||||
const ToolRegistry = (() => {
|
||||
const state: Record<
|
||||
string,
|
||||
{
|
||||
name: string
|
||||
render?: Component<ToolProps<any>>
|
||||
}
|
||||
> = {}
|
||||
function register<T extends Tool.Info>(input: { name: string; render?: Component<ToolProps<T>> }) {
|
||||
state[input.name] = input
|
||||
return input
|
||||
}
|
||||
return {
|
||||
register,
|
||||
render(name: string) {
|
||||
return state[name]?.render
|
||||
},
|
||||
}
|
||||
})()
|
||||
|
||||
ToolRegistry.register<typeof ReadTool>({
|
||||
name: "read",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="glasses"
|
||||
trigger={{ title: props.tool, subtitle: props.input.filePath ? getFilename(props.input.filePath) : "" }}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof ListTool>({
|
||||
name: "list",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool icon="bullet-list" trigger={{ title: props.tool, subtitle: props.input.path || "/" }}>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof GlobTool>({
|
||||
name: "glob",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="magnifying-glass-menu"
|
||||
trigger={{
|
||||
title: props.tool,
|
||||
subtitle: props.input.path || "/",
|
||||
args: props.input.pattern ? ["pattern=" + props.input.pattern] : [],
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof GrepTool>({
|
||||
name: "grep",
|
||||
render(props) {
|
||||
const args = []
|
||||
if (props.input.pattern) args.push("pattern=" + props.input.pattern)
|
||||
if (props.input.include) args.push("include=" + props.input.include)
|
||||
return (
|
||||
<BasicTool
|
||||
icon="magnifying-glass-menu"
|
||||
trigger={{
|
||||
title: props.tool,
|
||||
subtitle: props.input.path || "/",
|
||||
args,
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof WebFetchTool>({
|
||||
name: "webfetch",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="window-cursor"
|
||||
trigger={{
|
||||
title: props.tool,
|
||||
subtitle: props.input.url || "",
|
||||
args: props.input.format ? ["format=" + props.input.format] : [],
|
||||
action: (
|
||||
<div class="size-6 flex items-center justify-center">
|
||||
<Icon name="square-arrow-top-right" size="small" />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof TaskTool>({
|
||||
name: "task",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="task"
|
||||
trigger={{
|
||||
title: `${props.input.subagent_type || props.tool} Agent`,
|
||||
subtitle: props.input.description,
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof BashTool>({
|
||||
name: "bash",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="console"
|
||||
trigger={{
|
||||
title: "Shell",
|
||||
subtitle: "Ran " + props.input.command,
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof EditTool>({
|
||||
name: "edit",
|
||||
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">Edit</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>
|
||||
}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</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-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>
|
||||
}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,459 @@
|
||||
import type { Part, ReasoningPart, TextPart, ToolPart, Message, AssistantMessage, UserMessage } 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 { 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"
|
||||
import type { WebFetchTool } from "opencode/tool/webfetch"
|
||||
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 Message(props: { message: Message; parts: Part[] }) {
|
||||
return (
|
||||
<Switch>
|
||||
<Match when={props.message.role === "user" && props.message}>
|
||||
{(userMessage) => <UserMessage message={userMessage()} parts={props.parts} />}
|
||||
</Match>
|
||||
<Match when={props.message.role === "assistant" && props.message}>
|
||||
{(assistantMessage) => <AssistantMessage message={assistantMessage()} parts={props.parts} />}
|
||||
</Match>
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
||||
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={filteredParts()}>{(part) => <Part part={part} message={props.message} />}</For>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function UserMessage(props: { message: UserMessage; parts: Part[] }) {
|
||||
const text = createMemo(() =>
|
||||
props.parts
|
||||
?.filter((p) => p.type === "text" && !p.synthetic)
|
||||
?.map((p) => (p as TextPart).text)
|
||||
?.join(""),
|
||||
)
|
||||
return <div class="text-12-regular text-text-base line-clamp-3">{text()}</div>
|
||||
}
|
||||
|
||||
export function Part(props: { part: Part; message: Message; hideDetails?: 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}
|
||||
hideDetails={props.hideDetails}
|
||||
/>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
const PART_MAPPING = {
|
||||
text: TextPart,
|
||||
tool: ToolPart,
|
||||
reasoning: ReasoningPart,
|
||||
}
|
||||
|
||||
function ReasoningPart(props: { part: ReasoningPart; message: Message }) {
|
||||
return (
|
||||
<Show when={props.part.text.trim()}>
|
||||
<Markdown text={props.part.text.trim()} />
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
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; hideDetails?: 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 : {}
|
||||
|
||||
return (
|
||||
<Dynamic
|
||||
component={render}
|
||||
input={input}
|
||||
tool={props.part.tool}
|
||||
metadata={metadata}
|
||||
output={props.part.state.status === "completed" ? props.part.state.output : undefined}
|
||||
hideDetails={props.hideDetails}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
return <Show when={component()}>{component()}</Show>
|
||||
}
|
||||
|
||||
type TriggerTitle = {
|
||||
title: string
|
||||
titleClass?: string
|
||||
subtitle?: string
|
||||
subtitleClass?: string
|
||||
args?: string[]
|
||||
argsClass?: string
|
||||
action?: JSX.Element
|
||||
}
|
||||
|
||||
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
|
||||
hideDetails?: 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" 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() && !props.hideDetails}>
|
||||
<Collapsible.Arrow />
|
||||
</Show>
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
<Show when={resolved() && !props.hideDetails}>
|
||||
<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 }} hideDetails={props.hideDetails} />
|
||||
}
|
||||
|
||||
type ToolProps<T extends Tool.Info> = {
|
||||
input: Partial<Tool.InferParameters<T>>
|
||||
metadata: Partial<Tool.InferMetadata<T>>
|
||||
tool: string
|
||||
output?: string
|
||||
hideDetails?: boolean
|
||||
}
|
||||
|
||||
const ToolRegistry = (() => {
|
||||
const state: Record<
|
||||
string,
|
||||
{
|
||||
name: string
|
||||
render?: Component<ToolProps<any>>
|
||||
}
|
||||
> = {}
|
||||
function register<T extends Tool.Info>(input: { name: string; render?: Component<ToolProps<T>> }) {
|
||||
state[input.name] = input
|
||||
return input
|
||||
}
|
||||
return {
|
||||
register,
|
||||
render(name: string) {
|
||||
return state[name]?.render
|
||||
},
|
||||
}
|
||||
})()
|
||||
|
||||
ToolRegistry.register<typeof ReadTool>({
|
||||
name: "read",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="glasses"
|
||||
trigger={{ title: "Read", subtitle: props.input.filePath ? getFilename(props.input.filePath) : "" }}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof ListTool>({
|
||||
name: "list",
|
||||
render(props) {
|
||||
return (
|
||||
<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>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof GlobTool>({
|
||||
name: "glob",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="magnifying-glass-menu"
|
||||
trigger={{
|
||||
title: "Glob",
|
||||
subtitle: getDirectory(props.input.path || "/"),
|
||||
args: props.input.pattern ? ["pattern=" + props.input.pattern] : [],
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof GrepTool>({
|
||||
name: "grep",
|
||||
render(props) {
|
||||
const args = []
|
||||
if (props.input.pattern) args.push("pattern=" + props.input.pattern)
|
||||
if (props.input.include) args.push("include=" + props.input.include)
|
||||
return (
|
||||
<BasicTool
|
||||
icon="magnifying-glass-menu"
|
||||
trigger={{
|
||||
title: "Grep",
|
||||
subtitle: getDirectory(props.input.path || "/"),
|
||||
args,
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof WebFetchTool>({
|
||||
name: "webfetch",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="window-cursor"
|
||||
trigger={{
|
||||
title: "Webfetch",
|
||||
subtitle: props.input.url || "",
|
||||
args: props.input.format ? ["format=" + props.input.format] : [],
|
||||
action: (
|
||||
<div class="size-6 flex items-center justify-center">
|
||||
<Icon name="square-arrow-top-right" size="small" />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof TaskTool>({
|
||||
name: "task",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="task"
|
||||
trigger={{
|
||||
title: `${props.input.subagent_type || props.tool} Agent`,
|
||||
titleClass: "capitalize",
|
||||
subtitle: props.input.description,
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof BashTool>({
|
||||
name: "bash",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="console"
|
||||
trigger={{
|
||||
title: "Shell",
|
||||
subtitle: "Ran " + props.input.command,
|
||||
}}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof EditTool>({
|
||||
name: "edit",
|
||||
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">Edit</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">
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex gap-4 items-center justify-end">{/* <DiffChanges diff={diff} /> */}</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Show when={false && props.output}>
|
||||
<div class="whitespace-pre">{props.output}</div>
|
||||
</Show>
|
||||
</BasicTool>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
ToolRegistry.register<typeof TodoWriteTool>({
|
||||
name: "todowrite",
|
||||
render(props) {
|
||||
return (
|
||||
<BasicTool
|
||||
icon="checklist"
|
||||
trigger={{
|
||||
title: "To-dos",
|
||||
subtitle: `${props.input.todos?.filter((t) => t.status === "completed").length}/${props.input.todos?.length}`,
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -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>
|
||||
@@ -368,9 +393,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
<img src={`https://models.dev/logos/${i.provider.id}.svg`} class="size-6 p-0.5 shrink-0 " />
|
||||
<div class="flex gap-x-3 items-baseline flex-[1_0_0]">
|
||||
<span class="text-14-medium text-text-strong overflow-hidden text-ellipsis">{i.name}</span>
|
||||
<span class="text-12-medium text-text-weak overflow-hidden text-ellipsis truncate min-w-0">
|
||||
{DateTime.fromFormat(i.release_date, "yyyy-MM-dd").toFormat("LLL yyyy")}
|
||||
</span>
|
||||
<Show when={i.release_date}>
|
||||
<span class="text-12-medium text-text-weak overflow-hidden text-ellipsis truncate min-w-0">
|
||||
{DateTime.fromFormat(i.release_date, "yyyy-MM-dd").toFormat("LLL yyyy")}
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={!i.cost || i.cost?.input === 0}>
|
||||
|
||||
@@ -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,8 @@ 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")
|
||||
|
||||
return sync.data.part[message.id]
|
||||
?.filter((p) => p.type === "text")
|
||||
?.filter((p) => !p.synthetic)
|
||||
@@ -506,7 +501,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
messages,
|
||||
messagesWithValidParts,
|
||||
userMessages,
|
||||
working,
|
||||
// working,
|
||||
getMessageText,
|
||||
setActive(sessionId: string | undefined) {
|
||||
setStore("active", sessionId)
|
||||
|
||||
@@ -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 { Message, Part } from "@/components/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,11 @@ 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})`
|
||||
}
|
||||
|
||||
local.session.setActive(session.id)
|
||||
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 +221,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 +234,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,
|
||||
},
|
||||
@@ -279,7 +260,6 @@ export default function Page() {
|
||||
],
|
||||
},
|
||||
})
|
||||
local.session.setActive(session.id)
|
||||
}
|
||||
|
||||
const handleNewSession = () => {
|
||||
@@ -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,65 +635,164 @@ 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 title = createMemo(() => message.summary?.title)
|
||||
const [expanded, setExpanded] = createSignal(false)
|
||||
const parts = createMemo(() => sync.data.part[message.id])
|
||||
const prompt = createMemo(() => local.session.getMessageText(message))
|
||||
const title = createMemo(() => message.summary?.title)
|
||||
const summary = createMemo(() => message.summary?.body)
|
||||
const assistantMessages = createMemo(() => {
|
||||
return sync.data.message[activeSession().id]?.filter(
|
||||
(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">
|
||||
<Message message={message} parts={parts()} />
|
||||
</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 <Message 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()!} hideDetails />
|
||||
)
|
||||
}}
|
||||
</Match>
|
||||
<Match when={lastMessageWithReasoning()}>
|
||||
{(last) => {
|
||||
const lastReasoningPart = createMemo(() =>
|
||||
sync.data.part[last().id].findLast(
|
||||
(p) => p.type === "reasoning",
|
||||
),
|
||||
)
|
||||
return (
|
||||
<Part
|
||||
message={last()}
|
||||
part={lastReasoningPart()!}
|
||||
hideDetails
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</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()!} hideDetails />
|
||||
}}
|
||||
</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)}‎
|
||||
</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 +815,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 +923,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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
@@ -20,6 +20,10 @@
|
||||
"@ai-sdk/amazon-bedrock": "2.2.10",
|
||||
"@ai-sdk/google-vertex": "3.0.16",
|
||||
"@octokit/webhooks-types": "7.6.1",
|
||||
"@parcel/watcher-darwin-arm64": "2.5.1",
|
||||
"@parcel/watcher-darwin-x64": "2.5.1",
|
||||
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-x64-glibc": "2.5.1",
|
||||
"@parcel/watcher-win32-x64": "2.5.1",
|
||||
"@standard-schema/spec": "1.0.0",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
})
|
||||
|
||||
@@ -78,6 +78,10 @@ export const RunCommand = cmd({
|
||||
array: true,
|
||||
describe: "file(s) to attach to message",
|
||||
})
|
||||
.option("title", {
|
||||
type: "string",
|
||||
describe: "title for the session (uses truncated prompt if no value provided)",
|
||||
})
|
||||
},
|
||||
handler: async (args) => {
|
||||
let message = args.message.join(" ")
|
||||
@@ -143,7 +147,19 @@ export const RunCommand = cmd({
|
||||
|
||||
if (args.session) return Session.get(args.session)
|
||||
|
||||
return Session.create({})
|
||||
const title = (() => {
|
||||
if (args.title !== undefined) {
|
||||
if (args.title === "") {
|
||||
return message.slice(0, 50) + (message.length > 50 ? "..." : "")
|
||||
}
|
||||
return args.title
|
||||
}
|
||||
return undefined
|
||||
})()
|
||||
|
||||
return Session.create({
|
||||
title,
|
||||
})
|
||||
})()
|
||||
|
||||
if (!session) {
|
||||
|
||||
@@ -61,6 +61,11 @@ export namespace Config {
|
||||
)),
|
||||
]
|
||||
|
||||
if (Flag.OPENCODE_CONFIG_DIR) {
|
||||
directories.push(Flag.OPENCODE_CONFIG_DIR)
|
||||
log.debug("loading config from OPENCODE_CONFIG_DIR", { path: Flag.OPENCODE_CONFIG_DIR })
|
||||
}
|
||||
|
||||
for (const dir of directories) {
|
||||
await assertValid(dir)
|
||||
installDependencies(dir)
|
||||
@@ -581,6 +586,7 @@ export namespace Config {
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
chatMaxRetries: z.number().optional().describe("Number of retries for chat completions on failure"),
|
||||
disable_paste_summary: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export namespace Flag {
|
||||
export const OPENCODE_AUTO_SHARE = truthy("OPENCODE_AUTO_SHARE")
|
||||
export const OPENCODE_CONFIG = process.env["OPENCODE_CONFIG"]
|
||||
export const OPENCODE_CONFIG_DIR = process.env["OPENCODE_CONFIG_DIR"]
|
||||
export const OPENCODE_CONFIG_CONTENT = process.env["OPENCODE_CONFIG_CONTENT"]
|
||||
export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE")
|
||||
export const OPENCODE_DISABLE_PRUNE = truthy("OPENCODE_DISABLE_PRUNE")
|
||||
|
||||
@@ -142,7 +142,9 @@ export namespace Installation {
|
||||
export const USER_AGENT = `opencode/${CHANNEL}/${VERSION}`
|
||||
|
||||
export async function latest() {
|
||||
return fetch(`https://registry.npmjs.org/opencode-ai/${CHANNEL}`)
|
||||
const [major] = VERSION.split(".").map((x) => Number(x))
|
||||
const channel = CHANNEL === "latest" ? `latest-${major}` : CHANNEL
|
||||
return fetch(`https://registry.npmjs.org/opencode-ai/${channel}`)
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(res.statusText)
|
||||
return res.json()
|
||||
|
||||
@@ -188,6 +188,7 @@ export namespace MCP {
|
||||
}
|
||||
if (state.clients[key]) {
|
||||
result[key] = "connected"
|
||||
continue
|
||||
}
|
||||
result[key] = "failed"
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Log } from "../util/log"
|
||||
import { SessionLock } from "./lock"
|
||||
import { ProviderTransform } from "@/provider/transform"
|
||||
import { SessionRetry } from "./retry"
|
||||
import { Config } from "@/config/config"
|
||||
|
||||
export namespace SessionCompaction {
|
||||
const log = Log.create({ service: "session.compaction" })
|
||||
@@ -258,12 +259,14 @@ export namespace SessionCompaction {
|
||||
}
|
||||
|
||||
let stream = doStream()
|
||||
const cfg = await Config.get()
|
||||
const maxRetries = cfg.experimental?.chatMaxRetries ?? MAX_RETRIES
|
||||
let result = await process(stream, {
|
||||
count: 0,
|
||||
max: MAX_RETRIES,
|
||||
max: maxRetries,
|
||||
})
|
||||
if (result.shouldRetry) {
|
||||
for (let retry = 1; retry < MAX_RETRIES; retry++) {
|
||||
for (let retry = 1; retry < maxRetries; retry++) {
|
||||
const lastRetryPart = result.parts.findLast((p) => p.type === "retry")
|
||||
|
||||
if (lastRetryPart) {
|
||||
@@ -300,7 +303,7 @@ export namespace SessionCompaction {
|
||||
stream = doStream()
|
||||
result = await process(stream, {
|
||||
count: retry,
|
||||
max: MAX_RETRIES,
|
||||
max: maxRetries,
|
||||
})
|
||||
if (!result.shouldRetry) {
|
||||
break
|
||||
|
||||
@@ -23,11 +23,17 @@ import { Snapshot } from "@/snapshot"
|
||||
export namespace Session {
|
||||
const log = Log.create({ service: "session" })
|
||||
|
||||
const parentSessionTitlePrefix = "New session - "
|
||||
const childSessionTitlePrefix = "Child session - "
|
||||
const parentTitlePrefix = "New session - "
|
||||
const childTitlePrefix = "Child session - "
|
||||
|
||||
function createDefaultTitle(isChild = false) {
|
||||
return (isChild ? childSessionTitlePrefix : parentSessionTitlePrefix) + new Date().toISOString()
|
||||
return (isChild ? childTitlePrefix : parentTitlePrefix) + new Date().toISOString()
|
||||
}
|
||||
|
||||
export function isDefaultTitle(title: string) {
|
||||
return new RegExp(
|
||||
`^(${parentTitlePrefix}|${childTitlePrefix})\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$`,
|
||||
).test(title)
|
||||
}
|
||||
|
||||
export const Info = z
|
||||
|
||||
@@ -50,6 +50,7 @@ import { Command } from "../command"
|
||||
import { $, fileURLToPath } from "bun"
|
||||
import { ConfigMarkdown } from "../config/markdown"
|
||||
import { SessionSummary } from "./summary"
|
||||
import { Config } from "@/config/config"
|
||||
|
||||
export namespace SessionPrompt {
|
||||
const log = Log.create({ service: "session.prompt" })
|
||||
@@ -285,7 +286,11 @@ export namespace SessionPrompt {
|
||||
OUTPUT_TOKEN_MAX,
|
||||
),
|
||||
abortSignal: abort.signal,
|
||||
providerOptions: ProviderTransform.providerOptions(model.npm, model.providerID, params.options),
|
||||
providerOptions: ProviderTransform.providerOptions(
|
||||
model.npm,
|
||||
model.providerID,
|
||||
params.options,
|
||||
),
|
||||
stopWhen: stepCountIs(1),
|
||||
temperature: params.temperature,
|
||||
topP: params.topP,
|
||||
@@ -320,7 +325,11 @@ export namespace SessionPrompt {
|
||||
async transformParams(args) {
|
||||
if (args.type === "stream") {
|
||||
// @ts-expect-error
|
||||
args.params.prompt = ProviderTransform.message(args.params.prompt, model.providerID, model.modelID)
|
||||
args.params.prompt = ProviderTransform.message(
|
||||
args.params.prompt,
|
||||
model.providerID,
|
||||
model.modelID,
|
||||
)
|
||||
}
|
||||
return args.params
|
||||
},
|
||||
@@ -330,12 +339,14 @@ export namespace SessionPrompt {
|
||||
})
|
||||
|
||||
let stream = doStream()
|
||||
const cfg = await Config.get()
|
||||
const maxRetries = cfg.experimental?.chatMaxRetries ?? MAX_RETRIES
|
||||
let result = await processor.process(stream, {
|
||||
count: 0,
|
||||
max: MAX_RETRIES,
|
||||
max: maxRetries,
|
||||
})
|
||||
if (result.shouldRetry) {
|
||||
for (let retry = 1; retry < MAX_RETRIES; retry++) {
|
||||
for (let retry = 1; retry < maxRetries; retry++) {
|
||||
const lastRetryPart = result.parts.findLast((p) => p.type === "retry")
|
||||
|
||||
if (lastRetryPart) {
|
||||
@@ -372,7 +383,7 @@ export namespace SessionPrompt {
|
||||
stream = doStream()
|
||||
result = await processor.process(stream, {
|
||||
count: retry,
|
||||
max: MAX_RETRIES,
|
||||
max: maxRetries,
|
||||
})
|
||||
if (!result.shouldRetry) {
|
||||
break
|
||||
@@ -501,7 +512,11 @@ export namespace SessionPrompt {
|
||||
)
|
||||
for (const item of await ToolRegistry.tools(input.providerID, input.modelID)) {
|
||||
if (Wildcard.all(item.id, enabledTools) === false) continue
|
||||
const schema = ProviderTransform.schema(input.providerID, input.modelID, z.toJSONSchema(item.parameters))
|
||||
const schema = ProviderTransform.schema(
|
||||
input.providerID,
|
||||
input.modelID,
|
||||
z.toJSONSchema(item.parameters),
|
||||
)
|
||||
tools[item.id] = tool({
|
||||
id: item.id as any,
|
||||
description: item.description,
|
||||
@@ -518,6 +533,7 @@ export namespace SessionPrompt {
|
||||
args,
|
||||
},
|
||||
)
|
||||
item.parameters.parse(args)
|
||||
const result = await item.execute(args, {
|
||||
sessionID: input.sessionID,
|
||||
abort: options.abortSignal!,
|
||||
@@ -796,7 +812,9 @@ export namespace SessionPrompt {
|
||||
messageID: info.id,
|
||||
sessionID: input.sessionID,
|
||||
type: "file",
|
||||
url: `data:${part.mime};base64,` + Buffer.from(await file.bytes()).toString("base64"),
|
||||
url:
|
||||
`data:${part.mime};base64,` +
|
||||
Buffer.from(await file.bytes()).toString("base64"),
|
||||
mime: part.mime,
|
||||
filename: part.filename!,
|
||||
source: part.source,
|
||||
@@ -870,7 +888,9 @@ export namespace SessionPrompt {
|
||||
synthetic: true,
|
||||
})
|
||||
}
|
||||
const wasPlan = input.messages.some((msg) => msg.info.role === "assistant" && msg.info.mode === "plan")
|
||||
const wasPlan = input.messages.some(
|
||||
(msg) => msg.info.role === "assistant" && msg.info.mode === "plan",
|
||||
)
|
||||
if (wasPlan && input.agent.name === "build") {
|
||||
userMessage.parts.push({
|
||||
id: Identifier.ascending("part"),
|
||||
@@ -950,7 +970,10 @@ export namespace SessionPrompt {
|
||||
partFromToolCall(toolCallID: string) {
|
||||
return toolcalls[toolCallID]
|
||||
},
|
||||
async process(stream: StreamTextResult<Record<string, AITool>, never>, retries: { count: number; max: number }) {
|
||||
async process(
|
||||
stream: StreamTextResult<Record<string, AITool>, never>,
|
||||
retries: { count: number; max: number },
|
||||
) {
|
||||
log.info("process")
|
||||
if (!assistantMsg) throw new Error("call next() first before processing")
|
||||
let shouldRetry = false
|
||||
@@ -1081,7 +1104,10 @@ export namespace SessionPrompt {
|
||||
status: "error",
|
||||
input: value.input,
|
||||
error: (value.error as any).toString(),
|
||||
metadata: value.error instanceof Permission.RejectedError ? value.error.metadata : undefined,
|
||||
metadata:
|
||||
value.error instanceof Permission.RejectedError
|
||||
? value.error.metadata
|
||||
: undefined,
|
||||
time: {
|
||||
start: match.state.time.start,
|
||||
end: Date.now(),
|
||||
@@ -1205,7 +1231,11 @@ export namespace SessionPrompt {
|
||||
error: e,
|
||||
})
|
||||
const error = MessageV2.fromError(e, { providerID: input.providerID })
|
||||
if (retries.count < retries.max && MessageV2.APIError.isInstance(error) && error.data.isRetryable) {
|
||||
if (
|
||||
retries.count < retries.max &&
|
||||
MessageV2.APIError.isInstance(error) &&
|
||||
error.data.isRetryable
|
||||
) {
|
||||
shouldRetry = true
|
||||
await Session.updatePart({
|
||||
id: Identifier.ascending("part"),
|
||||
@@ -1228,7 +1258,11 @@ export namespace SessionPrompt {
|
||||
}
|
||||
const p = await Session.getParts(assistantMsg.id)
|
||||
for (const part of p) {
|
||||
if (part.type === "tool" && part.state.status !== "completed" && part.state.status !== "error") {
|
||||
if (
|
||||
part.type === "tool" &&
|
||||
part.state.status !== "completed" &&
|
||||
part.state.status !== "error"
|
||||
) {
|
||||
Session.updatePart({
|
||||
...part,
|
||||
state: {
|
||||
@@ -1690,12 +1724,15 @@ export namespace SessionPrompt {
|
||||
modelID: string
|
||||
}) {
|
||||
if (input.session.parentID) return
|
||||
if (!Session.isDefaultTitle(input.session.title)) return
|
||||
const isFirst =
|
||||
input.history.filter((m) => m.info.role === "user" && !m.parts.every((p) => "synthetic" in p && p.synthetic))
|
||||
.length === 1
|
||||
input.history.filter(
|
||||
(m) => m.info.role === "user" && !m.parts.every((p) => "synthetic" in p && p.synthetic),
|
||||
).length === 1
|
||||
if (!isFirst) return
|
||||
const small =
|
||||
(await Provider.getSmallModel(input.providerID)) ?? (await Provider.getModel(input.providerID, input.modelID))
|
||||
(await Provider.getSmallModel(input.providerID)) ??
|
||||
(await Provider.getModel(input.providerID, input.modelID))
|
||||
const options = {
|
||||
...ProviderTransform.options(small.providerID, small.modelID, input.session.id),
|
||||
...small.info.options,
|
||||
|
||||
@@ -29,7 +29,15 @@ export namespace SessionSummary {
|
||||
)
|
||||
|
||||
async function summarizeSession(input: { sessionID: string; messages: MessageV2.WithParts[] }) {
|
||||
const diffs = await computeDiff({ messages: input.messages })
|
||||
const files = new Set(
|
||||
input.messages
|
||||
.flatMap((x) => x.parts)
|
||||
.filter((x) => x.type === "patch")
|
||||
.flatMap((x) => x.files),
|
||||
)
|
||||
const diffs = await computeDiff({ messages: input.messages }).then((x) =>
|
||||
x.filter((x) => files.has(x.file)),
|
||||
)
|
||||
await Session.update(input.sessionID, (draft) => {
|
||||
draft.summary = {
|
||||
diffs,
|
||||
@@ -39,7 +47,9 @@ export namespace SessionSummary {
|
||||
|
||||
async function summarizeMessage(input: { messageID: string; messages: MessageV2.WithParts[] }) {
|
||||
const messages = input.messages.filter(
|
||||
(m) => m.info.id === input.messageID || (m.info.role === "assistant" && m.info.parentID === input.messageID),
|
||||
(m) =>
|
||||
m.info.id === input.messageID ||
|
||||
(m.info.role === "assistant" && m.info.parentID === input.messageID),
|
||||
)
|
||||
const msgWithParts = messages.find((m) => m.info.id === input.messageID)!
|
||||
const userMsg = msgWithParts.info as MessageV2.User
|
||||
@@ -50,11 +60,14 @@ export namespace SessionSummary {
|
||||
}
|
||||
await Session.updateMessage(userMsg)
|
||||
|
||||
const assistantMsg = messages.find((m) => m.info.role === "assistant")!.info as MessageV2.Assistant
|
||||
const assistantMsg = messages.find((m) => m.info.role === "assistant")!
|
||||
.info as MessageV2.Assistant
|
||||
const small = await Provider.getSmallModel(assistantMsg.providerID)
|
||||
if (!small) return
|
||||
|
||||
const textPart = msgWithParts.parts.find((p) => p.type === "text" && !p.synthetic) as MessageV2.TextPart
|
||||
const textPart = msgWithParts.parts.find(
|
||||
(p) => p.type === "text" && !p.synthetic,
|
||||
) as MessageV2.TextPart
|
||||
if (textPart && !userMsg.summary?.title) {
|
||||
const result = await generateText({
|
||||
maxOutputTokens: small.info.reasoning ? 1500 : 20,
|
||||
@@ -81,26 +94,33 @@ export namespace SessionSummary {
|
||||
if (
|
||||
messages.some(
|
||||
(m) =>
|
||||
m.info.role === "assistant" && m.parts.some((p) => p.type === "step-finish" && p.reason !== "tool-calls"),
|
||||
m.info.role === "assistant" &&
|
||||
m.parts.some((p) => p.type === "step-finish" && p.reason !== "tool-calls"),
|
||||
)
|
||||
) {
|
||||
const result = await generateText({
|
||||
model: small.language,
|
||||
maxOutputTokens: 50,
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: `
|
||||
let summary = messages
|
||||
.findLast((m) => m.info.role === "assistant")
|
||||
?.parts.findLast((p) => p.type === "text")?.text
|
||||
if (!summary || diffs.length > 0) {
|
||||
const result = await generateText({
|
||||
model: small.language,
|
||||
maxOutputTokens: 100,
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: `
|
||||
Summarize the following conversation into 2 sentences MAX explaining what the assistant did and why. Do not explain the user's input. Do not speak in the third person about the assistant.
|
||||
<conversation>
|
||||
${JSON.stringify(MessageV2.toModelMessage(messages))}
|
||||
</conversation>
|
||||
`,
|
||||
},
|
||||
],
|
||||
})
|
||||
userMsg.summary.body = result.text
|
||||
log.info("body", { body: result.text })
|
||||
},
|
||||
],
|
||||
})
|
||||
summary = result.text
|
||||
}
|
||||
userMsg.summary.body = summary
|
||||
log.info("body", { body: summary })
|
||||
await Session.updateMessage(userMsg)
|
||||
}
|
||||
}
|
||||
@@ -114,7 +134,9 @@ export namespace SessionSummary {
|
||||
let all = await Session.messages(input.sessionID)
|
||||
if (input.messageID)
|
||||
all = all.filter(
|
||||
(x) => x.info.id === input.messageID || (x.info.role === "assistant" && x.info.parentID === input.messageID),
|
||||
(x) =>
|
||||
x.info.id === input.messageID ||
|
||||
(x.info.role === "assistant" && x.info.parentID === input.messageID),
|
||||
)
|
||||
|
||||
return computeDiff({
|
||||
|
||||
@@ -27,7 +27,9 @@ const parser = lazy(async () => {
|
||||
return p
|
||||
} catch (e) {
|
||||
const { default: Parser } = await import("web-tree-sitter")
|
||||
const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, { with: { type: "wasm" } })
|
||||
const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, {
|
||||
with: { type: "wasm" },
|
||||
})
|
||||
await Parser.init({
|
||||
locateFile() {
|
||||
return treeWasm
|
||||
@@ -55,6 +57,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))
|
||||
const permissions = await Agent.get(ctx.agent).then((x) => x.permission.bash)
|
||||
@@ -97,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)}`,
|
||||
|
||||
@@ -7,7 +7,7 @@ import z from "zod"
|
||||
import * as path from "path"
|
||||
import { Tool } from "./tool"
|
||||
import { LSP } from "../lsp"
|
||||
import { createTwoFilesPatch } from "diff"
|
||||
import { createTwoFilesPatch, diffLines } from "diff"
|
||||
import { Permission } from "../permission"
|
||||
import DESCRIPTION from "./edit.txt"
|
||||
import { File } from "../file"
|
||||
@@ -16,6 +16,7 @@ import { FileTime } from "../file/time"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
import { Instance } from "../project/instance"
|
||||
import { Agent } from "../agent/agent"
|
||||
import { Snapshot } from "@/snapshot"
|
||||
|
||||
export const EditTool = Tool.define("edit", {
|
||||
description: DESCRIPTION,
|
||||
@@ -114,10 +115,23 @@ export const EditTool = Tool.define("edit", {
|
||||
}
|
||||
}
|
||||
|
||||
const filediff: Snapshot.FileDiff = {
|
||||
file: filePath,
|
||||
before: contentOld,
|
||||
after: contentNew,
|
||||
additions: 0,
|
||||
deletions: 0,
|
||||
}
|
||||
for (const change of diffLines(contentOld, contentNew)) {
|
||||
if (change.added) filediff.additions += change.count || 0
|
||||
if (change.removed) filediff.deletions += change.count || 0
|
||||
}
|
||||
|
||||
return {
|
||||
metadata: {
|
||||
diagnostics,
|
||||
diff,
|
||||
filediff,
|
||||
},
|
||||
title: `${path.relative(Instance.worktree, filePath)}`,
|
||||
output,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.15.20",
|
||||
"version": "0.15.28",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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)[]
|
||||
|
||||
@@ -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]) => {
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
|
||||
@@ -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>({
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,10 @@ import type {
|
||||
TuiExecuteCommandErrors,
|
||||
TuiShowToastData,
|
||||
TuiShowToastResponses,
|
||||
TuiControlNextData,
|
||||
TuiControlNextResponses,
|
||||
TuiControlResponseData,
|
||||
TuiControlResponseResponses,
|
||||
AuthSetData,
|
||||
AuthSetResponses,
|
||||
AuthSetErrors,
|
||||
@@ -133,10 +137,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
|
||||
@@ -164,7 +168,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,
|
||||
@@ -174,7 +180,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,
|
||||
@@ -196,8 +204,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: {
|
||||
@@ -210,7 +224,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,
|
||||
@@ -256,7 +272,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,
|
||||
@@ -266,8 +284,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: {
|
||||
@@ -280,8 +304,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,
|
||||
})
|
||||
@@ -291,7 +321,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,
|
||||
})
|
||||
@@ -300,8 +334,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: {
|
||||
@@ -314,8 +354,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,
|
||||
})
|
||||
@@ -324,8 +370,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,
|
||||
})
|
||||
@@ -334,8 +386,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: {
|
||||
@@ -348,7 +406,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,
|
||||
@@ -362,8 +422,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,
|
||||
})
|
||||
@@ -372,8 +438,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,
|
||||
})
|
||||
@@ -382,8 +454,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,
|
||||
})
|
||||
@@ -392,7 +470,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,
|
||||
@@ -402,8 +482,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: {
|
||||
@@ -416,8 +502,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,
|
||||
})
|
||||
@@ -426,8 +518,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: {
|
||||
@@ -440,8 +538,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,
|
||||
})
|
||||
@@ -450,8 +554,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: {
|
||||
@@ -464,8 +574,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: {
|
||||
@@ -478,8 +594,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: {
|
||||
@@ -492,8 +614,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,
|
||||
})
|
||||
@@ -504,7 +632,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,
|
||||
@@ -526,7 +656,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,
|
||||
@@ -536,7 +668,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,
|
||||
@@ -568,7 +702,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,
|
||||
@@ -594,7 +730,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,
|
||||
@@ -606,7 +744,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,
|
||||
@@ -614,12 +754,52 @@ class Mcp extends _HeyApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
class Control extends _HeyApiClient {
|
||||
/**
|
||||
* Get the next TUI request from the queue
|
||||
*/
|
||||
public next<ThrowOnError extends boolean = false>(
|
||||
options?: Options<TuiControlNextData, ThrowOnError>,
|
||||
) {
|
||||
return (options?.client ?? this._client).get<TuiControlNextResponses, unknown, ThrowOnError>({
|
||||
url: "/tui/control/next",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a response to the TUI request queue
|
||||
*/
|
||||
public response<ThrowOnError extends boolean = false>(
|
||||
options?: Options<TuiControlResponseData, ThrowOnError>,
|
||||
) {
|
||||
return (options?.client ?? this._client).post<
|
||||
TuiControlResponseResponses,
|
||||
unknown,
|
||||
ThrowOnError
|
||||
>({
|
||||
url: "/tui/control/response",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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: {
|
||||
@@ -632,7 +812,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,
|
||||
@@ -642,7 +824,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,
|
||||
@@ -652,7 +836,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,
|
||||
@@ -662,7 +848,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,
|
||||
@@ -672,7 +860,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,
|
||||
@@ -682,7 +872,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,
|
||||
@@ -692,8 +884,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: {
|
||||
@@ -706,7 +904,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,
|
||||
@@ -716,6 +916,7 @@ class Tui extends _HeyApiClient {
|
||||
},
|
||||
})
|
||||
}
|
||||
control = new Control({ client: this._client })
|
||||
}
|
||||
|
||||
class Auth extends _HeyApiClient {
|
||||
@@ -738,8 +939,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,
|
||||
})
|
||||
|
||||
@@ -505,6 +505,10 @@ export type Config = {
|
||||
}
|
||||
}>
|
||||
}
|
||||
/**
|
||||
* Number of retries for chat completions on failure
|
||||
*/
|
||||
chatMaxRetries?: number
|
||||
disable_paste_summary?: boolean
|
||||
}
|
||||
}
|
||||
@@ -658,7 +662,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
|
||||
@@ -1222,7 +1231,12 @@ export type EventSessionError = {
|
||||
type: "session.error"
|
||||
properties: {
|
||||
sessionID?: string
|
||||
error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError
|
||||
error?:
|
||||
| ProviderAuthError
|
||||
| UnknownError
|
||||
| MessageOutputLengthError
|
||||
| MessageAbortedError
|
||||
| ApiError
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2618,6 +2632,46 @@ export type TuiShowToastResponses = {
|
||||
|
||||
export type TuiShowToastResponse = TuiShowToastResponses[keyof TuiShowToastResponses]
|
||||
|
||||
export type TuiControlNextData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/tui/control/next"
|
||||
}
|
||||
|
||||
export type TuiControlNextResponses = {
|
||||
/**
|
||||
* Next TUI request
|
||||
*/
|
||||
200: {
|
||||
path: string
|
||||
body: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type TuiControlNextResponse = TuiControlNextResponses[keyof TuiControlNextResponses]
|
||||
|
||||
export type TuiControlResponseData = {
|
||||
body?: unknown
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/tui/control/response"
|
||||
}
|
||||
|
||||
export type TuiControlResponseResponses = {
|
||||
/**
|
||||
* Response submitted successfully
|
||||
*/
|
||||
200: boolean
|
||||
}
|
||||
|
||||
export type TuiControlResponseResponse =
|
||||
TuiControlResponseResponses[keyof TuiControlResponseResponses]
|
||||
|
||||
export type AuthSetData = {
|
||||
body?: Auth
|
||||
path: {
|
||||
|
||||
@@ -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/
|
||||
@@ -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.
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
```
|
||||
@@ -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))
|
||||
```
|
||||
@@ -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())
|
||||
```
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -0,0 +1,4 @@
|
||||
from opencode_ai import OpenCodeClient
|
||||
|
||||
client = OpenCodeClient()
|
||||
print([s.id for s in client.list_sessions() or []])
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"]
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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",
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
"""Contains methods for accessing the API"""
|
||||
@@ -0,0 +1 @@
|
||||
"""Contains endpoint functions for accessing the API"""
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user