Compare commits
242
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5a710c9b2 | ||
|
|
9a5dd18c49 | ||
|
|
cd9eeb7d41 | ||
|
|
6011200128 | ||
|
|
740fcd243c | ||
|
|
e4d8a117c4 | ||
|
|
8c4a816cf6 | ||
|
|
5605fc3f38 | ||
|
|
009b096004 | ||
|
|
64f898601b | ||
|
|
224e5466c1 | ||
|
|
87b5b34280 | ||
|
|
855fd07d22 | ||
|
|
f9be2bab3a | ||
|
|
25f1643e8e | ||
|
|
e015bea462 | ||
|
|
7dc55ac3ca | ||
|
|
cd8ecf9722 | ||
|
|
eb021a5f92 | ||
|
|
7f5e30834f | ||
|
|
750a936ae1 | ||
|
|
8dfef670b3 | ||
|
|
1b1b73b5b3 | ||
|
|
6baee0791f | ||
|
|
291b65977c | ||
|
|
90f232d7f1 | ||
|
|
af214d35cb | ||
|
|
3f0afd7cf6 | ||
|
|
0545c5da2d | ||
|
|
4a32fa6f02 | ||
|
|
29c99ed4ab | ||
|
|
753abbe164 | ||
|
|
8e01f6cc13 | ||
|
|
33c0b125cb | ||
|
|
dab2e54df8 | ||
|
|
60db171b44 | ||
|
|
c6e9a5c800 | ||
|
|
2c16b9fa61 | ||
|
|
240ad31edd | ||
|
|
a97631f769 | ||
|
|
dbaac79039 | ||
|
|
a05915ddc8 | ||
|
|
eebbd73346 | ||
|
|
d4c981495a | ||
|
|
653c206688 | ||
|
|
580f46b589 | ||
|
|
986d12fd20 | ||
|
|
d04a72a4ad | ||
|
|
5fd873a35a | ||
|
|
a9fbd786b3 | ||
|
|
abde984b3e | ||
|
|
a95aa037a3 | ||
|
|
11a92b24c2 | ||
|
|
f9c10c62d8 | ||
|
|
6339f39871 | ||
|
|
68b09b30a1 | ||
|
|
92ade2a320 | ||
|
|
cb1a1fb26c | ||
|
|
af5ebabd03 | ||
|
|
fe2626a4ea | ||
|
|
45447e3336 | ||
|
|
7a3e82ec5d | ||
|
|
345f4801e8 | ||
|
|
ac4b8d62e3 | ||
|
|
236ce7a8c0 | ||
|
|
8bdc0c8f79 | ||
|
|
04650f01fe | ||
|
|
02d4594abf | ||
|
|
22375fb7ef | ||
|
|
a9542e282c | ||
|
|
c1894b4e3d | ||
|
|
2062247e72 | ||
|
|
8785bec29c | ||
|
|
d4b7f75ce3 | ||
|
|
4f73d58031 | ||
|
|
b906f2de88 | ||
|
|
4035afe5c8 | ||
|
|
8fe0715928 | ||
|
|
cb8af962cd | ||
|
|
c333ffa38b | ||
|
|
3456f4ed80 | ||
|
|
2536e9f45b | ||
|
|
9188bc542c | ||
|
|
cbaba10994 | ||
|
|
85d3604309 | ||
|
|
507ba644cf | ||
|
|
3d6f62746a | ||
|
|
2f48c8c05f | ||
|
|
4828fd1eac | ||
|
|
10375263ef | ||
|
|
ae00001aa0 | ||
|
|
f53ebafbab | ||
|
|
23ebc50da9 | ||
|
|
673c6f97b7 | ||
|
|
ec46f71258 | ||
|
|
8865e524cb | ||
|
|
36bb02ae45 | ||
|
|
5072331f04 | ||
|
|
9d48fd4bbd | ||
|
|
bf66390557 | ||
|
|
184643f0db | ||
|
|
1bce898ca7 | ||
|
|
8c895570c6 | ||
|
|
6dc4e5ac93 | ||
|
|
d3922f0965 | ||
|
|
cfaac9f2e1 | ||
|
|
0b046d6cf0 | ||
|
|
3d822e5f79 | ||
|
|
f9cef22a53 | ||
|
|
b5d7d3dec1 | ||
|
|
182630e0d7 | ||
|
|
c81506b28d | ||
|
|
6c40bfe043 | ||
|
|
9caaae6a18 | ||
|
|
ad6a5e6157 | ||
|
|
7dd8ea58c2 | ||
|
|
3b261e0125 | ||
|
|
426791f68a | ||
|
|
c7cade2494 | ||
|
|
8f6c8844d7 | ||
|
|
da6e0e60c0 | ||
|
|
d89b567b47 | ||
|
|
34eb03f5b8 | ||
|
|
2f6d15a51e | ||
|
|
8ffea80980 | ||
|
|
c87d61b561 | ||
|
|
35c12e2053 | ||
|
|
33d8bfc937 | ||
|
|
f2343a6794 | ||
|
|
bab000eeb5 | ||
|
|
8e674ae053 | ||
|
|
6a4f4009d5 | ||
|
|
5e79b95927 | ||
|
|
a7a2bbb497 | ||
|
|
6e93d14bdb | ||
|
|
f29f284b3e | ||
|
|
b1b8f6cf71 | ||
|
|
4c3336bbe7 | ||
|
|
354ac0b493 | ||
|
|
1d159c6858 | ||
|
|
d70639b256 | ||
|
|
e4a92f0084 | ||
|
|
fdf5a70a27 | ||
|
|
f71da42520 | ||
|
|
f6bdeb9e3a | ||
|
|
2400354bab | ||
|
|
db348c46cc | ||
|
|
49567fe61a | ||
|
|
e5b3f796e4 | ||
|
|
a9700c8773 | ||
|
|
26cf5e003e | ||
|
|
742cf10dee | ||
|
|
7664453f94 | ||
|
|
460672aa93 | ||
|
|
b4e4fd9807 | ||
|
|
34bdfd0937 | ||
|
|
84591ca8ad | ||
|
|
fd4d0c5c0b | ||
|
|
9f5db46911 | ||
|
|
755ddbb223 | ||
|
|
701d470d01 | ||
|
|
1d9058d26b | ||
|
|
39e2a5f595 | ||
|
|
f862ab6722 | ||
|
|
129d4f0b1b | ||
|
|
3a1e50d1f8 | ||
|
|
e2fb690d8e | ||
|
|
0a7f58a811 | ||
|
|
dae0168ed8 | ||
|
|
edfe2e4f1c | ||
|
|
1bc1ea8b47 | ||
|
|
dacbbe3184 | ||
|
|
89285d8f5f | ||
|
|
2e853911c3 | ||
|
|
695fdecf23 | ||
|
|
054d22791d | ||
|
|
4a57cc69d8 | ||
|
|
7e0c8db029 | ||
|
|
ba4cc3bf86 | ||
|
|
b19a424c85 | ||
|
|
1689281c35 | ||
|
|
cdbb59fae8 | ||
|
|
4eb311e98f | ||
|
|
80eac96258 | ||
|
|
4bad6f9f1b | ||
|
|
d7db57e8e1 | ||
|
|
943fbf39a3 | ||
|
|
d8a34c2fcc | ||
|
|
5720ed1f44 | ||
|
|
bb20a359e4 | ||
|
|
0d472a49a0 | ||
|
|
203581e82f | ||
|
|
677631916c | ||
|
|
1aa1e8c904 | ||
|
|
55d62fbd9f | ||
|
|
e1ad2a355c | ||
|
|
4f318f913e | ||
|
|
2d814b6db2 | ||
|
|
e561f1ad68 | ||
|
|
ebfb985215 | ||
|
|
2646da50df | ||
|
|
50a5f6e53b | ||
|
|
d03fac52e7 | ||
|
|
6a802c01cd | ||
|
|
14146428dd | ||
|
|
26d0280f70 | ||
|
|
3274a5813e | ||
|
|
382905602c | ||
|
|
8b5cea7899 | ||
|
|
100c31cbb1 | ||
|
|
0b286f1b84 | ||
|
|
2f6ca958fe | ||
|
|
5218e7a546 | ||
|
|
7f8e799392 | ||
|
|
289f4abaaa | ||
|
|
7ce898ce43 | ||
|
|
0dd716a75e | ||
|
|
87171467fa | ||
|
|
b99afdad91 | ||
|
|
4fd576f3af | ||
|
|
2f41d0bedd | ||
|
|
5f03290534 | ||
|
|
427157c683 | ||
|
|
a0ab3d98b7 | ||
|
|
c8de766913 | ||
|
|
d57b963141 | ||
|
|
0ebcaff927 | ||
|
|
15931fa170 | ||
|
|
af4087d7b5 | ||
|
|
323ea1040c | ||
|
|
1fe87b0233 | ||
|
|
8d11df1b3b | ||
|
|
ecc5050838 | ||
|
|
606cf3b6f2 | ||
|
|
67cfd7f06b | ||
|
|
ab9ac7c87a | ||
|
|
ee9f979613 | ||
|
|
228b6444f8 | ||
|
|
9998efdae2 | ||
|
|
9427f56e1a | ||
|
|
a6dd35d73d | ||
|
|
faeaafa5f5 |
@@ -0,0 +1,70 @@
|
|||||||
|
name: Docs Update
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run every 4 hours
|
||||||
|
- cron: "0 */4 * * *"
|
||||||
|
workflow_dispatch: # Allow manual trigger for testing
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-docs:
|
||||||
|
if: github.repository == 'sst/opencode'
|
||||||
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Fetch full history to access commits
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: ./.github/actions/setup-bun
|
||||||
|
|
||||||
|
- name: Get recent commits
|
||||||
|
id: commits
|
||||||
|
run: |
|
||||||
|
COMMITS=$(git log --since="4 hours ago" --pretty=format:"- %h %s" 2>/dev/null || echo "")
|
||||||
|
if [ -z "$COMMITS" ]; then
|
||||||
|
echo "No commits in the last 4 hours"
|
||||||
|
echo "has_commits=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "has_commits=true" >> $GITHUB_OUTPUT
|
||||||
|
{
|
||||||
|
echo "list<<EOF"
|
||||||
|
echo "$COMMITS"
|
||||||
|
echo "EOF"
|
||||||
|
} >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run opencode
|
||||||
|
if: steps.commits.outputs.has_commits == 'true'
|
||||||
|
uses: sst/opencode/github@latest
|
||||||
|
env:
|
||||||
|
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||||
|
with:
|
||||||
|
model: opencode/gpt-5.2
|
||||||
|
agent: docs
|
||||||
|
prompt: |
|
||||||
|
Review the following commits from the last 4 hours and identify any new features that may need documentation.
|
||||||
|
|
||||||
|
<recent_commits>
|
||||||
|
${{ steps.commits.outputs.list }}
|
||||||
|
</recent_commits>
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. For each commit that looks like a new feature or significant change:
|
||||||
|
- Read the changed files to understand what was added
|
||||||
|
- Check if the feature is already documented in packages/web/src/content/docs/*
|
||||||
|
2. If you find undocumented features:
|
||||||
|
- Update the relevant documentation files in packages/web/src/content/docs/*
|
||||||
|
- Follow the existing documentation style and structure
|
||||||
|
- Make sure to document the feature clearly with examples where appropriate
|
||||||
|
3. If all new features are already documented, report that no updates are needed
|
||||||
|
4. If you are creating a new documentation file be sure to update packages/web/astro.config.mjs too.
|
||||||
|
|
||||||
|
Focus on user-facing features and API changes. Skip internal refactors, bug fixes, and test updates unless they affect user-facing behavior.
|
||||||
|
Don't feel the need to document every little thing. It is perfectly okay to make 0 changes at all.
|
||||||
|
Try to keep documentation only for large features or changes that already have a good spot to be documented.
|
||||||
@@ -2,11 +2,8 @@ name: generate
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches:
|
||||||
- production
|
- dev
|
||||||
pull_request:
|
|
||||||
branches-ignore:
|
|
||||||
- production
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -14,6 +11,7 @@ jobs:
|
|||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -25,14 +23,29 @@ jobs:
|
|||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: ./.github/actions/setup-bun
|
uses: ./.github/actions/setup-bun
|
||||||
|
|
||||||
- name: Generate SDK
|
- name: Generate
|
||||||
run: |
|
run: ./script/generate.ts
|
||||||
bun ./packages/sdk/js/script/build.ts
|
|
||||||
(cd packages/opencode && bun dev generate > ../sdk/openapi.json)
|
|
||||||
bun x prettier --write packages/sdk/openapi.json
|
|
||||||
|
|
||||||
- name: Format
|
- name: Commit and push
|
||||||
run: ./script/format.ts
|
run: |
|
||||||
env:
|
if [ -z "$(git status --porcelain)" ]; then
|
||||||
CI: true
|
echo "No changes to commit"
|
||||||
PUSH_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }}
|
exit 0
|
||||||
|
fi
|
||||||
|
git config --local user.email "action@github.com"
|
||||||
|
git config --local user.name "GitHub Action"
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore: generate"
|
||||||
|
git push origin HEAD:${{ github.ref_name }} --no-verify
|
||||||
|
# if ! git push origin HEAD:${{ github.event.pull_request.head.ref || github.ref_name }} --no-verify; then
|
||||||
|
# echo ""
|
||||||
|
# echo "============================================"
|
||||||
|
# echo "Failed to push generated code."
|
||||||
|
# echo "Please run locally and push:"
|
||||||
|
# echo ""
|
||||||
|
# echo " ./script/generate.ts"
|
||||||
|
# echo " git add -A && git commit -m \"chore: generate\" && git push"
|
||||||
|
# echo ""
|
||||||
|
# echo "============================================"
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|||||||
@@ -41,21 +41,9 @@ jobs:
|
|||||||
|
|
||||||
- uses: ./.github/actions/setup-bun
|
- uses: ./.github/actions/setup-bun
|
||||||
|
|
||||||
- name: Setup SSH for AUR
|
|
||||||
if: inputs.bump || inputs.version
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y pacman-package-manager
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa
|
|
||||||
chmod 600 ~/.ssh/id_rsa
|
|
||||||
git config --global user.email "opencode@sst.dev"
|
|
||||||
git config --global user.name "opencode"
|
|
||||||
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true
|
|
||||||
|
|
||||||
- name: Install OpenCode
|
- name: Install OpenCode
|
||||||
if: inputs.bump || inputs.version
|
if: inputs.bump || inputs.version
|
||||||
run: bun i -g opencode-ai@1.0.143
|
run: bun i -g opencode-ai@1.0.169
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -75,9 +63,15 @@ jobs:
|
|||||||
node-version: "24"
|
node-version: "24"
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- name: Setup Git Identity
|
||||||
|
run: |
|
||||||
|
git config --global user.email "opencode@sst.dev"
|
||||||
|
git config --global user.name "opencode"
|
||||||
|
git remote set-url origin https://x-access-token:${{ secrets.SST_GITHUB_TOKEN }}@github.com/${{ github.repository }}
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
id: publish
|
id: publish
|
||||||
run: ./script/publish.ts
|
run: ./script/publish-start.ts
|
||||||
env:
|
env:
|
||||||
OPENCODE_BUMP: ${{ inputs.bump }}
|
OPENCODE_BUMP: ${{ inputs.bump }}
|
||||||
OPENCODE_VERSION: ${{ inputs.version }}
|
OPENCODE_VERSION: ${{ inputs.version }}
|
||||||
@@ -85,9 +79,16 @@ jobs:
|
|||||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||||
NPM_CONFIG_PROVENANCE: false
|
NPM_CONFIG_PROVENANCE: false
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: opencode-cli
|
||||||
|
path: packages/opencode/dist
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
releaseId: ${{ steps.publish.outputs.releaseId }}
|
release: ${{ steps.publish.outputs.release }}
|
||||||
tagName: ${{ steps.publish.outputs.tagName }}
|
tag: ${{ steps.publish.outputs.tag }}
|
||||||
|
version: ${{ steps.publish.outputs.version }}
|
||||||
|
|
||||||
publish-tauri:
|
publish-tauri:
|
||||||
needs: publish
|
needs: publish
|
||||||
@@ -104,12 +105,14 @@ jobs:
|
|||||||
target: x86_64-pc-windows-msvc
|
target: x86_64-pc-windows-msvc
|
||||||
- host: blacksmith-4vcpu-ubuntu-2404
|
- host: blacksmith-4vcpu-ubuntu-2404
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
|
- host: blacksmith-4vcpu-ubuntu-2404-arm
|
||||||
|
target: aarch64-unknown-linux-gnu
|
||||||
runs-on: ${{ matrix.settings.host }}
|
runs-on: ${{ matrix.settings.host }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ needs.publish.outputs.tagName }}
|
ref: ${{ needs.publish.outputs.tag }}
|
||||||
|
|
||||||
- uses: apple-actions/import-codesign-certs@v2
|
- uses: apple-actions/import-codesign-certs@v2
|
||||||
if: ${{ runner.os == 'macOS' }}
|
if: ${{ runner.os == 'macOS' }}
|
||||||
@@ -156,16 +159,14 @@ jobs:
|
|||||||
cd packages/tauri
|
cd packages/tauri
|
||||||
bun ./scripts/prepare.ts
|
bun ./scripts/prepare.ts
|
||||||
env:
|
env:
|
||||||
OPENCODE_BUMP: ${{ inputs.bump }}
|
OPENCODE_VERSION: ${{ needs.publish.outputs.version }}
|
||||||
OPENCODE_VERSION: ${{ inputs.version }}
|
|
||||||
OPENCODE_CHANNEL: latest
|
|
||||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||||
RUST_TARGET: ${{ matrix.settings.target }}
|
RUST_TARGET: ${{ matrix.settings.target }}
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
OPENCODE_RELEASE_TAG: ${{ needs.publish.outputs.tagName }}
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||||
|
|
||||||
# Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released
|
# Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released
|
||||||
- name: Install tauri-cli from portable appimage branch
|
- name: Install tauri-cli from portable appimage branch
|
||||||
@@ -193,10 +194,10 @@ jobs:
|
|||||||
projectPath: packages/tauri
|
projectPath: packages/tauri
|
||||||
uploadWorkflowArtifacts: true
|
uploadWorkflowArtifacts: true
|
||||||
tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
|
tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
|
||||||
args: --target ${{ matrix.settings.target }} --config src-tauri/tauri.prod.conf.json
|
args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose
|
||||||
updaterJsonPreferNsis: true
|
updaterJsonPreferNsis: true
|
||||||
releaseId: ${{ needs.publish.outputs.releaseId }}
|
releaseId: ${{ needs.publish.outputs.release }}
|
||||||
tagName: ${{ needs.publish.outputs.tagName }}
|
tagName: ${{ needs.publish.outputs.tag }}
|
||||||
releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext]
|
releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext]
|
||||||
releaseDraft: true
|
releaseDraft: true
|
||||||
|
|
||||||
@@ -204,14 +205,29 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- publish
|
- publish
|
||||||
- publish-tauri
|
- publish-tauri
|
||||||
if: needs.publish.outputs.tagName
|
if: needs.publish.outputs.tag
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ needs.publish.outputs.tagName }}
|
ref: ${{ needs.publish.outputs.tag }}
|
||||||
|
|
||||||
- run: gh release edit ${{ needs.publish.outputs.tagName }} --draft=false
|
- uses: ./.github/actions/setup-bun
|
||||||
|
|
||||||
|
- name: Setup SSH for AUR
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y pacman-package-manager
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
git config --global user.email "opencode@sst.dev"
|
||||||
|
git config --global user.name "opencode"
|
||||||
|
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true
|
||||||
|
|
||||||
|
- run: ./script/publish-complete.ts
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
OPENCODE_VERSION: ${{ needs.publish.outputs.version }}
|
||||||
|
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ jobs:
|
|||||||
When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simpliest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts)
|
When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simpliest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts)
|
||||||
|
|
||||||
Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block.
|
Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block.
|
||||||
|
If you are writing suggested fixes, BE SURE THAT the change you are recommending is actually valid typescript, often I have seen missing closing "}" or other syntax errors.
|
||||||
|
Generally, write a comment instead of writing suggested change if you can help it.
|
||||||
|
|
||||||
Command MUST be like this.
|
Command MUST be like this.
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ on:
|
|||||||
- cron: "0 12 * * *" # Run daily at 12:00 UTC
|
- cron: "0 12 * * *" # Run daily at 12:00 UTC
|
||||||
workflow_dispatch: # Allow manual trigger
|
workflow_dispatch: # Allow manual trigger
|
||||||
|
|
||||||
|
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stats:
|
stats:
|
||||||
|
if: github.repository == 'sst/opencode'
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ TUI issues potentially caused by our underlying TUI library:
|
|||||||
|
|
||||||
**Do not** add for general TUI bugs.
|
**Do not** add for general TUI bugs.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
When assigning to people here are the following rules:
|
When assigning to people here are the following rules:
|
||||||
|
|
||||||
adamdotdev:
|
adamdotdev:
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
name: test-skill
|
||||||
|
description: use this when asked to test skill
|
||||||
|
---
|
||||||
|
|
||||||
|
woah this is a test skill
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/// <reference path="../env.d.ts" />
|
/// <reference path="../env.d.ts" />
|
||||||
import { Octokit } from "@octokit/rest"
|
// import { Octokit } from "@octokit/rest"
|
||||||
import { tool } from "@opencode-ai/plugin"
|
import { tool } from "@opencode-ai/plugin"
|
||||||
import DESCRIPTION from "./github-triage.txt"
|
import DESCRIPTION from "./github-triage.txt"
|
||||||
|
|
||||||
@@ -9,6 +9,22 @@ function getIssueNumber(): number {
|
|||||||
return issue
|
return issue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function githubFetch(endpoint: string, options: RequestInit = {}) {
|
||||||
|
const response = await fetch(`https://api.github.com${endpoint}`, {
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
|
|
||||||
export default tool({
|
export default tool({
|
||||||
description: DESCRIPTION,
|
description: DESCRIPTION,
|
||||||
args: {
|
args: {
|
||||||
@@ -23,7 +39,7 @@ export default tool({
|
|||||||
},
|
},
|
||||||
async execute(args) {
|
async execute(args) {
|
||||||
const issue = getIssueNumber()
|
const issue = getIssueNumber()
|
||||||
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
|
// const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
|
||||||
const owner = "sst"
|
const owner = "sst"
|
||||||
const repo = "opencode"
|
const repo = "opencode"
|
||||||
|
|
||||||
@@ -41,22 +57,30 @@ export default tool({
|
|||||||
throw new Error("Only opentui issues should be assigned to kommander")
|
throw new Error("Only opentui issues should be assigned to kommander")
|
||||||
}
|
}
|
||||||
|
|
||||||
await octokit.rest.issues.addAssignees({
|
// await octokit.rest.issues.addAssignees({
|
||||||
owner,
|
// owner,
|
||||||
repo,
|
// repo,
|
||||||
issue_number: issue,
|
// issue_number: issue,
|
||||||
assignees: [args.assignee],
|
// assignees: [args.assignee],
|
||||||
|
// })
|
||||||
|
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ assignees: [args.assignee] }),
|
||||||
})
|
})
|
||||||
results.push(`Assigned @${args.assignee} to issue #${issue}`)
|
results.push(`Assigned @${args.assignee} to issue #${issue}`)
|
||||||
|
|
||||||
const labels: string[] = args.labels.map((label) => (label === "desktop" ? "web" : label))
|
const labels: string[] = args.labels.map((label) => (label === "desktop" ? "web" : label))
|
||||||
|
|
||||||
if (labels.length > 0) {
|
if (labels.length > 0) {
|
||||||
await octokit.rest.issues.addLabels({
|
// await octokit.rest.issues.addLabels({
|
||||||
owner,
|
// owner,
|
||||||
repo,
|
// repo,
|
||||||
issue_number: issue,
|
// issue_number: issue,
|
||||||
labels,
|
// labels,
|
||||||
|
// })
|
||||||
|
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ labels }),
|
||||||
})
|
})
|
||||||
results.push(`Added labels: ${args.labels.join(", ")}`)
|
results.push(`Added labels: ${args.labels.join(", ")}`)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ Want to take on an issue? Leave a comment and a maintainer may assign it to you
|
|||||||
- `packages/plugin`: Source for `@opencode-ai/plugin`
|
- `packages/plugin`: Source for `@opencode-ai/plugin`
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> After touching `packages/opencode/src/server/server.ts`, run "./packages/sdk/js/script/build.ts" to regenerate the JS sdk.
|
> If you make changes to the API or SDK (e.g. `packages/opencode/src/server/server.ts`), run `./script/generate.ts` to regenerate the SDK and related files.
|
||||||
|
|
||||||
Please try to follow the [style guide](./STYLE_GUIDE.md)
|
Please try to follow the [style guide](./STYLE_GUIDE.md)
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ scoop bucket add extras; scoop install extras/opencode # Windows
|
|||||||
choco install opencode # Windows
|
choco install opencode # Windows
|
||||||
brew install opencode # macOS and Linux
|
brew install opencode # macOS and Linux
|
||||||
paru -S opencode-bin # Arch Linux
|
paru -S opencode-bin # Arch Linux
|
||||||
mise use -g ubi:sst/opencode # Any OS
|
mise use -g github:sst/opencode # Any OS
|
||||||
nix run nixpkgs#opencode # or github:sst/opencode for latest dev branch
|
nix run nixpkgs#opencode # or github:sst/opencode for latest dev branch
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://opencode.ai">
|
||||||
|
<picture>
|
||||||
|
<source srcset="packages/console/app/src/asset/logo-ornate-dark.svg" media="(prefers-color-scheme: dark)">
|
||||||
|
<source srcset="packages/console/app/src/asset/logo-ornate-light.svg" media="(prefers-color-scheme: light)">
|
||||||
|
<img src="packages/console/app/src/asset/logo-ornate-light.svg" alt="OpenCode logo">
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p align="center">開源的 AI Coding Agent。</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://opencode.ai/discord"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
|
||||||
|
<a href="https://www.npmjs.com/package/opencode-ai"><img alt="npm" src="https://img.shields.io/npm/v/opencode-ai?style=flat-square" /></a>
|
||||||
|
<a href="https://github.com/sst/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/sst/opencode/publish.yml?style=flat-square&branch=dev" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[](https://opencode.ai)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 安裝
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 直接安裝 (YOLO)
|
||||||
|
curl -fsSL https://opencode.ai/install | bash
|
||||||
|
|
||||||
|
# 套件管理員
|
||||||
|
npm i -g opencode-ai@latest # 也可使用 bun/pnpm/yarn
|
||||||
|
scoop bucket add extras; scoop install extras/opencode # Windows
|
||||||
|
choco install opencode # Windows
|
||||||
|
brew install opencode # macOS 與 Linux
|
||||||
|
paru -S opencode-bin # Arch Linux
|
||||||
|
mise use -g github:sst/opencode # 任何作業系統
|
||||||
|
nix run nixpkgs#opencode # 或使用 github:sst/opencode 以取得最新開發分支
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> 安裝前請先移除 0.1.x 以前的舊版本。
|
||||||
|
|
||||||
|
### 桌面應用程式 (BETA)
|
||||||
|
|
||||||
|
OpenCode 也提供桌面版應用程式。您可以直接從 [發佈頁面 (releases page)](https://github.com/sst/opencode/releases) 或 [opencode.ai/download](https://opencode.ai/download) 下載。
|
||||||
|
|
||||||
|
| 平台 | 下載連結 |
|
||||||
|
| --------------------- | ------------------------------------- |
|
||||||
|
| macOS (Apple Silicon) | `opencode-desktop-darwin-aarch64.dmg` |
|
||||||
|
| macOS (Intel) | `opencode-desktop-darwin-x64.dmg` |
|
||||||
|
| Windows | `opencode-desktop-windows-x64.exe` |
|
||||||
|
| Linux | `.deb`, `.rpm`, 或 AppImage |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# macOS (Homebrew Cask)
|
||||||
|
brew install --cask opencode-desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 安裝目錄
|
||||||
|
|
||||||
|
安裝腳本會依據以下優先順序決定安裝路徑:
|
||||||
|
|
||||||
|
1. `$OPENCODE_INSTALL_DIR` - 自定義安裝目錄
|
||||||
|
2. `$XDG_BIN_DIR` - 符合 XDG 基礎目錄規範的路徑
|
||||||
|
3. `$HOME/bin` - 標準使用者執行檔目錄 (若存在或可建立)
|
||||||
|
4. `$HOME/.opencode/bin` - 預設備用路徑
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 範例
|
||||||
|
OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash
|
||||||
|
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Agents
|
||||||
|
|
||||||
|
OpenCode 內建了兩種 Agent,您可以使用 `Tab` 鍵快速切換。
|
||||||
|
|
||||||
|
- **build** - 預設模式,具備完整權限的 Agent,適用於開發工作。
|
||||||
|
- **plan** - 唯讀模式,適用於程式碼分析與探索。
|
||||||
|
- 預設禁止修改檔案。
|
||||||
|
- 執行 bash 指令前會詢問權限。
|
||||||
|
- 非常適合用來探索陌生的程式碼庫或規劃變更。
|
||||||
|
|
||||||
|
此外,OpenCode 還包含一個 **general** 子 Agent,用於處理複雜搜尋與多步驟任務。此 Agent 供系統內部使用,亦可透過在訊息中輸入 `@general` 來呼叫。
|
||||||
|
|
||||||
|
了解更多關於 [Agents](https://opencode.ai/docs/agents) 的資訊。
|
||||||
|
|
||||||
|
### 線上文件
|
||||||
|
|
||||||
|
關於如何設定 OpenCode 的詳細資訊,請參閱我們的 [**官方文件**](https://opencode.ai/docs)。
|
||||||
|
|
||||||
|
### 參與貢獻
|
||||||
|
|
||||||
|
如果您有興趣參與 OpenCode 的開發,請在提交 Pull Request 前先閱讀我們的 [貢獻指南 (Contributing Docs)](./CONTRIBUTING.md)。
|
||||||
|
|
||||||
|
### 基於 OpenCode 進行開發
|
||||||
|
|
||||||
|
如果您正在開發與 OpenCode 相關的專案,並在名稱中使用了 "opencode"(例如 "opencode-dashboard" 或 "opencode-mobile"),請在您的 README 中加入聲明,說明該專案並非由 OpenCode 團隊開發,且與我們沒有任何隸屬關係。
|
||||||
|
|
||||||
|
### 常見問題 (FAQ)
|
||||||
|
|
||||||
|
#### 這跟 Claude Code 有什麼不同?
|
||||||
|
|
||||||
|
在功能面上與 Claude Code 非常相似。以下是關鍵差異:
|
||||||
|
|
||||||
|
- 100% 開源。
|
||||||
|
- 不綁定特定的服務提供商。雖然我們推薦使用透過 [OpenCode Zen](https://opencode.ai/zen) 提供的模型,但 OpenCode 也可搭配 Claude, OpenAI, Google 甚至本地模型使用。隨著模型不斷演進,彼此間的差距會縮小且價格會下降,因此具備「不限廠商 (provider-agnostic)」的特性至關重要。
|
||||||
|
- 內建 LSP (語言伺服器協定) 支援。
|
||||||
|
- 專注於終端機介面 (TUI)。OpenCode 由 Neovim 愛好者與 [terminal.shop](https://terminal.shop) 的創作者打造;我們將不斷挑戰終端機介面的極限。
|
||||||
|
- 客戶端/伺服器架構 (Client/Server Architecture)。這讓 OpenCode 能夠在您的電腦上運行的同時,由行動裝置進行遠端操控。這意味著 TUI 前端只是眾多可能的客戶端之一。
|
||||||
|
|
||||||
|
#### 另一個同名的 Repo 是什麼?
|
||||||
|
|
||||||
|
另一個名稱相近的儲存庫與本專案無關。您可以點此[閱讀背後的故事](https://x.com/thdxr/status/1933561254481666466)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**加入我們的社群** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
|
||||||
@@ -174,3 +174,7 @@
|
|||||||
| 2025-12-16 | 1,120,477 (+26,845) | 1,078,022 (+18,944) | 2,198,499 (+45,789) |
|
| 2025-12-16 | 1,120,477 (+26,845) | 1,078,022 (+18,944) | 2,198,499 (+45,789) |
|
||||||
| 2025-12-17 | 1,151,067 (+30,590) | 1,097,661 (+19,639) | 2,248,728 (+50,229) |
|
| 2025-12-17 | 1,151,067 (+30,590) | 1,097,661 (+19,639) | 2,248,728 (+50,229) |
|
||||||
| 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) |
|
| 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) |
|
||||||
|
| 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) |
|
||||||
|
| 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) |
|
||||||
|
| 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) |
|
||||||
|
| 2025-12-22 | 1,262,522 (+19,847) | 1,169,121 (+10,212) | 2,431,643 (+30,059) |
|
||||||
|
|||||||
@@ -5,13 +5,21 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ai-sdk/cerebras": "1.0.33",
|
||||||
|
"@ai-sdk/cohere": "2.0.21",
|
||||||
|
"@ai-sdk/deepinfra": "1.0.30",
|
||||||
|
"@ai-sdk/gateway": "2.0.23",
|
||||||
|
"@ai-sdk/groq": "2.0.33",
|
||||||
|
"@ai-sdk/perplexity": "2.0.22",
|
||||||
|
"@ai-sdk/togetherai": "1.0.30",
|
||||||
"@aws-sdk/client-s3": "3.933.0",
|
"@aws-sdk/client-s3": "3.933.0",
|
||||||
"@octokit/rest": "22.0.1",
|
"@opencode-ai/plugin": "workspace:*",
|
||||||
"@opencode-ai/script": "workspace:*",
|
"@opencode-ai/script": "workspace:*",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@actions/artifact": "5.0.1",
|
||||||
"@tsconfig/bun": "catalog:",
|
"@tsconfig/bun": "catalog:",
|
||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
@@ -21,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/app": {
|
"packages/console/app": {
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cloudflare/vite-plugin": "1.15.2",
|
"@cloudflare/vite-plugin": "1.15.2",
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
@@ -49,7 +57,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/core": {
|
"packages/console/core": {
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sts": "3.782.0",
|
"@aws-sdk/client-sts": "3.782.0",
|
||||||
"@jsx-email/render": "1.1.1",
|
"@jsx-email/render": "1.1.1",
|
||||||
@@ -76,7 +84,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/function": {
|
"packages/console/function": {
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "2.0.0",
|
"@ai-sdk/anthropic": "2.0.0",
|
||||||
"@ai-sdk/openai": "2.0.2",
|
"@ai-sdk/openai": "2.0.2",
|
||||||
@@ -100,7 +108,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/mail": {
|
"packages/console/mail": {
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
@@ -124,7 +132,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -154,6 +162,7 @@
|
|||||||
"solid-list": "catalog:",
|
"solid-list": "catalog:",
|
||||||
"tailwindcss": "catalog:",
|
"tailwindcss": "catalog:",
|
||||||
"virtua": "catalog:",
|
"virtua": "catalog:",
|
||||||
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@happy-dom/global-registrator": "20.0.11",
|
"@happy-dom/global-registrator": "20.0.11",
|
||||||
@@ -171,7 +180,7 @@
|
|||||||
},
|
},
|
||||||
"packages/enterprise": {
|
"packages/enterprise": {
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
@@ -200,7 +209,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "catalog:",
|
"@octokit/rest": "catalog:",
|
||||||
@@ -216,7 +225,7 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -230,10 +239,12 @@
|
|||||||
"@ai-sdk/google": "2.0.44",
|
"@ai-sdk/google": "2.0.44",
|
||||||
"@ai-sdk/google-vertex": "3.0.81",
|
"@ai-sdk/google-vertex": "3.0.81",
|
||||||
"@ai-sdk/mcp": "0.0.8",
|
"@ai-sdk/mcp": "0.0.8",
|
||||||
|
"@ai-sdk/mistral": "2.0.26",
|
||||||
"@ai-sdk/openai": "2.0.71",
|
"@ai-sdk/openai": "2.0.71",
|
||||||
"@ai-sdk/openai-compatible": "1.0.27",
|
"@ai-sdk/openai-compatible": "1.0.27",
|
||||||
"@ai-sdk/provider": "2.0.0",
|
"@ai-sdk/provider": "2.0.0",
|
||||||
"@ai-sdk/provider-utils": "3.0.18",
|
"@ai-sdk/provider-utils": "3.0.18",
|
||||||
|
"@ai-sdk/xai": "2.0.42",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
@@ -246,8 +257,8 @@
|
|||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
"@openrouter/ai-sdk-provider": "1.5.2",
|
"@openrouter/ai-sdk-provider": "1.5.2",
|
||||||
"@opentui/core": "0.1.61",
|
"@opentui/core": "0.1.63",
|
||||||
"@opentui/solid": "0.1.61",
|
"@opentui/solid": "0.1.63",
|
||||||
"@parcel/watcher": "2.5.1",
|
"@parcel/watcher": "2.5.1",
|
||||||
"@pierre/diffs": "catalog:",
|
"@pierre/diffs": "catalog:",
|
||||||
"@solid-primitives/event-bus": "1.1.2",
|
"@solid-primitives/event-bus": "1.1.2",
|
||||||
@@ -308,7 +319,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
@@ -328,7 +339,7 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hey-api/openapi-ts": "0.88.1",
|
"@hey-api/openapi-ts": "0.88.1",
|
||||||
"@tsconfig/node22": "catalog:",
|
"@tsconfig/node22": "catalog:",
|
||||||
@@ -339,7 +350,7 @@
|
|||||||
},
|
},
|
||||||
"packages/slack": {
|
"packages/slack": {
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@slack/bolt": "^3.17.1",
|
"@slack/bolt": "^3.17.1",
|
||||||
@@ -352,12 +363,13 @@
|
|||||||
},
|
},
|
||||||
"packages/tauri": {
|
"packages/tauri": {
|
||||||
"name": "@opencode-ai/tauri",
|
"name": "@opencode-ai/tauri",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/desktop": "workspace:*",
|
"@opencode-ai/desktop": "workspace:*",
|
||||||
"@solid-primitives/storage": "catalog:",
|
"@solid-primitives/storage": "catalog:",
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-dialog": "~2",
|
"@tauri-apps/plugin-dialog": "~2",
|
||||||
|
"@tauri-apps/plugin-http": "~2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"@tauri-apps/plugin-os": "~2",
|
"@tauri-apps/plugin-os": "~2",
|
||||||
"@tauri-apps/plugin-process": "~2",
|
"@tauri-apps/plugin-process": "~2",
|
||||||
@@ -378,7 +390,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -413,7 +425,7 @@
|
|||||||
},
|
},
|
||||||
"packages/util": {
|
"packages/util": {
|
||||||
"name": "@opencode-ai/util",
|
"name": "@opencode-ai/util",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -424,7 +436,7 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cloudflare": "12.6.3",
|
"@astrojs/cloudflare": "12.6.3",
|
||||||
"@astrojs/markdown-remark": "6.3.1",
|
"@astrojs/markdown-remark": "6.3.1",
|
||||||
@@ -504,7 +516,7 @@
|
|||||||
"zod": "4.1.8",
|
"zod": "4.1.8",
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@actions/artifact": ["@actions/artifact@4.0.0", "", { "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^6.0.1", "@actions/http-client": "^2.1.0", "@azure/core-http": "^3.0.5", "@azure/storage-blob": "^12.15.0", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-HCc2jMJRAfviGFAh0FsOR/jNfWhirxl7W6z8zDtttt0GltwxBLdEIjLiweOPFl9WbyJRW1VWnPUSAixJqcWUMQ=="],
|
"@actions/artifact": ["@actions/artifact@5.0.1", "", { "dependencies": { "@actions/core": "^2.0.0", "@actions/github": "^6.0.1", "@actions/http-client": "^3.0.0", "@azure/storage-blob": "^12.29.1", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-dHJ5rHduhCKUikKTT9eXeWoUvfKia3IjR1sO/VTAV3DVAL4yMTRnl2iO5mcfiBjySHLwPNezwENAVskKYU5ymw=="],
|
||||||
|
|
||||||
"@actions/core": ["@actions/core@1.11.1", "", { "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" } }, "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A=="],
|
"@actions/core": ["@actions/core@1.11.1", "", { "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" } }, "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A=="],
|
||||||
|
|
||||||
@@ -512,7 +524,7 @@
|
|||||||
|
|
||||||
"@actions/github": ["@actions/github@6.0.1", "", { "dependencies": { "@actions/http-client": "^2.2.0", "@octokit/core": "^5.0.1", "@octokit/plugin-paginate-rest": "^9.2.2", "@octokit/plugin-rest-endpoint-methods": "^10.4.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "undici": "^5.28.5" } }, "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw=="],
|
"@actions/github": ["@actions/github@6.0.1", "", { "dependencies": { "@actions/http-client": "^2.2.0", "@octokit/core": "^5.0.1", "@octokit/plugin-paginate-rest": "^9.2.2", "@octokit/plugin-rest-endpoint-methods": "^10.4.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "undici": "^5.28.5" } }, "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw=="],
|
||||||
|
|
||||||
"@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="],
|
"@actions/http-client": ["@actions/http-client@3.0.0", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.28.5" } }, "sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ=="],
|
||||||
|
|
||||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||||
|
|
||||||
@@ -526,22 +538,38 @@
|
|||||||
|
|
||||||
"@ai-sdk/azure": ["@ai-sdk/azure@2.0.73", "", { "dependencies": { "@ai-sdk/openai": "2.0.71", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LpAg3Ak/V3WOemBu35Qbx9jfQfApsHNXX9p3bXVsnRu3XXi1QQUt5gMOCIb4znPonz+XnHenIDZMBwdsb1TfRQ=="],
|
"@ai-sdk/azure": ["@ai-sdk/azure@2.0.73", "", { "dependencies": { "@ai-sdk/openai": "2.0.71", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LpAg3Ak/V3WOemBu35Qbx9jfQfApsHNXX9p3bXVsnRu3XXi1QQUt5gMOCIb4znPonz+XnHenIDZMBwdsb1TfRQ=="],
|
||||||
|
|
||||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="],
|
"@ai-sdk/cerebras": ["@ai-sdk/cerebras@1.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2gSSS/7kunIwMdC4td5oWsUAzoLw84ccGpz6wQbxVnrb1iWnrEnKa5tRBduaP6IXpzLWsu8wME3+dQhZy+gT7w=="],
|
||||||
|
|
||||||
|
"@ai-sdk/cohere": ["@ai-sdk/cohere@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZjaZFvJlc5XOPi3QwTLEFZbHIgTJc6YGvxz+8zIMGVZi/hdynR8/f/C1A9x6mhzmBtAqi/dZ2h11oouAQH5z4g=="],
|
||||||
|
|
||||||
|
"@ai-sdk/deepinfra": ["@ai-sdk/deepinfra@1.0.30", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XK8oRZFApzo6xnS5C+FhWUUkB2itA5Nfon3pU9dJVM0goViq8GwdleZTBRqhu4DE4KJURo5DGWpJr2hfV54cEg=="],
|
||||||
|
|
||||||
|
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-qmX7afPRszUqG5hryHF3UN8ITPIRSGmDW6VYCmByzjoUkgm3MekzSx2hMV1wr0P+llDeuXb378SjqUfpvWJulg=="],
|
||||||
|
|
||||||
"@ai-sdk/google": ["@ai-sdk/google@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw=="],
|
"@ai-sdk/google": ["@ai-sdk/google@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw=="],
|
||||||
|
|
||||||
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.81", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.50", "@ai-sdk/google": "2.0.44", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yrl5Ug0Mqwo9ya45oxczgy2RWgpEA/XQQCSFYP+3NZMQ4yA3Iim1vkOjVCsGaZZ8rjVk395abi1ZMZV0/6rqVA=="],
|
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.81", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.50", "@ai-sdk/google": "2.0.44", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yrl5Ug0Mqwo9ya45oxczgy2RWgpEA/XQQCSFYP+3NZMQ4yA3Iim1vkOjVCsGaZZ8rjVk395abi1ZMZV0/6rqVA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/groq": ["@ai-sdk/groq@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FWGl7xNr88NBveao3y9EcVWYUt9ABPrwLFY7pIutSNgaTf32vgvyhREobaMrLU4Scr5G/2tlNqOPZ5wkYMaZig=="],
|
||||||
|
|
||||||
"@ai-sdk/mcp": ["@ai-sdk/mcp@0.0.8", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q=="],
|
"@ai-sdk/mcp": ["@ai-sdk/mcp@0.0.8", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q=="],
|
||||||
|
|
||||||
|
"@ai-sdk/mistral": ["@ai-sdk/mistral@2.0.26", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jxDB++4WI1wEx5ONNBI+VbkmYJOYIuS8UQY13/83UGRaiW7oB/WHiH4ETe6KzbKpQPB3XruwTJQjUMsMfKyTXA=="],
|
||||||
|
|
||||||
"@ai-sdk/openai": ["@ai-sdk/openai@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw=="],
|
"@ai-sdk/openai": ["@ai-sdk/openai@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw=="],
|
||||||
|
|
||||||
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-luHVcU+yKzwv3ekKgbP3v+elUVxb2Rt+8c6w9qi7g2NYG2/pEL21oIrnaEnc6UtTZLLZX9EFBcpq2N1FQKDIMw=="],
|
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-luHVcU+yKzwv3ekKgbP3v+elUVxb2Rt+8c6w9qi7g2NYG2/pEL21oIrnaEnc6UtTZLLZX9EFBcpq2N1FQKDIMw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/perplexity": ["@ai-sdk/perplexity@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-zwzcnk08R2J3mZcQPn4Ifl4wYGrvANR7jsBB0hCTUSbb+Rx3ybpikSWiGuXQXxdiRc1I5MWXgj70m+bZaLPvHw=="],
|
||||||
|
|
||||||
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
||||||
|
|
||||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="],
|
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="],
|
||||||
|
|
||||||
|
"@ai-sdk/togetherai": ["@ai-sdk/togetherai@1.0.30", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9bxQbIXnWSN4bNismrza3NvIo+ui/Y3pj3UN6e9vCszCWFCN45RgISi4oDe10RqmzaJ/X8cfO/Tem+K8MT3wGQ=="],
|
||||||
|
|
||||||
|
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.42", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wlwO4yRoZ/d+ca29vN8SDzxus7POdnL7GBTyRdSrt6icUF0hooLesauC8qRUC4aLxtqvMEc1YHtJOU7ZnLWbTQ=="],
|
||||||
|
|
||||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||||
|
|
||||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||||
@@ -648,7 +676,7 @@
|
|||||||
|
|
||||||
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.1", "", {}, "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww=="],
|
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.1", "", {}, "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww=="],
|
||||||
|
|
||||||
"@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="],
|
"@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
||||||
|
|
||||||
"@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
|
"@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
|
||||||
|
|
||||||
@@ -664,7 +692,7 @@
|
|||||||
|
|
||||||
"@azure/core-rest-pipeline": ["@azure/core-rest-pipeline@1.22.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg=="],
|
"@azure/core-rest-pipeline": ["@azure/core-rest-pipeline@1.22.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg=="],
|
||||||
|
|
||||||
"@azure/core-tracing": ["@azure/core-tracing@1.0.0-preview.13", "", { "dependencies": { "@opentelemetry/api": "^1.0.1", "tslib": "^2.2.0" } }, "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ=="],
|
"@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
|
||||||
|
|
||||||
"@azure/core-util": ["@azure/core-util@1.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A=="],
|
"@azure/core-util": ["@azure/core-util@1.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A=="],
|
||||||
|
|
||||||
@@ -1086,11 +1114,11 @@
|
|||||||
|
|
||||||
"@octokit/auth-oauth-user": ["@octokit/auth-oauth-user@6.0.2", "", { "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/oauth-methods": "^6.0.2", "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A=="],
|
"@octokit/auth-oauth-user": ["@octokit/auth-oauth-user@6.0.2", "", { "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/oauth-methods": "^6.0.2", "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A=="],
|
||||||
|
|
||||||
"@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
|
"@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="],
|
||||||
|
|
||||||
"@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="],
|
"@octokit/core": ["@octokit/core@5.2.2", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg=="],
|
||||||
|
|
||||||
"@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
"@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="],
|
||||||
|
|
||||||
"@octokit/graphql": ["@octokit/graphql@9.0.2", "", { "dependencies": { "@octokit/request": "^10.0.4", "@octokit/types": "^15.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-iz6KzZ7u95Fzy9Nt2L8cG88lGRMr/qy1Q36ih/XVzMIlPDMYwaNLE/ENhqmIzgPrlNWiYJkwmveEetvxAgFBJw=="],
|
"@octokit/graphql": ["@octokit/graphql@9.0.2", "", { "dependencies": { "@octokit/request": "^10.0.4", "@octokit/types": "^15.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-iz6KzZ7u95Fzy9Nt2L8cG88lGRMr/qy1Q36ih/XVzMIlPDMYwaNLE/ENhqmIzgPrlNWiYJkwmveEetvxAgFBJw=="],
|
||||||
|
|
||||||
@@ -1100,19 +1128,19 @@
|
|||||||
|
|
||||||
"@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
"@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
||||||
|
|
||||||
"@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@14.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw=="],
|
"@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="],
|
||||||
|
|
||||||
"@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="],
|
"@octokit/plugin-request-log": ["@octokit/plugin-request-log@1.0.4", "", { "peerDependencies": { "@octokit/core": ">=3" } }, "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA=="],
|
||||||
|
|
||||||
"@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@17.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw=="],
|
"@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg=="],
|
||||||
|
|
||||||
"@octokit/plugin-retry": ["@octokit/plugin-retry@3.0.9", "", { "dependencies": { "@octokit/types": "^6.0.3", "bottleneck": "^2.15.3" } }, "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ=="],
|
"@octokit/plugin-retry": ["@octokit/plugin-retry@3.0.9", "", { "dependencies": { "@octokit/types": "^6.0.3", "bottleneck": "^2.15.3" } }, "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ=="],
|
||||||
|
|
||||||
"@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
"@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="],
|
||||||
|
|
||||||
"@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
"@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="],
|
||||||
|
|
||||||
"@octokit/rest": ["@octokit/rest@22.0.1", "", { "dependencies": { "@octokit/core": "^7.0.6", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0" } }, "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw=="],
|
"@octokit/rest": ["@octokit/rest@22.0.0", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="],
|
||||||
|
|
||||||
"@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
||||||
|
|
||||||
@@ -1160,21 +1188,21 @@
|
|||||||
|
|
||||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||||
|
|
||||||
"@opentui/core": ["@opentui/core@0.1.61", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.61", "@opentui/core-darwin-x64": "0.1.61", "@opentui/core-linux-arm64": "0.1.61", "@opentui/core-linux-x64": "0.1.61", "@opentui/core-win32-arm64": "0.1.61", "@opentui/core-win32-x64": "0.1.61", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-WrVbdki0tnsgmWCB3Iix6n8eXGXUheTqr/tcnBN7gLA/TqT9udcX+DW3/qRdgtTNJS1sVBVeuwSTYU3eqDSUJQ=="],
|
"@opentui/core": ["@opentui/core@0.1.63", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.63", "@opentui/core-darwin-x64": "0.1.63", "@opentui/core-linux-arm64": "0.1.63", "@opentui/core-linux-x64": "0.1.63", "@opentui/core-win32-arm64": "0.1.63", "@opentui/core-win32-x64": "0.1.63", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-m4xZQTNCnHXWUWCnGvacJ3Gts1H2aMwP5V/puAG77SDb51jm4W/QOyqAAdgeSakkb9II+8FfUpApX7sfwRXPUg=="],
|
||||||
|
|
||||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.61", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zX7EK8PwBJFwsZ2tDnScLFD0GbBfHE7sqpzGDXP2luMnBZJ0OOO95a4Hzu9dQWqxEr4RgfGDT8uIRhgimKNQEg=="],
|
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.63", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jKCThZGiiublKkP/hMtDtl1MLCw5NU0hMNJdEYvz1WLT9bzliWf6Kb7MIDAmk32XlbQW8/RHdp+hGyGDXK62OQ=="],
|
||||||
|
|
||||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.61", "", { "os": "darwin", "cpu": "x64" }, "sha512-xfvl8EnyN0XwlYpyTskVhHOpbMdgt++ntcuTh7M7IEFYQGzJux19NBwJl17mOxB1McG+KTa7kNx5/zu0VB9eVQ=="],
|
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.63", "", { "os": "darwin", "cpu": "x64" }, "sha512-rfNxynHzJpxN9i+SAMnn1NToEc8rYj64BsOxY78JNsm4Gg1Js1uyMaawwh2WbdGknFy4cDXS9QwkUMdMcfnjiw=="],
|
||||||
|
|
||||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.61", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ghg7j4H6bz7CLxhgDcWx3Ann3AblDIjKFUu4vFrVysuiwfmDHwdKm8awLj8tnmC/0y8juG4ODUQbR0BXBIkE+Q=="],
|
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.63", "", { "os": "linux", "cpu": "arm64" }, "sha512-wG9d6mHWWKZGrzxYS4c+BrcEGXBv/MYBUPSyjP/lD0CxT+X3h6CYhI317JkRyMNfh3vI9CpAKGFTOFvrTTHimQ=="],
|
||||||
|
|
||||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.61", "", { "os": "linux", "cpu": "x64" }, "sha512-Xs9czMEOuHtnX4tigC4fNb1MU7+Gaohbk+k4teraulIgYZf19nRHIKNvXissDjOfqvOGygCkxMQIG0zeUFsPEA=="],
|
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.63", "", { "os": "linux", "cpu": "x64" }, "sha512-TKSzFv4BgWW3RB/iZmq5qxTR4/tRaXo8IZNnVR+LFzShbPOqhUi466AByy9SUmCxD8uYjmMDFYfKtkCy0AnAwA=="],
|
||||||
|
|
||||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.61", "", { "os": "win32", "cpu": "arm64" }, "sha512-2CYAEPqArJqE36LkSRAs0csRzWwVJY99S/7EuY7abBm58BIL6RUw5kSw1r75oDo4I3W6v6WwW0u8B5Ik98m0Kg=="],
|
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.63", "", { "os": "win32", "cpu": "arm64" }, "sha512-CBWPyPognERP0Mq4eC1q01Ado2C2WU+BLTgMdhyt+E2P4w8rPhJ2kCt2MNxO66vQUiynspmZkgjQr0II/VjxWA=="],
|
||||||
|
|
||||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.61", "", { "os": "win32", "cpu": "x64" }, "sha512-c0OK5YwcKH51Qj6wPmwTZP3X8LHA0I0dKz4fO4mOh4f+OqgU9WOG4hpbf7lv0bVlHoTvgP4zDUsjmtIVA8l6Lg=="],
|
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.63", "", { "os": "win32", "cpu": "x64" }, "sha512-qEp6h//FrT+TQiiHm87wZWUwqTPTqIy1ZD+8R+VCUK+usoQiOAD2SqrYnM7W8JkCMGn5/TKm/GaKLyx/qlK4VA=="],
|
||||||
|
|
||||||
"@opentui/solid": ["@opentui/solid@0.1.61", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.61", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-CiZHduIoeABoS0ev+eGeHA/LiRl/SpdL6io4jrwiwFi/rToKtc7YgJ8MWxIgeHScHUbpQnIr1v7jzsGI3DAYvw=="],
|
"@opentui/solid": ["@opentui/solid@0.1.63", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.63", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-Gccln4qRucAoaoQEZ4NPAHvGmVYzU/8aKCLG8EPgwCKTcpUzlqYt4357cDHq4cnCNOcXOC06hTz/0pK9r0dqXA=="],
|
||||||
|
|
||||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||||
|
|
||||||
@@ -1672,6 +1700,8 @@
|
|||||||
|
|
||||||
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ=="],
|
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ=="],
|
||||||
|
|
||||||
|
"@tauri-apps/plugin-http": ["@tauri-apps/plugin-http@2.5.4", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-/i4U/9za3mrytTgfRn5RHneKubZE/dwRmshYwyMvNRlkWjvu1m4Ma72kcbVJMZFGXpkbl+qLyWMGrihtWB76Zg=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew=="],
|
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A=="],
|
"@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A=="],
|
||||||
@@ -1820,19 +1850,19 @@
|
|||||||
|
|
||||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
|
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
|
||||||
|
|
||||||
"@vitest/expect": ["@vitest/expect@4.0.13", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.13", "@vitest/utils": "4.0.13", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-zYtcnNIBm6yS7Gpr7nFTmq8ncowlMdOJkWLqYvhr/zweY6tFbDkDi8BPPOeHxEtK1rSI69H7Fd4+1sqvEGli6w=="],
|
"@vitest/expect": ["@vitest/expect@4.0.16", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.16", "@vitest/utils": "4.0.16", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA=="],
|
||||||
|
|
||||||
"@vitest/mocker": ["@vitest/mocker@4.0.13", "", { "dependencies": { "@vitest/spy": "4.0.13", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-eNCwzrI5djoauklwP1fuslHBjrbR8rqIVbvNlAnkq1OTa6XT+lX68mrtPirNM9TnR69XUPt4puBCx2Wexseylg=="],
|
"@vitest/mocker": ["@vitest/mocker@4.0.16", "", { "dependencies": { "@vitest/spy": "4.0.16", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg=="],
|
||||||
|
|
||||||
"@vitest/pretty-format": ["@vitest/pretty-format@4.0.13", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-ooqfze8URWbI2ozOeLDMh8YZxWDpGXoeY3VOgcDnsUxN0jPyPWSUvjPQWqDGCBks+opWlN1E4oP1UYl3C/2EQA=="],
|
"@vitest/pretty-format": ["@vitest/pretty-format@4.0.16", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA=="],
|
||||||
|
|
||||||
"@vitest/runner": ["@vitest/runner@4.0.13", "", { "dependencies": { "@vitest/utils": "4.0.13", "pathe": "^2.0.3" } }, "sha512-9IKlAru58wcVaWy7hz6qWPb2QzJTKt+IOVKjAx5vb5rzEFPTL6H4/R9BMvjZ2ppkxKgTrFONEJFtzvnyEpiT+A=="],
|
"@vitest/runner": ["@vitest/runner@4.0.16", "", { "dependencies": { "@vitest/utils": "4.0.16", "pathe": "^2.0.3" } }, "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q=="],
|
||||||
|
|
||||||
"@vitest/snapshot": ["@vitest/snapshot@4.0.13", "", { "dependencies": { "@vitest/pretty-format": "4.0.13", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-hb7Usvyika1huG6G6l191qu1urNPsq1iFc2hmdzQY3F5/rTgqQnwwplyf8zoYHkpt7H6rw5UfIw6i/3qf9oSxQ=="],
|
"@vitest/snapshot": ["@vitest/snapshot@4.0.16", "", { "dependencies": { "@vitest/pretty-format": "4.0.16", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA=="],
|
||||||
|
|
||||||
"@vitest/spy": ["@vitest/spy@4.0.13", "", {}, "sha512-hSu+m4se0lDV5yVIcNWqjuncrmBgwaXa2utFLIrBkQCQkt+pSwyZTPFQAZiiF/63j8jYa8uAeUZ3RSfcdWaYWw=="],
|
"@vitest/spy": ["@vitest/spy@4.0.16", "", {}, "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw=="],
|
||||||
|
|
||||||
"@vitest/utils": ["@vitest/utils@4.0.13", "", { "dependencies": { "@vitest/pretty-format": "4.0.13", "tinyrainbow": "^3.0.3" } }, "sha512-ydozWyQ4LZuu8rLp47xFUWis5VOKMdHjXCWhs1LuJsTNKww+pTHQNK4e0assIB9K80TxFyskENL6vCu3j34EYA=="],
|
"@vitest/utils": ["@vitest/utils@4.0.16", "", { "dependencies": { "@vitest/pretty-format": "4.0.16", "tinyrainbow": "^3.0.3" } }, "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA=="],
|
||||||
|
|
||||||
"@webgpu/types": ["@webgpu/types@0.1.66", "", {}, "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA=="],
|
"@webgpu/types": ["@webgpu/types@0.1.66", "", {}, "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA=="],
|
||||||
|
|
||||||
@@ -1966,7 +1996,7 @@
|
|||||||
|
|
||||||
"bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="],
|
"bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="],
|
||||||
|
|
||||||
"before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
"before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="],
|
||||||
|
|
||||||
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||||
|
|
||||||
@@ -2338,7 +2368,7 @@
|
|||||||
|
|
||||||
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
||||||
|
|
||||||
"events": ["events@1.1.1", "", {}, "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="],
|
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
||||||
|
|
||||||
"events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="],
|
"events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="],
|
||||||
|
|
||||||
@@ -2354,7 +2384,7 @@
|
|||||||
|
|
||||||
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
|
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
|
||||||
|
|
||||||
"expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
|
"expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
|
||||||
|
|
||||||
"express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="],
|
"express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="],
|
||||||
|
|
||||||
@@ -3084,6 +3114,8 @@
|
|||||||
|
|
||||||
"object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
|
"object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
|
||||||
|
|
||||||
|
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
|
||||||
|
|
||||||
"ofetch": ["ofetch@2.0.0-alpha.3", "", {}, "sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA=="],
|
"ofetch": ["ofetch@2.0.0-alpha.3", "", {}, "sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA=="],
|
||||||
|
|
||||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||||
@@ -3784,7 +3816,7 @@
|
|||||||
|
|
||||||
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
||||||
|
|
||||||
"vitest": ["vitest@4.0.13", "", { "dependencies": { "@vitest/expect": "4.0.13", "@vitest/mocker": "4.0.13", "@vitest/pretty-format": "4.0.13", "@vitest/runner": "4.0.13", "@vitest/snapshot": "4.0.13", "@vitest/spy": "4.0.13", "@vitest/utils": "4.0.13", "debug": "^4.4.3", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/debug": "^4.1.12", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.13", "@vitest/browser-preview": "4.0.13", "@vitest/browser-webdriverio": "4.0.13", "@vitest/ui": "4.0.13", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/debug", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-QSD4I0fN6uZQfftryIXuqvqgBxTvJ3ZNkF6RWECd82YGAYAfhcppBLFXzXJHQAAhVFyYEuFTrq6h0hQqjB7jIQ=="],
|
"vitest": ["vitest@4.0.16", "", { "dependencies": { "@vitest/expect": "4.0.16", "@vitest/mocker": "4.0.16", "@vitest/pretty-format": "4.0.16", "@vitest/runner": "4.0.16", "@vitest/snapshot": "4.0.16", "@vitest/spy": "4.0.16", "@vitest/utils": "4.0.16", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.16", "@vitest/browser-preview": "4.0.16", "@vitest/browser-webdriverio": "4.0.16", "@vitest/ui": "4.0.16", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q=="],
|
||||||
|
|
||||||
"vscode-jsonrpc": ["vscode-jsonrpc@8.2.1", "", {}, "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="],
|
"vscode-jsonrpc": ["vscode-jsonrpc@8.2.1", "", {}, "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="],
|
||||||
|
|
||||||
@@ -3874,24 +3906,16 @@
|
|||||||
|
|
||||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/core": ["@octokit/core@5.2.2", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg=="],
|
"@actions/artifact/@actions/core": ["@actions/core@2.0.1", "", { "dependencies": { "@actions/exec": "^2.0.0", "@actions/http-client": "^3.0.0" } }, "sha512-oBfqT3GwkvLlo1fjvhQLQxuwZCGTarTE5OuZ2Wg10hvhBj7LRIlF611WT4aZS6fDhO5ZKlY7lCAZTlpmyaHaeg=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/plugin-request-log": ["@octokit/plugin-request-log@1.0.4", "", { "peerDependencies": { "@octokit/core": ">=3" } }, "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA=="],
|
"@actions/core/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="],
|
"@actions/github/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/core": ["@octokit/core@5.2.2", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@9.2.2", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ=="],
|
"@actions/github/@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@9.2.2", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ=="],
|
||||||
|
|
||||||
"@actions/github/@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@10.4.1", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg=="],
|
"@actions/github/@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@10.4.1", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg=="],
|
||||||
|
|
||||||
"@actions/github/@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="],
|
|
||||||
|
|
||||||
"@actions/github/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
"@actions/github/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||||
|
|
||||||
"@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
"@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||||
@@ -3908,16 +3932,40 @@
|
|||||||
|
|
||||||
"@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
"@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
||||||
|
|
||||||
"@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
"@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/cerebras/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/cohere/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/deepinfra/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/deepinfra/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="],
|
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
"@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
"@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/mistral/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
"@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
"@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
||||||
|
|
||||||
"@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
"@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/perplexity/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/togetherai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/togetherai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/xai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
||||||
|
|
||||||
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||||
|
|
||||||
"@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="],
|
"@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="],
|
||||||
@@ -3962,40 +4010,16 @@
|
|||||||
|
|
||||||
"@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
"@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
||||||
|
|
||||||
"@azure/core-auth/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
"@azure/core-http/@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="],
|
||||||
|
|
||||||
"@azure/core-client/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
"@azure/core-http/@azure/core-tracing": ["@azure/core-tracing@1.0.0-preview.13", "", { "dependencies": { "@opentelemetry/api": "^1.0.1", "tslib": "^2.2.0" } }, "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ=="],
|
||||||
|
|
||||||
"@azure/core-client/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
|
|
||||||
|
|
||||||
"@azure/core-http/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
"@azure/core-http/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||||
|
|
||||||
"@azure/core-http/xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
|
"@azure/core-http/xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
|
||||||
|
|
||||||
"@azure/core-http-compat/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
|
||||||
|
|
||||||
"@azure/core-lro/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
|
||||||
|
|
||||||
"@azure/core-rest-pipeline/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
|
||||||
|
|
||||||
"@azure/core-rest-pipeline/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
|
|
||||||
|
|
||||||
"@azure/core-util/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
|
||||||
|
|
||||||
"@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
"@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
||||||
|
|
||||||
"@azure/storage-blob/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
|
||||||
|
|
||||||
"@azure/storage-blob/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
|
|
||||||
|
|
||||||
"@azure/storage-blob/events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
|
||||||
|
|
||||||
"@azure/storage-common/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
|
||||||
|
|
||||||
"@azure/storage-common/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
|
|
||||||
|
|
||||||
"@azure/storage-common/events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
|
||||||
|
|
||||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||||
@@ -4068,37 +4092,67 @@
|
|||||||
|
|
||||||
"@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
|
"@octokit/auth-app/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
|
"@octokit/auth-app/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-app/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
"@octokit/auth-oauth-app/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/auth-oauth-app/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-device/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
"@octokit/auth-oauth-device/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/auth-oauth-device/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-user/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
"@octokit/auth-oauth-user/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/auth-oauth-user/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
"@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="],
|
"@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="],
|
||||||
|
|
||||||
"@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/core/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
||||||
|
|
||||||
"@octokit/endpoint/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/core/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
||||||
|
|
||||||
|
"@octokit/endpoint/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
||||||
|
|
||||||
|
"@octokit/endpoint/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
||||||
|
|
||||||
|
"@octokit/graphql/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
"@octokit/graphql/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
"@octokit/graphql/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
||||||
|
|
||||||
|
"@octokit/oauth-methods/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
|
"@octokit/oauth-methods/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
"@octokit/oauth-methods/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/oauth-methods/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
"@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/plugin-paginate-rest/@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="],
|
||||||
|
|
||||||
"@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
||||||
|
|
||||||
"@octokit/plugin-retry/@octokit/types": ["@octokit/types@6.41.0", "", { "dependencies": { "@octokit/openapi-types": "^12.11.0" } }, "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg=="],
|
"@octokit/plugin-retry/@octokit/types": ["@octokit/types@6.41.0", "", { "dependencies": { "@octokit/openapi-types": "^12.11.0" } }, "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg=="],
|
||||||
|
|
||||||
"@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/request/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
||||||
|
|
||||||
"@octokit/request-error/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
"@octokit/request/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
||||||
|
|
||||||
|
"@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="],
|
||||||
|
|
||||||
"@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="],
|
"@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="],
|
||||||
|
|
||||||
"@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
"@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
||||||
|
|
||||||
"@opencode-ai/function/@octokit/rest": ["@octokit/rest@22.0.0", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="],
|
"@opencode-ai/tauri/@actions/artifact": ["@actions/artifact@4.0.0", "", { "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^6.0.1", "@actions/http-client": "^2.1.0", "@azure/core-http": "^3.0.5", "@azure/storage-blob": "^12.15.0", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-HCc2jMJRAfviGFAh0FsOR/jNfWhirxl7W6z8zDtttt0GltwxBLdEIjLiweOPFl9WbyJRW1VWnPUSAixJqcWUMQ=="],
|
||||||
|
|
||||||
"@opencode-ai/tauri/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
"@opencode-ai/tauri/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
||||||
|
|
||||||
@@ -4170,6 +4224,8 @@
|
|||||||
|
|
||||||
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
|
"ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="],
|
||||||
|
|
||||||
"ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
"ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
||||||
|
|
||||||
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
@@ -4194,6 +4250,8 @@
|
|||||||
|
|
||||||
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
|
"aws-sdk/events": ["events@1.1.1", "", {}, "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="],
|
||||||
|
|
||||||
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
|
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
|
||||||
|
|
||||||
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
|
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
|
||||||
@@ -4292,8 +4350,6 @@
|
|||||||
|
|
||||||
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bpYruxVLhrTbVH6CCq48zMJNeHu6FmHtEedl9FXckEgcIEAi036idFhJlcRwC1jNCwlacbzb8dPD7OAH1EKJaQ=="],
|
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bpYruxVLhrTbVH6CCq48zMJNeHu6FmHtEedl9FXckEgcIEAi036idFhJlcRwC1jNCwlacbzb8dPD7OAH1EKJaQ=="],
|
||||||
|
|
||||||
"opencode/@octokit/rest": ["@octokit/rest@22.0.0", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="],
|
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
|
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
|
||||||
|
|
||||||
"opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
|
"opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
|
||||||
@@ -4330,8 +4386,6 @@
|
|||||||
|
|
||||||
"readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
"readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||||
|
|
||||||
"readable-stream/events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
|
||||||
|
|
||||||
"readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
"readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
||||||
|
|
||||||
"router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
"router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
||||||
@@ -4384,6 +4438,8 @@
|
|||||||
|
|
||||||
"vite-plugin-icons-spritesheet/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
"vite-plugin-icons-spritesheet/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
|
"vitest/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||||
|
|
||||||
"vitest/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="],
|
"vitest/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="],
|
||||||
|
|
||||||
"vitest/why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
"vitest/why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
||||||
@@ -4404,46 +4460,14 @@
|
|||||||
|
|
||||||
"zod-to-ts/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"zod-to-ts/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="],
|
"@actions/artifact/@actions/core/@actions/exec": ["@actions/exec@2.0.0", "", { "dependencies": { "@actions/io": "^2.0.0" } }, "sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="],
|
"@actions/core/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/core/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
|
||||||
|
|
||||||
"@actions/artifact/@octokit/core/before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="],
|
|
||||||
|
|
||||||
"@actions/artifact/@octokit/core/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="],
|
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/core/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/core/before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/core/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
|
"@actions/github/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
|
||||||
|
|
||||||
"@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
|
"@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
|
||||||
|
|
||||||
"@actions/github/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/request/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/request/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="],
|
"@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="],
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="],
|
"@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="],
|
||||||
@@ -4634,33 +4658,93 @@
|
|||||||
|
|
||||||
"@modelcontextprotocol/sdk/raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
"@modelcontextprotocol/sdk/raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||||
|
|
||||||
|
"@octokit/auth-app/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/auth-app/@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
|
"@octokit/auth-app/@octokit/request-error/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-app/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-app/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
"@octokit/auth-oauth-app/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/auth-oauth-app/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-device/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-device/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
"@octokit/auth-oauth-device/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/auth-oauth-device/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-user/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/auth-oauth-user/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
"@octokit/auth-oauth-user/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/auth-oauth-user/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
"@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||||
|
|
||||||
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||||
|
|
||||||
|
"@octokit/graphql/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/graphql/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
|
"@octokit/graphql/@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
||||||
|
|
||||||
|
"@octokit/oauth-methods/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
"@octokit/oauth-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/oauth-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
"@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/plugin-paginate-rest/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
|
||||||
|
|
||||||
"@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/plugin-paginate-rest/@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/core/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/core/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
||||||
|
|
||||||
"@octokit/plugin-retry/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@12.11.0", "", {}, "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="],
|
"@octokit/plugin-retry/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@12.11.0", "", {}, "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="],
|
||||||
|
|
||||||
"@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||||
|
|
||||||
"@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
"@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
||||||
|
|
||||||
"@opencode-ai/function/@octokit/rest/@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="],
|
"@octokit/rest/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
|
||||||
|
|
||||||
"@opencode-ai/function/@octokit/rest/@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg=="],
|
"@octokit/rest/@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/core/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/core/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
||||||
|
|
||||||
|
"@opencode-ai/tauri/@actions/artifact/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="],
|
||||||
|
|
||||||
"@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/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=="],
|
||||||
|
|
||||||
@@ -4876,10 +4960,6 @@
|
|||||||
|
|
||||||
"opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
"opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
||||||
|
|
||||||
"opencode/@octokit/rest/@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="],
|
|
||||||
|
|
||||||
"opencode/@octokit/rest/@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg=="],
|
|
||||||
|
|
||||||
"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/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=="],
|
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
|
||||||
@@ -4922,22 +5002,12 @@
|
|||||||
|
|
||||||
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
"@actions/artifact/@actions/core/@actions/exec/@actions/io": ["@actions/io@2.0.0", "", {}, "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg=="],
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
|
||||||
|
|
||||||
"@actions/artifact/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="],
|
"@actions/github/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="],
|
||||||
|
|
||||||
"@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="],
|
"@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="],
|
||||||
|
|
||||||
"@actions/github/@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
|
||||||
|
|
||||||
"@actions/github/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
|
|
||||||
|
|
||||||
"@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="],
|
"@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="],
|
||||||
|
|
||||||
"@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="],
|
"@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="],
|
||||||
@@ -5020,9 +5090,25 @@
|
|||||||
|
|
||||||
"@modelcontextprotocol/sdk/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
"@modelcontextprotocol/sdk/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||||
|
|
||||||
"@opencode-ai/function/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
"@octokit/auth-app/@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
"@opencode-ai/function/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
"@octokit/auth-app/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
|
"@octokit/graphql/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-paginate-rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
|
"@octokit/rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
|
"@opencode-ai/tauri/@actions/artifact/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||||
|
|
||||||
"@slack/web-api/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
"@slack/web-api/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||||
|
|
||||||
@@ -5044,10 +5130,6 @@
|
|||||||
|
|
||||||
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
"opencode/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
|
||||||
|
|
||||||
"opencode/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
|
||||||
|
|
||||||
"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/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=="],
|
"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=="],
|
||||||
@@ -5140,18 +5222,10 @@
|
|||||||
|
|
||||||
"@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"@opencode-ai/function/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
|
||||||
|
|
||||||
"@opencode-ai/function/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
|
||||||
|
|
||||||
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
|
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
|
||||||
|
|
||||||
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="],
|
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="],
|
||||||
|
|
||||||
"opencode/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
|
||||||
|
|
||||||
"opencode/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
"opencontrol/@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/express/body-parser/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
"opencontrol/@modelcontextprotocol/sdk/express/body-parser/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||||
|
|||||||
Generated
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765934234,
|
"lastModified": 1766314097,
|
||||||
"narHash": "sha256-pJjWUzNnjbIAMIc5gRFUuKCDQ9S1cuh3b2hKgA7Mc4A=",
|
"narHash": "sha256-laJftWbghBehazn/zxVJ8NdENVgjccsWAdAqKXhErrM=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "af84f9d270d404c17699522fab95bbf928a2d92f",
|
"rev": "306ea70f9eb0fb4e040f8540e2deab32ed7e2055",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ inputs:
|
|||||||
description: "Model to use"
|
description: "Model to use"
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
agent:
|
||||||
|
description: "Agent to use. Must be a primary agent. Falls back to default_agent from config or 'build' if not found."
|
||||||
|
required: false
|
||||||
|
|
||||||
share:
|
share:
|
||||||
description: "Share the opencode session (defaults to true for public repos)"
|
description: "Share the opencode session (defaults to true for public repos)"
|
||||||
required: false
|
required: false
|
||||||
@@ -62,6 +66,7 @@ runs:
|
|||||||
run: opencode github run
|
run: opencode github run
|
||||||
env:
|
env:
|
||||||
MODEL: ${{ inputs.model }}
|
MODEL: ${{ inputs.model }}
|
||||||
|
AGENT: ${{ inputs.agent }}
|
||||||
SHARE: ${{ inputs.share }}
|
SHARE: ${{ inputs.share }}
|
||||||
PROMPT: ${{ inputs.prompt }}
|
PROMPT: ${{ inputs.prompt }}
|
||||||
USE_GITHUB_TOKEN: ${{ inputs.use_github_token }}
|
USE_GITHUB_TOKEN: ${{ inputs.use_github_token }}
|
||||||
|
|||||||
+31
-2
@@ -318,6 +318,10 @@ function useEnvRunUrl() {
|
|||||||
return `/${repo.owner}/${repo.repo}/actions/runs/${runId}`
|
return `/${repo.owner}/${repo.repo}/actions/runs/${runId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useEnvAgent() {
|
||||||
|
return process.env["AGENT"] || undefined
|
||||||
|
}
|
||||||
|
|
||||||
function useEnvShare() {
|
function useEnvShare() {
|
||||||
const value = process.env["SHARE"]
|
const value = process.env["SHARE"]
|
||||||
if (!value) return undefined
|
if (!value) return undefined
|
||||||
@@ -570,24 +574,49 @@ async function subscribeSessionEvents() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function summarize(response: string) {
|
async function summarize(response: string) {
|
||||||
const payload = useContext().payload as IssueCommentEvent
|
|
||||||
try {
|
try {
|
||||||
return await chat(`Summarize the following in less than 40 characters:\n\n${response}`)
|
return await chat(`Summarize the following in less than 40 characters:\n\n${response}`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (isScheduleEvent()) {
|
||||||
|
return "Scheduled task changes"
|
||||||
|
}
|
||||||
|
const payload = useContext().payload as IssueCommentEvent
|
||||||
return `Fix issue: ${payload.issue.title}`
|
return `Fix issue: ${payload.issue.title}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function resolveAgent(): Promise<string | undefined> {
|
||||||
|
const envAgent = useEnvAgent()
|
||||||
|
if (!envAgent) return undefined
|
||||||
|
|
||||||
|
// Validate the agent exists and is a primary agent
|
||||||
|
const agents = await client.agent.list<true>()
|
||||||
|
const agent = agents.data?.find((a) => a.name === envAgent)
|
||||||
|
|
||||||
|
if (!agent) {
|
||||||
|
console.warn(`agent "${envAgent}" not found. Falling back to default agent`)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agent.mode === "subagent") {
|
||||||
|
console.warn(`agent "${envAgent}" is a subagent, not a primary agent. Falling back to default agent`)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return envAgent
|
||||||
|
}
|
||||||
|
|
||||||
async function chat(text: string, files: PromptFiles = []) {
|
async function chat(text: string, files: PromptFiles = []) {
|
||||||
console.log("Sending message to opencode...")
|
console.log("Sending message to opencode...")
|
||||||
const { providerID, modelID } = useEnvModel()
|
const { providerID, modelID } = useEnvModel()
|
||||||
|
const agent = await resolveAgent()
|
||||||
|
|
||||||
const chat = await client.session.chat<true>({
|
const chat = await client.session.chat<true>({
|
||||||
path: session,
|
path: session,
|
||||||
body: {
|
body: {
|
||||||
providerID,
|
providerID,
|
||||||
modelID,
|
modelID,
|
||||||
agent: "build",
|
agent,
|
||||||
parts: [
|
parts: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"nodeModules": "sha256-g6XHWk9IoDoeXbvENs+U2fqk185xKMLb0BRopCbXaIk="
|
"nodeModules": "sha256-SJSVpKmRDS24yzZ3ypYKWVyntOG0TNrrpqQXUkf8gXY="
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -56,6 +56,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@actions/artifact": "5.0.1",
|
||||||
"@tsconfig/bun": "catalog:",
|
"@tsconfig/bun": "catalog:",
|
||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
@@ -63,8 +64,15 @@
|
|||||||
"turbo": "2.5.6"
|
"turbo": "2.5.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ai-sdk/cerebras": "1.0.33",
|
||||||
|
"@ai-sdk/cohere": "2.0.21",
|
||||||
|
"@ai-sdk/deepinfra": "1.0.30",
|
||||||
|
"@ai-sdk/gateway": "2.0.23",
|
||||||
|
"@ai-sdk/groq": "2.0.33",
|
||||||
|
"@ai-sdk/perplexity": "2.0.22",
|
||||||
|
"@ai-sdk/togetherai": "1.0.30",
|
||||||
"@aws-sdk/client-s3": "3.933.0",
|
"@aws-sdk/client-s3": "3.933.0",
|
||||||
"@octokit/rest": "22.0.1",
|
"@opencode-ai/plugin": "workspace:*",
|
||||||
"@opencode-ai/script": "workspace:*",
|
"@opencode-ai/script": "workspace:*",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"typescript": "catalog:"
|
"typescript": "catalog:"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsgo --noEmit",
|
"typecheck": "tsgo --noEmit",
|
||||||
|
|||||||
@@ -202,6 +202,14 @@ export function IconZai(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function IconMiniMax(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg {...props} fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function IconGemini(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
export function IconGemini(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
<svg {...props} viewBox="0 0 50 50" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<svg {...props} viewBox="0 0 50 50" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export const config = {
|
|||||||
github: {
|
github: {
|
||||||
repoUrl: "https://github.com/sst/opencode",
|
repoUrl: "https://github.com/sst/opencode",
|
||||||
starsFormatted: {
|
starsFormatted: {
|
||||||
compact: "38K",
|
compact: "41K",
|
||||||
full: "38,000",
|
full: "41,000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -22,8 +22,8 @@ export const config = {
|
|||||||
|
|
||||||
// Static stats (used on landing page)
|
// Static stats (used on landing page)
|
||||||
stats: {
|
stats: {
|
||||||
contributors: "400",
|
contributors: "450",
|
||||||
commits: "5,000",
|
commits: "6,000",
|
||||||
monthlyUsers: "400,000",
|
monthlyUsers: "400,000",
|
||||||
},
|
},
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import "./index.css"
|
import "./index.css"
|
||||||
import { Title, Meta, Link } from "@solidjs/meta"
|
import { Title, Meta, Link } from "@solidjs/meta"
|
||||||
// import { HttpHeader } from "@solidjs/start"
|
//import { HttpHeader } from "@solidjs/start"
|
||||||
import video from "../asset/lander/opencode-min.mp4"
|
import video from "../asset/lander/opencode-min.mp4"
|
||||||
import videoPoster from "../asset/lander/opencode-poster.png"
|
import videoPoster from "../asset/lander/opencode-poster.png"
|
||||||
import { IconCopy, IconCheck } from "../component/icon"
|
import { IconCopy, IconCheck } from "../component/icon"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
IconAlibaba,
|
IconAlibaba,
|
||||||
IconAnthropic,
|
IconAnthropic,
|
||||||
IconGemini,
|
IconGemini,
|
||||||
|
IconMiniMax,
|
||||||
IconMoonshotAI,
|
IconMoonshotAI,
|
||||||
IconOpenAI,
|
IconOpenAI,
|
||||||
IconStealth,
|
IconStealth,
|
||||||
@@ -23,6 +24,7 @@ const getModelLab = (modelId: string) => {
|
|||||||
if (modelId.startsWith("kimi")) return "Moonshot AI"
|
if (modelId.startsWith("kimi")) return "Moonshot AI"
|
||||||
if (modelId.startsWith("glm")) return "Z.ai"
|
if (modelId.startsWith("glm")) return "Z.ai"
|
||||||
if (modelId.startsWith("qwen")) return "Alibaba"
|
if (modelId.startsWith("qwen")) return "Alibaba"
|
||||||
|
if (modelId.startsWith("minimax")) return "MiniMax"
|
||||||
if (modelId.startsWith("grok")) return "xAI"
|
if (modelId.startsWith("grok")) return "xAI"
|
||||||
return "Stealth"
|
return "Stealth"
|
||||||
}
|
}
|
||||||
@@ -35,7 +37,7 @@ const getModelsInfo = query(async (workspaceID: string) => {
|
|||||||
.filter(([id, _model]) => !["claude-3-5-haiku"].includes(id))
|
.filter(([id, _model]) => !["claude-3-5-haiku"].includes(id))
|
||||||
.filter(([id, _model]) => !id.startsWith("alpha-"))
|
.filter(([id, _model]) => !id.startsWith("alpha-"))
|
||||||
.sort(([idA, modelA], [idB, modelB]) => {
|
.sort(([idA, modelA], [idB, modelB]) => {
|
||||||
const priority = ["big-pickle", "grok", "claude", "gpt", "gemini"]
|
const priority = ["big-pickle", "minimax", "grok", "claude", "gpt", "gemini"]
|
||||||
const getPriority = (id: string) => {
|
const getPriority = (id: string) => {
|
||||||
const index = priority.findIndex((p) => id.startsWith(p))
|
const index = priority.findIndex((p) => id.startsWith(p))
|
||||||
return index === -1 ? Infinity : index
|
return index === -1 ? Infinity : index
|
||||||
@@ -129,6 +131,8 @@ export function ModelSection() {
|
|||||||
return <IconAlibaba width={16} height={16} />
|
return <IconAlibaba width={16} height={16} />
|
||||||
case "xAI":
|
case "xAI":
|
||||||
return <IconXai width={16} height={16} />
|
return <IconXai width={16} height={16} />
|
||||||
|
case "MiniMax":
|
||||||
|
return <IconMiniMax width={16} height={16} />
|
||||||
default:
|
default:
|
||||||
return <IconStealth width={16} height={16} />
|
return <IconStealth width={16} height={16} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import "./index.css"
|
import "./index.css"
|
||||||
import { createAsync, query, redirect } from "@solidjs/router"
|
import { createAsync, query, redirect } from "@solidjs/router"
|
||||||
import { Title, Meta, Link } from "@solidjs/meta"
|
import { Title, Meta, Link } from "@solidjs/meta"
|
||||||
// import { HttpHeader } from "@solidjs/start"
|
//import { HttpHeader } from "@solidjs/start"
|
||||||
import zenLogoLight from "../../asset/zen-ornate-light.svg"
|
import zenLogoLight from "../../asset/zen-ornate-light.svg"
|
||||||
import { config } from "~/config"
|
import { config } from "~/config"
|
||||||
import zenLogoDark from "../../asset/zen-ornate-dark.svg"
|
import zenLogoDark from "../../asset/zen-ornate-dark.svg"
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ export async function handler(
|
|||||||
headers.delete("content-length")
|
headers.delete("content-length")
|
||||||
headers.delete("x-opencode-request")
|
headers.delete("x-opencode-request")
|
||||||
headers.delete("x-opencode-session")
|
headers.delete("x-opencode-session")
|
||||||
|
headers.delete("x-opencode-project")
|
||||||
|
headers.delete("x-opencode-client")
|
||||||
return headers
|
return headers
|
||||||
})(),
|
})(),
|
||||||
body: reqBody,
|
body: reqBody,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
"solid-list": "catalog:",
|
"solid-list": "catalog:",
|
||||||
"tailwindcss": "catalog:",
|
"tailwindcss": "catalog:",
|
||||||
"virtua": "catalog:"
|
"virtua": "catalog:",
|
||||||
|
"zod": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import Home from "@/pages/home"
|
|||||||
import DirectoryLayout from "@/pages/directory-layout"
|
import DirectoryLayout from "@/pages/directory-layout"
|
||||||
import Session from "@/pages/session"
|
import Session from "@/pages/session"
|
||||||
import { ErrorPage } from "./pages/error"
|
import { ErrorPage } from "./pages/error"
|
||||||
|
import { iife } from "@opencode-ai/util/iife"
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -28,20 +29,23 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1"
|
const url = iife(() => {
|
||||||
const port = window.__OPENCODE__?.port ?? import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"
|
const param = new URLSearchParams(document.location.search).get("url")
|
||||||
|
if (param) return param
|
||||||
|
|
||||||
const url =
|
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
|
||||||
new URLSearchParams(document.location.search).get("url") ||
|
if (window.__OPENCODE__) return `http://127.0.0.1:${window.__OPENCODE__.port}`
|
||||||
(location.hostname.includes("opencode.ai") || location.hostname.includes("localhost")
|
if (import.meta.env.VITE_OPENCODE_SERVER)
|
||||||
? `http://${host}:${port}`
|
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
|
||||||
: "/")
|
|
||||||
|
return "/"
|
||||||
|
})
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
return (
|
return (
|
||||||
<MetaProvider>
|
<MetaProvider>
|
||||||
<Font />
|
<Font />
|
||||||
<ErrorBoundary fallback={ErrorPage}>
|
<ErrorBoundary fallback={(error) => <ErrorPage error={error} />}>
|
||||||
<DialogProvider>
|
<DialogProvider>
|
||||||
<MarkedProvider>
|
<MarkedProvider>
|
||||||
<DiffComponentProvider component={Diff}>
|
<DiffComponentProvider component={Diff}>
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
|
import type { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client"
|
||||||
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
|
import type { IconName } from "@opencode-ai/ui/icons/provider"
|
||||||
|
import { List, type ListRef } from "@opencode-ai/ui/list"
|
||||||
|
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||||
|
import { Spinner } from "@opencode-ai/ui/spinner"
|
||||||
|
import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
|
import { iife } from "@opencode-ai/util/iife"
|
||||||
import { createMemo, Match, onCleanup, onMount, Switch } from "solid-js"
|
import { createMemo, Match, onCleanup, onMount, Switch } from "solid-js"
|
||||||
import { createStore, produce } from "solid-js/store"
|
import { createStore, produce } from "solid-js/store"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
|
||||||
import { useGlobalSDK } from "@/context/global-sdk"
|
|
||||||
import { usePlatform } from "@/context/platform"
|
|
||||||
import { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client"
|
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
|
||||||
import { List, ListRef } from "@opencode-ai/ui/list"
|
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
|
||||||
import { TextField } from "@opencode-ai/ui/text-field"
|
|
||||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
|
||||||
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
|
||||||
import { IconName } from "@opencode-ai/ui/icons/provider"
|
|
||||||
import { iife } from "@opencode-ai/util/iife"
|
|
||||||
import { Link } from "@/components/link"
|
import { Link } from "@/components/link"
|
||||||
import { DialogSelectProvider } from "./dialog-select-provider"
|
import { useGlobalSDK } from "@/context/global-sdk"
|
||||||
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
|
import { usePlatform } from "@/context/platform"
|
||||||
import { DialogSelectModel } from "./dialog-select-model"
|
import { DialogSelectModel } from "./dialog-select-model"
|
||||||
|
import { DialogSelectProvider } from "./dialog-select-provider"
|
||||||
|
|
||||||
export function DialogConnectProvider(props: { provider: string }) {
|
export function DialogConnectProvider(props: { provider: string }) {
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
@@ -154,7 +154,9 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
<div class="text-14-regular text-text-base">Select login method for {provider().name}.</div>
|
<div class="text-14-regular text-text-base">Select login method for {provider().name}.</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<List
|
<List
|
||||||
ref={(ref) => (listRef = ref)}
|
ref={(ref) => {
|
||||||
|
listRef = ref
|
||||||
|
}}
|
||||||
items={methods}
|
items={methods}
|
||||||
key={(m) => m?.label}
|
key={(m) => m?.label}
|
||||||
onSelect={async (method, index) => {
|
onSelect={async (method, index) => {
|
||||||
@@ -163,7 +165,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div class="w-full flex items-center gap-x-4">
|
<div class="w-full flex items-center gap-x-2">
|
||||||
<div class="w-4 h-2 rounded-[1px] bg-input-base shadow-xs-border-base flex items-center justify-center">
|
<div class="w-4 h-2 rounded-[1px] bg-input-base shadow-xs-border-base flex items-center justify-center">
|
||||||
<div class="w-2.5 h-0.5 bg-icon-strong-base hidden" data-slot="list-item-extra-icon" />
|
<div class="w-2.5 h-0.5 bg-icon-strong-base hidden" data-slot="list-item-extra-icon" />
|
||||||
</div>
|
</div>
|
||||||
@@ -175,7 +177,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
</Match>
|
</Match>
|
||||||
<Match when={store.state === "pending"}>
|
<Match when={store.state === "pending"}>
|
||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
<div class="flex items-center gap-x-4">
|
<div class="flex items-center gap-x-2">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<span>Authorization in progress...</span>
|
<span>Authorization in progress...</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,7 +185,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
</Match>
|
</Match>
|
||||||
<Match when={store.state === "error"}>
|
<Match when={store.state === "error"}>
|
||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
<div class="flex items-center gap-x-4">
|
<div class="flex items-center gap-x-2">
|
||||||
<Icon name="circle-ban-sign" class="text-icon-critical-base" />
|
<Icon name="circle-ban-sign" class="text-icon-critical-base" />
|
||||||
<span>Authorization failed: {store.error}</span>
|
<span>Authorization failed: {store.error}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { Component } from "solid-js"
|
|
||||||
import { useLocal } from "@/context/local"
|
|
||||||
import { popularProviders } from "@/hooks/use-providers"
|
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
import { List } from "@opencode-ai/ui/list"
|
import { List } from "@opencode-ai/ui/list"
|
||||||
import { Switch } from "@opencode-ai/ui/switch"
|
import { Switch } from "@opencode-ai/ui/switch"
|
||||||
|
import type { Component } from "solid-js"
|
||||||
|
import { useLocal } from "@/context/local"
|
||||||
|
import { popularProviders } from "@/hooks/use-providers"
|
||||||
|
|
||||||
export const DialogManageModels: Component = () => {
|
export const DialogManageModels: Component = () => {
|
||||||
const local = useLocal()
|
const local = useLocal()
|
||||||
return (
|
return (
|
||||||
<Dialog title="Manage models" description="Customize which models appear in the model selector.">
|
<Dialog title="Manage models" description="Customize which models appear in the model selector.">
|
||||||
<List
|
<List
|
||||||
class="px-2.5"
|
|
||||||
search={{ placeholder: "Search models", autofocus: true }}
|
search={{ placeholder: "Search models", autofocus: true }}
|
||||||
emptyMessage="No model results"
|
emptyMessage="No model results"
|
||||||
key={(x) => `${x?.provider?.id}:${x?.id}`}
|
key={(x) => `${x?.provider?.id}:${x?.id}`}
|
||||||
@@ -27,16 +26,24 @@ export const DialogManageModels: Component = () => {
|
|||||||
}}
|
}}
|
||||||
onSelect={(x) => {
|
onSelect={(x) => {
|
||||||
if (!x) return
|
if (!x) return
|
||||||
const visible = local.model.visible({ modelID: x.id, providerID: x.provider.id })
|
const visible = local.model.visible({
|
||||||
|
modelID: x.id,
|
||||||
|
providerID: x.provider.id,
|
||||||
|
})
|
||||||
local.model.setVisibility({ modelID: x.id, providerID: x.provider.id }, !visible)
|
local.model.setVisibility({ modelID: x.id, providerID: x.provider.id }, !visible)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div class="w-full flex items-center justify-between gap-x-2.5">
|
<div class="w-full flex items-center justify-between gap-x-3">
|
||||||
<span>{i.name}</span>
|
<span>{i.name}</span>
|
||||||
<div onClick={(e) => e.stopPropagation()}>
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
<Switch
|
<Switch
|
||||||
checked={!!local.model.visible({ modelID: i.id, providerID: i.provider.id })}
|
checked={
|
||||||
|
!!local.model.visible({
|
||||||
|
modelID: i.id,
|
||||||
|
providerID: i.provider.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
onChange={(checked) => {
|
onChange={(checked) => {
|
||||||
local.model.setVisibility({ modelID: i.id, providerID: i.provider.id }, checked)
|
local.model.setVisibility({ modelID: i.id, providerID: i.provider.id }, checked)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useLocal } from "@/context/local"
|
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
|
||||||
import { List } from "@opencode-ai/ui/list"
|
|
||||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
|
||||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
|
||||||
import { useLayout } from "@/context/layout"
|
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
|
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||||
|
import { List } from "@opencode-ai/ui/list"
|
||||||
|
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||||
import { useParams } from "@solidjs/router"
|
import { useParams } from "@solidjs/router"
|
||||||
import { createMemo } from "solid-js"
|
import { createMemo } from "solid-js"
|
||||||
|
import { useLayout } from "@/context/layout"
|
||||||
|
import { useLocal } from "@/context/local"
|
||||||
|
|
||||||
export function DialogSelectFile() {
|
export function DialogSelectFile() {
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
@@ -18,7 +18,6 @@ export function DialogSelectFile() {
|
|||||||
return (
|
return (
|
||||||
<Dialog title="Select file">
|
<Dialog title="Select file">
|
||||||
<List
|
<List
|
||||||
class="px-2.5"
|
|
||||||
search={{ placeholder: "Search files", autofocus: true }}
|
search={{ placeholder: "Search files", autofocus: true }}
|
||||||
emptyMessage="No files found"
|
emptyMessage="No files found"
|
||||||
items={local.file.searchFiles}
|
items={local.file.searchFiles}
|
||||||
@@ -32,7 +31,7 @@ export function DialogSelectFile() {
|
|||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div class="w-full flex items-center justify-between rounded-md">
|
<div class="w-full flex items-center justify-between rounded-md">
|
||||||
<div class="flex items-center gap-x-2 grow min-w-0">
|
<div class="flex items-center gap-x-3 grow min-w-0">
|
||||||
<FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
|
<FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
|
||||||
<div class="flex items-center text-14-regular">
|
<div class="flex items-center text-14-regular">
|
||||||
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
|
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Component, onCleanup, onMount, Show } from "solid-js"
|
|
||||||
import { useLocal } from "@/context/local"
|
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|
||||||
import { popularProviders, useProviders } from "@/hooks/use-providers"
|
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Tag } from "@opencode-ai/ui/tag"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
import { List, ListRef } from "@opencode-ai/ui/list"
|
import type { IconName } from "@opencode-ai/ui/icons/provider"
|
||||||
|
import { List, type ListRef } from "@opencode-ai/ui/list"
|
||||||
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||||
import { IconName } from "@opencode-ai/ui/icons/provider"
|
import { Tag } from "@opencode-ai/ui/tag"
|
||||||
import { DialogSelectProvider } from "./dialog-select-provider"
|
import { type Component, onCleanup, onMount, Show } from "solid-js"
|
||||||
|
import { useLocal } from "@/context/local"
|
||||||
|
import { popularProviders, useProviders } from "@/hooks/use-providers"
|
||||||
import { DialogConnectProvider } from "./dialog-connect-provider"
|
import { DialogConnectProvider } from "./dialog-connect-provider"
|
||||||
|
import { DialogSelectProvider } from "./dialog-select-provider"
|
||||||
|
|
||||||
export const DialogSelectModelUnpaid: Component = () => {
|
export const DialogSelectModelUnpaid: Component = () => {
|
||||||
const local = useLocal()
|
const local = useLocal()
|
||||||
@@ -64,7 +64,7 @@ export const DialogSelectModelUnpaid: Component = () => {
|
|||||||
<div class="px-2 text-14-medium text-text-base">Add more models from popular providers</div>
|
<div class="px-2 text-14-medium text-text-base">Add more models from popular providers</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<List
|
<List
|
||||||
class="w-full"
|
class="w-full px-0"
|
||||||
key={(x) => x?.id}
|
key={(x) => x?.id}
|
||||||
items={providers.popular}
|
items={providers.popular}
|
||||||
activeIcon="plus-small"
|
activeIcon="plus-small"
|
||||||
@@ -79,17 +79,8 @@ export const DialogSelectModelUnpaid: Component = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div class="w-full flex items-center gap-x-4">
|
<div class="w-full flex items-center gap-x-3">
|
||||||
<ProviderIcon
|
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
|
||||||
data-slot="list-item-extra-icon"
|
|
||||||
id={i.id as IconName}
|
|
||||||
// TODO: clean this up after we update icon in models.dev
|
|
||||||
classList={{
|
|
||||||
"text-icon-weak-base": true,
|
|
||||||
"size-4 mx-0.5": i.id === "opencode",
|
|
||||||
"size-5": i.id !== "opencode",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>{i.name}</span>
|
<span>{i.name}</span>
|
||||||
<Show when={i.id === "opencode"}>
|
<Show when={i.id === "opencode"}>
|
||||||
<Tag>Recommended</Tag>
|
<Tag>Recommended</Tag>
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<List
|
<List
|
||||||
class="px-2.5"
|
|
||||||
search={{ placeholder: "Search models", autofocus: true }}
|
search={{ placeholder: "Search models", autofocus: true }}
|
||||||
emptyMessage="No model results"
|
emptyMessage="No model results"
|
||||||
key={(x) => `${x.provider.id}:${x.id}`}
|
key={(x) => `${x.provider.id}:${x.id}`}
|
||||||
@@ -61,7 +60,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div class="w-full flex items-center gap-x-2.5">
|
<div class="w-full flex items-center gap-x-3">
|
||||||
<span>{i.name}</span>
|
<span>{i.name}</span>
|
||||||
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
|
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
|
||||||
<Tag>Free</Tag>
|
<Tag>Free</Tag>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export const DialogSelectProvider: Component = () => {
|
|||||||
return (
|
return (
|
||||||
<Dialog title="Connect provider">
|
<Dialog title="Connect provider">
|
||||||
<List
|
<List
|
||||||
class="px-2.5"
|
|
||||||
search={{ placeholder: "Search providers", autofocus: true }}
|
search={{ placeholder: "Search providers", autofocus: true }}
|
||||||
activeIcon="plus-small"
|
activeIcon="plus-small"
|
||||||
key={(x) => x?.id}
|
key={(x) => x?.id}
|
||||||
@@ -38,17 +37,8 @@ export const DialogSelectProvider: Component = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div class="px-1.25 w-full flex items-center gap-x-4">
|
<div class="px-1.25 w-full flex items-center gap-x-3">
|
||||||
<ProviderIcon
|
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
|
||||||
data-slot="list-item-extra-icon"
|
|
||||||
id={i.id as IconName}
|
|
||||||
// TODO: clean this up after we update icon in models.dev
|
|
||||||
classList={{
|
|
||||||
"text-icon-weak-base": true,
|
|
||||||
"size-4 mx-0.5": i.id === "opencode",
|
|
||||||
"size-5": i.id !== "opencode",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>{i.name}</span>
|
<span>{i.name}</span>
|
||||||
<Show when={i.id === "opencode"}>
|
<Show when={i.id === "opencode"}>
|
||||||
<Tag>Recommended</Tag>
|
<Tag>Recommended</Tag>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { Select } from "@opencode-ai/ui/select"
|
|||||||
import { TextField } from "@opencode-ai/ui/text-field"
|
import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||||
import { base64Decode } from "@opencode-ai/util/encode"
|
import { base64Decode } from "@opencode-ai/util/encode"
|
||||||
|
import { useCommand } from "@/context/command"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
import { A, useParams } from "@solidjs/router"
|
import { A, useParams } from "@solidjs/router"
|
||||||
import { createMemo, createResource, Show } from "solid-js"
|
import { createMemo, createResource, Show } from "solid-js"
|
||||||
@@ -19,24 +20,29 @@ import { iife } from "@opencode-ai/util/iife"
|
|||||||
export function Header(props: {
|
export function Header(props: {
|
||||||
navigateToProject: (directory: string) => void
|
navigateToProject: (directory: string) => void
|
||||||
navigateToSession: (session: Session | undefined) => void
|
navigateToSession: (session: Session | undefined) => void
|
||||||
|
onMobileMenuToggle?: () => void
|
||||||
}) {
|
}) {
|
||||||
const globalSync = useGlobalSync()
|
const globalSync = useGlobalSync()
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const currentDirectory = createMemo(() => base64Decode(params.dir ?? ""))
|
const command = useCommand()
|
||||||
const store = createMemo(() => globalSync.child(currentDirectory())[0])
|
|
||||||
const sessions = createMemo(() => store().session ?? [])
|
|
||||||
const currentSession = createMemo(() => sessions().find((s) => s.id === params.id))
|
|
||||||
const shareEnabled = createMemo(() => store().config.share !== "disabled")
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base flex" data-tauri-drag-region>
|
<header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base flex" data-tauri-drag-region>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="xl:hidden w-12 shrink-0 flex items-center justify-center border-r border-border-weak-base hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors"
|
||||||
|
onClick={props.onMobileMenuToggle}
|
||||||
|
>
|
||||||
|
<Icon name="menu" size="small" />
|
||||||
|
</button>
|
||||||
<A
|
<A
|
||||||
href="/"
|
href="/"
|
||||||
classList={{
|
classList={{
|
||||||
|
"hidden xl:flex": true,
|
||||||
"w-12 shrink-0 px-4 py-3.5": true,
|
"w-12 shrink-0 px-4 py-3.5": true,
|
||||||
"flex items-center justify-start self-stretch": true,
|
"items-center justify-start self-stretch": true,
|
||||||
"border-r border-border-weak-base": true,
|
"border-r border-border-weak-base": true,
|
||||||
}}
|
}}
|
||||||
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
|
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
|
||||||
@@ -45,101 +51,128 @@ export function Header(props: {
|
|||||||
<Mark class="shrink-0" />
|
<Mark class="shrink-0" />
|
||||||
</A>
|
</A>
|
||||||
<div class="pl-4 px-6 flex items-center justify-between gap-4 w-full">
|
<div class="pl-4 px-6 flex items-center justify-between gap-4 w-full">
|
||||||
<Show when={params.dir && layout.projects.list().length > 0}>
|
<Show when={layout.projects.list().length > 0 && params.dir}>
|
||||||
<div class="flex items-center gap-3">
|
{(directory) => {
|
||||||
<div class="flex items-center gap-2">
|
const currentDirectory = createMemo(() => base64Decode(directory()))
|
||||||
<Select
|
const store = createMemo(() => globalSync.child(currentDirectory())[0])
|
||||||
options={layout.projects.list().map((project) => project.worktree)}
|
const sessions = createMemo(() => (store().session ?? []).filter((s) => !s.parentID))
|
||||||
current={currentDirectory()}
|
const currentSession = createMemo(() => sessions().find((s) => s.id === params.id))
|
||||||
label={(x) => getFilename(x)}
|
const shareEnabled = createMemo(() => store().config.share !== "disabled")
|
||||||
onSelect={(x) => (x ? props.navigateToProject(x) : undefined)}
|
return (
|
||||||
class="text-14-regular text-text-base"
|
<>
|
||||||
variant="ghost"
|
<div class="flex items-center gap-3 min-w-0">
|
||||||
>
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
{/* @ts-ignore */}
|
<div class="hidden xl:flex items-center gap-2">
|
||||||
{(i) => (
|
<Select
|
||||||
<div class="flex items-center gap-2">
|
options={layout.projects.list().map((project) => project.worktree)}
|
||||||
<Icon name="folder" size="small" />
|
current={currentDirectory()}
|
||||||
<div class="text-text-strong">{getFilename(i)}</div>
|
label={(x) => getFilename(x)}
|
||||||
|
onSelect={(x) => (x ? props.navigateToProject(x) : undefined)}
|
||||||
|
class="text-14-regular text-text-base"
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
{(i) => (
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<Icon name="folder" size="small" />
|
||||||
|
<div class="text-text-strong">{getFilename(i)}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
<div class="text-text-weaker">/</div>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
options={sessions()}
|
||||||
|
current={currentSession()}
|
||||||
|
placeholder="New session"
|
||||||
|
label={(x) => x.title}
|
||||||
|
value={(x) => x.id}
|
||||||
|
onSelect={props.navigateToSession}
|
||||||
|
class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<Show when={currentSession()}>
|
||||||
</Select>
|
<Tooltip
|
||||||
<div class="text-text-weaker">/</div>
|
class="hidden xl:block"
|
||||||
<Select
|
value={
|
||||||
options={sessions()}
|
<div class="flex items-center gap-2">
|
||||||
current={currentSession()}
|
<span>New session</span>
|
||||||
placeholder="New session"
|
<span class="text-icon-base text-12-medium">{command.keybind("session.new")}</span>
|
||||||
label={(x) => x.title}
|
</div>
|
||||||
value={(x) => x.id}
|
|
||||||
onSelect={props.navigateToSession}
|
|
||||||
class="text-14-regular text-text-base max-w-md"
|
|
||||||
variant="ghost"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Show when={currentSession()}>
|
|
||||||
<Button as={A} href={`/${params.dir}/session`} icon="plus-small">
|
|
||||||
New session
|
|
||||||
</Button>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<Tooltip
|
|
||||||
class="shrink-0"
|
|
||||||
value={
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span>Toggle terminal</span>
|
|
||||||
<span class="text-icon-base text-12-medium">Ctrl `</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={layout.terminal.toggle}>
|
|
||||||
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
|
||||||
<Icon
|
|
||||||
size="small"
|
|
||||||
name={layout.terminal.opened() ? "layout-bottom-full" : "layout-bottom"}
|
|
||||||
class="group-hover/terminal-toggle:hidden"
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
size="small"
|
|
||||||
name="layout-bottom-partial"
|
|
||||||
class="hidden group-hover/terminal-toggle:inline-block"
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
size="small"
|
|
||||||
name={layout.terminal.opened() ? "layout-bottom" : "layout-bottom-full"}
|
|
||||||
class="hidden group-active/terminal-toggle:inline-block"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
<Show when={shareEnabled() && currentSession()}>
|
|
||||||
<Popover
|
|
||||||
title="Share session"
|
|
||||||
trigger={
|
|
||||||
<Tooltip class="shrink-0" value="Share session">
|
|
||||||
<IconButton icon="share" variant="ghost" class="" />
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{iife(() => {
|
|
||||||
const [url] = createResource(
|
|
||||||
() => currentSession(),
|
|
||||||
async (session) => {
|
|
||||||
if (!session) return
|
|
||||||
let shareURL = session.share?.url
|
|
||||||
if (!shareURL) {
|
|
||||||
shareURL = await globalSDK.client.session
|
|
||||||
.share({ sessionID: session.id, directory: currentDirectory() })
|
|
||||||
.then((r) => r.data?.share?.url)
|
|
||||||
}
|
}
|
||||||
return shareURL
|
>
|
||||||
},
|
<Button as={A} href={`/${params.dir}/session`} icon="plus-small">
|
||||||
)
|
New session
|
||||||
return <Show when={url()}>{(url) => <TextField value={url()} readOnly copyable class="w-72" />}</Show>
|
</Button>
|
||||||
})}
|
</Tooltip>
|
||||||
</Popover>
|
</Show>
|
||||||
</Show>
|
</div>
|
||||||
</div>
|
<div class="flex items-center gap-4">
|
||||||
|
<Tooltip
|
||||||
|
class="hidden md:block shrink-0"
|
||||||
|
value={
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span>Toggle terminal</span>
|
||||||
|
<span class="text-icon-base text-12-medium">{command.keybind("terminal.toggle")}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={layout.terminal.toggle}>
|
||||||
|
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
||||||
|
<Icon
|
||||||
|
size="small"
|
||||||
|
name={layout.terminal.opened() ? "layout-bottom-full" : "layout-bottom"}
|
||||||
|
class="group-hover/terminal-toggle:hidden"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
size="small"
|
||||||
|
name="layout-bottom-partial"
|
||||||
|
class="hidden group-hover/terminal-toggle:inline-block"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
size="small"
|
||||||
|
name={layout.terminal.opened() ? "layout-bottom" : "layout-bottom-full"}
|
||||||
|
class="hidden group-active/terminal-toggle:inline-block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Show when={shareEnabled() && currentSession()}>
|
||||||
|
<Popover
|
||||||
|
title="Share session"
|
||||||
|
trigger={
|
||||||
|
<Tooltip class="shrink-0" value="Share session">
|
||||||
|
<IconButton icon="share" variant="ghost" class="" />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{iife(() => {
|
||||||
|
const [url] = createResource(
|
||||||
|
() => currentSession(),
|
||||||
|
async (session) => {
|
||||||
|
if (!session) return
|
||||||
|
let shareURL = session.share?.url
|
||||||
|
if (!shareURL) {
|
||||||
|
shareURL = await globalSDK.client.session
|
||||||
|
.share({ sessionID: session.id, directory: currentDirectory() })
|
||||||
|
.then((r) => r.data?.share?.url)
|
||||||
|
}
|
||||||
|
return shareURL
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Show when={url()}>
|
||||||
|
{(url) => <TextField value={url()} readOnly copyable class="w-72" />}
|
||||||
|
</Show>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Popover>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}}
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|||||||
import { DialogSelectModel } from "@/components/dialog-select-model"
|
import { DialogSelectModel } from "@/components/dialog-select-model"
|
||||||
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
|
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
|
||||||
import { useProviders } from "@/hooks/use-providers"
|
import { useProviders } from "@/hooks/use-providers"
|
||||||
import { useCommand, formatKeybind } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { persisted } from "@/utils/persist"
|
import { persisted } from "@/utils/persist"
|
||||||
import { Identifier } from "@opencode-ai/util/identifier"
|
import { Identifier } from "@/utils/id"
|
||||||
|
import { SessionContextUsage } from "@/components/session-context-usage"
|
||||||
|
|
||||||
const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"]
|
const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"]
|
||||||
const ACCEPTED_FILE_TYPES = [...ACCEPTED_IMAGE_TYPES, "application/pdf"]
|
const ACCEPTED_FILE_TYPES = [...ACCEPTED_IMAGE_TYPES, "application/pdf"]
|
||||||
@@ -102,6 +103,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
imageAttachments: ImageAttachmentPart[]
|
imageAttachments: ImageAttachmentPart[]
|
||||||
mode: "normal" | "shell"
|
mode: "normal" | "shell"
|
||||||
applyingHistory: boolean
|
applyingHistory: boolean
|
||||||
|
userHasEdited: boolean
|
||||||
}>({
|
}>({
|
||||||
popover: null,
|
popover: null,
|
||||||
historyIndex: -1,
|
historyIndex: -1,
|
||||||
@@ -111,6 +113,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
imageAttachments: [],
|
imageAttachments: [],
|
||||||
mode: "normal",
|
mode: "normal",
|
||||||
applyingHistory: false,
|
applyingHistory: false,
|
||||||
|
userHasEdited: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const MAX_HISTORY = 100
|
const MAX_HISTORY = 100
|
||||||
@@ -122,6 +125,14 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
entries: [],
|
entries: [],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
const [shellHistory, setShellHistory] = persisted(
|
||||||
|
"prompt-history-shell.v1",
|
||||||
|
createStore<{
|
||||||
|
entries: Prompt[]
|
||||||
|
}>({
|
||||||
|
entries: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const clonePromptParts = (prompt: Prompt): Prompt =>
|
const clonePromptParts = (prompt: Prompt): Prompt =>
|
||||||
prompt.map((part) => {
|
prompt.map((part) => {
|
||||||
@@ -139,6 +150,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const applyHistoryPrompt = (p: Prompt, position: "start" | "end") => {
|
const applyHistoryPrompt = (p: Prompt, position: "start" | "end") => {
|
||||||
const length = position === "start" ? 0 : promptLength(p)
|
const length = position === "start" ? 0 : promptLength(p)
|
||||||
setStore("applyingHistory", true)
|
setStore("applyingHistory", true)
|
||||||
|
setStore("userHasEdited", false)
|
||||||
prompt.set(p, length)
|
prompt.set(p, length)
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
editorRef.focus()
|
editorRef.focus()
|
||||||
@@ -440,6 +452,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
|
|
||||||
if (shouldReset) {
|
if (shouldReset) {
|
||||||
setStore("popover", null)
|
setStore("popover", null)
|
||||||
|
setStore("userHasEdited", false)
|
||||||
if (store.historyIndex >= 0 && !store.applyingHistory) {
|
if (store.historyIndex >= 0 && !store.applyingHistory) {
|
||||||
setStore("historyIndex", -1)
|
setStore("historyIndex", -1)
|
||||||
setStore("savedPrompt", null)
|
setStore("savedPrompt", null)
|
||||||
@@ -474,6 +487,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
setStore("savedPrompt", null)
|
setStore("savedPrompt", null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!store.applyingHistory) {
|
||||||
|
setStore("userHasEdited", true)
|
||||||
|
}
|
||||||
|
|
||||||
prompt.set(rawParts, cursorPosition)
|
prompt.set(rawParts, cursorPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +564,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
sessionID: params.id!,
|
sessionID: params.id!,
|
||||||
})
|
})
|
||||||
|
|
||||||
const addToHistory = (prompt: Prompt) => {
|
const addToHistory = (prompt: Prompt, mode: "normal" | "shell") => {
|
||||||
const text = prompt
|
const text = prompt
|
||||||
.map((p) => ("content" in p ? p.content : ""))
|
.map((p) => ("content" in p ? p.content : ""))
|
||||||
.join("")
|
.join("")
|
||||||
@@ -555,17 +572,21 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
if (!text) return
|
if (!text) return
|
||||||
|
|
||||||
const entry = clonePromptParts(prompt)
|
const entry = clonePromptParts(prompt)
|
||||||
const lastEntry = history.entries[0]
|
const currentHistory = mode === "shell" ? shellHistory : history
|
||||||
|
const setCurrentHistory = mode === "shell" ? setShellHistory : setHistory
|
||||||
|
const lastEntry = currentHistory.entries[0]
|
||||||
if (lastEntry) {
|
if (lastEntry) {
|
||||||
const lastText = lastEntry.map((p) => ("content" in p ? p.content : "")).join("")
|
const lastText = lastEntry.map((p) => ("content" in p ? p.content : "")).join("")
|
||||||
if (lastText === text) return
|
if (lastText === text) return
|
||||||
}
|
}
|
||||||
|
|
||||||
setHistory("entries", (entries) => [entry, ...entries].slice(0, MAX_HISTORY))
|
setCurrentHistory("entries", (entries) => [entry, ...entries].slice(0, MAX_HISTORY))
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigateHistory = (direction: "up" | "down") => {
|
const navigateHistory = (direction: "up" | "down") => {
|
||||||
const entries = history.entries
|
if (store.userHasEdited) return false
|
||||||
|
|
||||||
|
const entries = store.mode === "shell" ? shellHistory.entries : history.entries
|
||||||
const current = store.historyIndex
|
const current = store.historyIndex
|
||||||
|
|
||||||
if (direction === "up") {
|
if (direction === "up") {
|
||||||
@@ -693,9 +714,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
addToHistory(currentPrompt)
|
addToHistory(currentPrompt, store.mode)
|
||||||
setStore("historyIndex", -1)
|
setStore("historyIndex", -1)
|
||||||
setStore("savedPrompt", null)
|
setStore("savedPrompt", null)
|
||||||
|
setStore("userHasEdited", false)
|
||||||
|
|
||||||
let existing = info()
|
let existing = info()
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
@@ -868,8 +890,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
custom
|
custom
|
||||||
</span>
|
</span>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={cmd.keybind}>
|
<Show when={command.keybind(cmd.id)}>
|
||||||
<span class="text-12-regular text-text-subtle">{formatKeybind(cmd.keybind!)}</span>
|
<span class="text-12-regular text-text-subtle">{command.keybind(cmd.id)}</span>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
@@ -951,7 +973,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Show when={!prompt.dirty() && store.imageAttachments.length === 0}>
|
<Show when={!prompt.dirty() && store.imageAttachments.length === 0}>
|
||||||
<div class="absolute top-0 left-0 px-5 py-3 text-14-regular text-text-weak pointer-events-none">
|
<div class="absolute top-0 inset-x-0 px-5 py-3 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate">
|
||||||
{store.mode === "shell"
|
{store.mode === "shell"
|
||||||
? "Enter shell command..."
|
? "Enter shell command..."
|
||||||
: `Ask anything... "${PLACEHOLDERS[store.placeholder]}"`}
|
: `Ask anything... "${PLACEHOLDERS[store.placeholder]}"`}
|
||||||
@@ -969,28 +991,51 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={store.mode === "normal"}>
|
<Match when={store.mode === "normal"}>
|
||||||
<Select
|
<Tooltip
|
||||||
options={local.agent.list().map((agent) => agent.name)}
|
placement="top"
|
||||||
current={local.agent.current().name}
|
value={
|
||||||
onSelect={local.agent.set}
|
<div class="flex items-center gap-2">
|
||||||
class="capitalize"
|
<span>Cycle agent</span>
|
||||||
variant="ghost"
|
<span class="text-icon-base text-12-medium">{command.keybind("agent.cycle")}</span>
|
||||||
/>
|
</div>
|
||||||
<Button
|
|
||||||
as="div"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() =>
|
|
||||||
dialog.show(() =>
|
|
||||||
providers.paid().length > 0 ? <DialogSelectModel /> : <DialogSelectModelUnpaid />,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{local.model.current()?.name ?? "Select model"}
|
<Select
|
||||||
<span class="ml-0.5 text-text-weak text-12-regular">{local.model.current()?.provider.name}</span>
|
options={local.agent.list().map((agent) => agent.name)}
|
||||||
<Icon name="chevron-down" size="small" />
|
current={local.agent.current().name}
|
||||||
</Button>
|
onSelect={local.agent.set}
|
||||||
|
class="capitalize"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
value={
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span>Choose model</span>
|
||||||
|
<span class="text-icon-base text-12-medium">{command.keybind("model.choose")}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
as="div"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() =>
|
||||||
|
dialog.show(() =>
|
||||||
|
providers.paid().length > 0 ? <DialogSelectModel /> : <DialogSelectModelUnpaid />,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{local.model.current()?.name ?? "Select model"}
|
||||||
|
<span class="hidden md:block ml-0.5 text-text-weak text-12-regular">
|
||||||
|
{local.model.current()?.provider.name}
|
||||||
|
</span>
|
||||||
|
<Icon name="chevron-down" size="small" />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
<SessionContextUsage />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1 absolute right-2 bottom-2">
|
<div class="flex items-center gap-1 absolute right-2 bottom-2">
|
||||||
<input
|
<input
|
||||||
@@ -1004,15 +1049,17 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
e.currentTarget.value = ""
|
e.currentTarget.value = ""
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tooltip placement="top" value="Attach image">
|
<Show when={store.mode === "normal"}>
|
||||||
<IconButton
|
<Tooltip placement="top" value="Attach image">
|
||||||
type="button"
|
<IconButton
|
||||||
icon="photo"
|
type="button"
|
||||||
variant="ghost"
|
icon="photo"
|
||||||
class="h-10 w-8"
|
variant="ghost"
|
||||||
onClick={() => fileInputRef.click()}
|
class="h-10 w-8"
|
||||||
/>
|
onClick={() => fileInputRef.click()}
|
||||||
</Tooltip>
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Show>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement="top"
|
placement="top"
|
||||||
inactive={!prompt.dirty() && !working()}
|
inactive={!prompt.dirty() && !working()}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { createMemo, Show } from "solid-js"
|
||||||
|
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||||
|
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
|
||||||
|
import { useSync } from "@/context/sync"
|
||||||
|
import { useParams } from "@solidjs/router"
|
||||||
|
import { AssistantMessage } from "@opencode-ai/sdk/v2"
|
||||||
|
|
||||||
|
export function SessionContextUsage() {
|
||||||
|
const sync = useSync()
|
||||||
|
const params = useParams()
|
||||||
|
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
||||||
|
|
||||||
|
const cost = createMemo(() => {
|
||||||
|
const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
|
||||||
|
return new Intl.NumberFormat("en-US", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "USD",
|
||||||
|
}).format(total)
|
||||||
|
})
|
||||||
|
|
||||||
|
const context = createMemo(() => {
|
||||||
|
const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage
|
||||||
|
if (!last) return
|
||||||
|
const total =
|
||||||
|
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
|
||||||
|
const model = sync.data.provider.all.find((x) => x.id === last.providerID)?.models[last.modelID]
|
||||||
|
return {
|
||||||
|
tokens: total.toLocaleString(),
|
||||||
|
percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Show when={context?.()}>
|
||||||
|
{(ctx) => (
|
||||||
|
<Tooltip
|
||||||
|
openDelay={300}
|
||||||
|
value={
|
||||||
|
<div class="flex flex-col gap-1 p-2">
|
||||||
|
<div class="flex justify-between gap-4">
|
||||||
|
<span class="text-text-weaker">Tokens</span>
|
||||||
|
<span class="text-text-strong">{ctx().tokens}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between gap-4">
|
||||||
|
<span class="text-text-weaker">Usage</span>
|
||||||
|
<span class="text-text-strong">{ctx().percentage ?? 0}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between gap-4">
|
||||||
|
<span class="text-text-weaker">Cost</span>
|
||||||
|
<span class="text-text-strong">{cost()}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<span class="text-12-medium text-text-weak">{`${ctx().percentage ?? 0}%`}</span>
|
||||||
|
<ProgressCircle size={16} strokeWidth={2} percentage={ctx().percentage ?? 0} />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Show>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -119,7 +119,6 @@ function DialogCommand(props: { options: CommandOption[] }) {
|
|||||||
return (
|
return (
|
||||||
<Dialog title="Commands">
|
<Dialog title="Commands">
|
||||||
<List
|
<List
|
||||||
class="px-2.5"
|
|
||||||
search={{ placeholder: "Search commands", autofocus: true }}
|
search={{ placeholder: "Search commands", autofocus: true }}
|
||||||
emptyMessage="No commands found"
|
emptyMessage="No commands found"
|
||||||
items={() => props.options.filter((x) => !x.id.startsWith("suggested.") || !x.disabled)}
|
items={() => props.options.filter((x) => !x.id.startsWith("suggested.") || !x.disabled)}
|
||||||
@@ -226,6 +225,11 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
keybind(id: string) {
|
||||||
|
const option = options().find((x) => x.id === id || x.id === "suggested." + id)
|
||||||
|
if (!option?.keybind) return ""
|
||||||
|
return formatKeybind(option.keybind)
|
||||||
|
},
|
||||||
show: showPalette,
|
show: showPalette,
|
||||||
keybinds(enabled: boolean) {
|
keybinds(enabled: boolean) {
|
||||||
setSuspendCount((count) => count + (enabled ? -1 : 1))
|
setSuspendCount((count) => count + (enabled ? -1 : 1))
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
|
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
import { onCleanup } from "solid-js"
|
import { usePlatform } from "./platform"
|
||||||
|
|
||||||
export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleContext({
|
export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleContext({
|
||||||
name: "GlobalSDK",
|
name: "GlobalSDK",
|
||||||
init: (props: { url: string }) => {
|
init: (props: { url: string }) => {
|
||||||
const abort = new AbortController()
|
const eventSdk = createOpencodeClient({
|
||||||
const sdk = createOpencodeClient({
|
|
||||||
baseUrl: props.url,
|
baseUrl: props.url,
|
||||||
signal: abort.signal,
|
// signal: AbortSignal.timeout(1000 * 60 * 10),
|
||||||
throwOnError: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const emitter = createGlobalEmitter<{
|
const emitter = createGlobalEmitter<{
|
||||||
[key: string]: Event
|
[key: string]: Event
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
sdk.global.event().then(async (events) => {
|
eventSdk.global.event().then(async (events) => {
|
||||||
for await (const event of events.stream) {
|
for await (const event of events.stream) {
|
||||||
// console.log("event", event)
|
// console.log("event", event)
|
||||||
emitter.emit(event.directory ?? "global", event.payload)
|
emitter.emit(event.directory ?? "global", event.payload)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onCleanup(() => {
|
const platform = usePlatform()
|
||||||
abort.abort()
|
const sdk = createOpencodeClient({
|
||||||
|
baseUrl: props.url,
|
||||||
|
signal: AbortSignal.timeout(1000 * 60 * 10),
|
||||||
|
fetch: platform.fetch,
|
||||||
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return { url: props.url, client: sdk, event: emitter }
|
return { url: props.url, client: sdk, event: emitter }
|
||||||
|
|||||||
@@ -18,9 +18,12 @@ import {
|
|||||||
} from "@opencode-ai/sdk/v2/client"
|
} from "@opencode-ai/sdk/v2/client"
|
||||||
import { createStore, produce, reconcile } from "solid-js/store"
|
import { createStore, produce, reconcile } from "solid-js/store"
|
||||||
import { Binary } from "@opencode-ai/util/binary"
|
import { Binary } from "@opencode-ai/util/binary"
|
||||||
|
import { retry } from "@opencode-ai/util/retry"
|
||||||
import { useGlobalSDK } from "./global-sdk"
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
import { ErrorPage, type InitError } from "../pages/error"
|
import { ErrorPage, type InitError } from "../pages/error"
|
||||||
import { createContext, useContext, onMount, type ParentProps, Switch, Match } from "solid-js"
|
import { createContext, useContext, onMount, type ParentProps, Switch, Match } from "solid-js"
|
||||||
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
ready: boolean
|
ready: boolean
|
||||||
@@ -72,6 +75,7 @@ function createGlobalSync() {
|
|||||||
|
|
||||||
const children: Record<string, ReturnType<typeof createStore<State>>> = {}
|
const children: Record<string, ReturnType<typeof createStore<State>>> = {}
|
||||||
function child(directory: string) {
|
function child(directory: string) {
|
||||||
|
if (!directory) console.error("No directory provided")
|
||||||
if (!children[directory]) {
|
if (!children[directory]) {
|
||||||
setGlobalStore("children", directory, {
|
setGlobalStore("children", directory, {
|
||||||
project: "",
|
project: "",
|
||||||
@@ -117,11 +121,13 @@ function createGlobalSync() {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("Failed to load sessions", err)
|
console.error("Failed to load sessions", err)
|
||||||
setGlobalStore("error", err)
|
const project = getFilename(directory)
|
||||||
|
showToast({ title: `Failed to load sessions for ${project}`, description: err.message })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bootstrapInstance(directory: string) {
|
async function bootstrapInstance(directory: string) {
|
||||||
|
if (!directory) return
|
||||||
const [, setStore] = child(directory)
|
const [, setStore] = child(directory)
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: globalSDK.url,
|
baseUrl: globalSDK.url,
|
||||||
@@ -140,7 +146,7 @@ function createGlobalSync() {
|
|||||||
changes: () => sdk.file.status().then((x) => setStore("changes", x.data!)),
|
changes: () => sdk.file.status().then((x) => setStore("changes", x.data!)),
|
||||||
node: () => sdk.file.list({ path: "/" }).then((x) => setStore("node", x.data!)),
|
node: () => sdk.file.list({ path: "/" }).then((x) => setStore("node", x.data!)),
|
||||||
}
|
}
|
||||||
await Promise.all(Object.values(load).map((p) => p().catch((e) => setGlobalStore("error", e))))
|
await Promise.all(Object.values(load).map((p) => retry(p).catch((e) => setGlobalStore("error", e))))
|
||||||
.then(() => setStore("ready", true))
|
.then(() => setStore("ready", true))
|
||||||
.catch((e) => setGlobalStore("error", e))
|
.catch((e) => setGlobalStore("error", e))
|
||||||
}
|
}
|
||||||
@@ -290,21 +296,29 @@ function createGlobalSync() {
|
|||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
globalSDK.client.path.get().then((x) => {
|
retry(() =>
|
||||||
setGlobalStore("path", x.data!)
|
globalSDK.client.path.get().then((x) => {
|
||||||
}),
|
setGlobalStore("path", x.data!)
|
||||||
globalSDK.client.project.list().then(async (x) => {
|
}),
|
||||||
setGlobalStore(
|
),
|
||||||
"project",
|
retry(() =>
|
||||||
x.data!.filter((p) => !p.worktree.includes("opencode-test")).sort((a, b) => a.id.localeCompare(b.id)),
|
globalSDK.client.project.list().then(async (x) => {
|
||||||
)
|
setGlobalStore(
|
||||||
}),
|
"project",
|
||||||
globalSDK.client.provider.list().then((x) => {
|
x.data!.filter((p) => !p.worktree.includes("opencode-test")).sort((a, b) => a.id.localeCompare(b.id)),
|
||||||
setGlobalStore("provider", x.data ?? {})
|
)
|
||||||
}),
|
}),
|
||||||
globalSDK.client.provider.auth().then((x) => {
|
),
|
||||||
setGlobalStore("provider_auth", x.data ?? {})
|
retry(() =>
|
||||||
}),
|
globalSDK.client.provider.list().then((x) => {
|
||||||
|
setGlobalStore("provider", x.data ?? {})
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
retry(() =>
|
||||||
|
globalSDK.client.provider.auth().then((x) => {
|
||||||
|
setGlobalStore("provider_auth", x.data ?? {})
|
||||||
|
}),
|
||||||
|
),
|
||||||
])
|
])
|
||||||
.then(() => setGlobalStore("ready", true))
|
.then(() => setGlobalStore("ready", true))
|
||||||
.catch((e) => setGlobalStore("error", e))
|
.catch((e) => setGlobalStore("error", e))
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ type SessionTabs = {
|
|||||||
all: string[]
|
all: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean }
|
||||||
|
|
||||||
export const { use: useLayout, provider: LayoutProvider } = createSimpleContext({
|
export const { use: useLayout, provider: LayoutProvider } = createSimpleContext({
|
||||||
name: "Layout",
|
name: "Layout",
|
||||||
init: () => {
|
init: () => {
|
||||||
@@ -44,8 +46,8 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
opened: false,
|
opened: false,
|
||||||
height: 280,
|
height: 280,
|
||||||
},
|
},
|
||||||
review: {
|
session: {
|
||||||
state: "pane" as "pane" | "tab",
|
width: 600,
|
||||||
},
|
},
|
||||||
sessionTabs: {} as Record<string, SessionTabs>,
|
sessionTabs: {} as Record<string, SessionTabs>,
|
||||||
}),
|
}),
|
||||||
@@ -61,21 +63,22 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
|
|
||||||
function enrich(project: { worktree: string; expanded: boolean }) {
|
function enrich(project: { worktree: string; expanded: boolean }) {
|
||||||
const metadata = globalSync.data.project.find((x) => x.worktree === project.worktree)
|
const metadata = globalSync.data.project.find((x) => x.worktree === project.worktree)
|
||||||
if (!metadata) return []
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
...project,
|
...project,
|
||||||
...metadata,
|
...(metadata ?? {}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function colorize(project: Project & { expanded: boolean }) {
|
function colorize(project: LocalProject) {
|
||||||
if (project.icon?.color) return project
|
if (project.icon?.color) return project
|
||||||
const color = pickAvailableColor()
|
const color = pickAvailableColor()
|
||||||
usedColors.add(color)
|
usedColors.add(color)
|
||||||
project.icon = { ...project.icon, color }
|
project.icon = { ...project.icon, color }
|
||||||
globalSdk.client.project.update({ projectID: project.id, icon: { color } })
|
if (project.id) {
|
||||||
|
globalSdk.client.project.update({ projectID: project.id, icon: { color } })
|
||||||
|
}
|
||||||
return project
|
return project
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +98,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
projects: {
|
projects: {
|
||||||
list,
|
list,
|
||||||
open(directory: string) {
|
open(directory: string) {
|
||||||
if (store.projects.find((x) => x.worktree === directory)) return
|
if (store.projects.find((x) => x.worktree === directory)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
globalSync.project.loadSessions(directory)
|
globalSync.project.loadSessions(directory)
|
||||||
setStore("projects", (x) => [{ worktree: directory, expanded: true }, ...x])
|
setStore("projects", (x) => [{ worktree: directory, expanded: true }, ...x])
|
||||||
},
|
},
|
||||||
@@ -103,10 +108,12 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
setStore("projects", (x) => x.filter((x) => x.worktree !== directory))
|
setStore("projects", (x) => x.filter((x) => x.worktree !== directory))
|
||||||
},
|
},
|
||||||
expand(directory: string) {
|
expand(directory: string) {
|
||||||
setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: true } : x)))
|
const index = store.projects.findIndex((x) => x.worktree === directory)
|
||||||
|
if (index !== -1) setStore("projects", index, "expanded", true)
|
||||||
},
|
},
|
||||||
collapse(directory: string) {
|
collapse(directory: string) {
|
||||||
setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: false } : x)))
|
const index = store.projects.findIndex((x) => x.worktree === directory)
|
||||||
|
if (index !== -1) setStore("projects", index, "expanded", false)
|
||||||
},
|
},
|
||||||
move(directory: string, toIndex: number) {
|
move(directory: string, toIndex: number) {
|
||||||
setStore("projects", (projects) => {
|
setStore("projects", (projects) => {
|
||||||
@@ -151,13 +158,14 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
setStore("terminal", "height", height)
|
setStore("terminal", "height", height)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
review: {
|
session: {
|
||||||
state: createMemo(() => store.review?.state ?? "closed"),
|
width: createMemo(() => store.session?.width ?? 600),
|
||||||
pane() {
|
resize(width: number) {
|
||||||
setStore("review", "state", "pane")
|
if (!store.session) {
|
||||||
},
|
setStore("session", { width })
|
||||||
tab() {
|
} else {
|
||||||
setStore("review", "state", "tab")
|
setStore("session", "width", width)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tabs(sessionKey: string) {
|
tabs(sessionKey: string) {
|
||||||
@@ -181,14 +189,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async open(tab: string) {
|
async open(tab: string) {
|
||||||
if (tab === "chat") {
|
|
||||||
if (!store.sessionTabs[sessionKey]) {
|
|
||||||
setStore("sessionTabs", sessionKey, { all: [], active: undefined })
|
|
||||||
} else {
|
|
||||||
setStore("sessionTabs", sessionKey, "active", undefined)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const current = store.sessionTabs[sessionKey] ?? { all: [] }
|
const current = store.sessionTabs[sessionKey] ?? { all: [] }
|
||||||
if (tab !== "review") {
|
if (tab !== "review") {
|
||||||
if (!current.all.includes(tab)) {
|
if (!current.all.includes(tab)) {
|
||||||
|
|||||||
@@ -337,6 +337,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
const load = async (path: string) => {
|
const load = async (path: string) => {
|
||||||
const relativePath = relative(path)
|
const relativePath = relative(path)
|
||||||
await sdk.client.file.read({ path: relativePath }).then((x) => {
|
await sdk.client.file.read({ path: relativePath }).then((x) => {
|
||||||
|
if (!store.node[relativePath]) return
|
||||||
setStore(
|
setStore(
|
||||||
"node",
|
"node",
|
||||||
relativePath,
|
relativePath,
|
||||||
@@ -359,7 +360,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
const init = async (path: string) => {
|
const init = async (path: string) => {
|
||||||
const relativePath = relative(path)
|
const relativePath = relative(path)
|
||||||
if (!store.node[relativePath]) await fetch(path)
|
if (!store.node[relativePath]) await fetch(path)
|
||||||
if (store.node[relativePath].loaded) return
|
if (store.node[relativePath]?.loaded) return
|
||||||
return load(relativePath)
|
return load(relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,7 +380,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
context.addActive()
|
context.addActive()
|
||||||
if (options?.pinned) setStore("node", path, "pinned", true)
|
if (options?.pinned) setStore("node", path, "pinned", true)
|
||||||
if (options?.view && store.node[relativePath].view === undefined) setStore("node", path, "view", options.view)
|
if (options?.view && store.node[relativePath].view === undefined) setStore("node", path, "view", options.view)
|
||||||
if (store.node[relativePath].loaded) return
|
if (store.node[relativePath]?.loaded) return
|
||||||
return load(relativePath)
|
return load(relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +426,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
init,
|
init,
|
||||||
expand(path: string) {
|
expand(path: string) {
|
||||||
setStore("node", path, "expanded", true)
|
setStore("node", path, "expanded", true)
|
||||||
if (store.node[path].loaded) return
|
if (store.node[path]?.loaded) return
|
||||||
setStore("node", path, "loaded", true)
|
setStore("node", path, "loaded", true)
|
||||||
list(path)
|
list(path)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ export type Platform = {
|
|||||||
/** Platform discriminator */
|
/** Platform discriminator */
|
||||||
platform: "web" | "tauri"
|
platform: "web" | "tauri"
|
||||||
|
|
||||||
|
/** Open a URL in the default browser */
|
||||||
|
openLink(url: string): void
|
||||||
|
|
||||||
|
/** Restart the app */
|
||||||
|
restart(): Promise<void>
|
||||||
|
|
||||||
/** Open native directory picker dialog (Tauri only) */
|
/** Open native directory picker dialog (Tauri only) */
|
||||||
openDirectoryPickerDialog?(opts?: { title?: string; multiple?: boolean }): Promise<string | string[] | null>
|
openDirectoryPickerDialog?(opts?: { title?: string; multiple?: boolean }): Promise<string | string[] | null>
|
||||||
|
|
||||||
@@ -14,9 +20,6 @@ export type Platform = {
|
|||||||
/** Save file picker dialog (Tauri only) */
|
/** Save file picker dialog (Tauri only) */
|
||||||
saveFilePickerDialog?(opts?: { title?: string; defaultPath?: string }): Promise<string | null>
|
saveFilePickerDialog?(opts?: { title?: string; defaultPath?: string }): Promise<string | null>
|
||||||
|
|
||||||
/** Open a URL in the default browser */
|
|
||||||
openLink(url: string): void
|
|
||||||
|
|
||||||
/** Storage mechanism, defaults to localStorage */
|
/** Storage mechanism, defaults to localStorage */
|
||||||
storage?: (name?: string) => SyncStorage | AsyncStorage
|
storage?: (name?: string) => SyncStorage | AsyncStorage
|
||||||
|
|
||||||
@@ -25,6 +28,9 @@ export type Platform = {
|
|||||||
|
|
||||||
/** Install updates (Tauri only) */
|
/** Install updates (Tauri only) */
|
||||||
update?(): Promise<void>
|
update?(): Promise<void>
|
||||||
|
|
||||||
|
/** Fetch override */
|
||||||
|
fetch?: typeof fetch
|
||||||
}
|
}
|
||||||
|
|
||||||
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
|
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
|
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
import { onCleanup } from "solid-js"
|
|
||||||
import { useGlobalSDK } from "./global-sdk"
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
|
import { usePlatform } from "./platform"
|
||||||
|
|
||||||
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||||
name: "SDK",
|
name: "SDK",
|
||||||
init: (props: { directory: string }) => {
|
init: (props: { directory: string }) => {
|
||||||
|
const platform = usePlatform()
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
const abort = new AbortController()
|
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: globalSDK.url,
|
baseUrl: globalSDK.url,
|
||||||
signal: abort.signal,
|
signal: AbortSignal.timeout(1000 * 60 * 10),
|
||||||
|
fetch: platform.fetch,
|
||||||
directory: props.directory,
|
directory: props.directory,
|
||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
@@ -24,10 +25,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||||||
emitter.emit(event.type, event)
|
emitter.emit(event.type, event)
|
||||||
})
|
})
|
||||||
|
|
||||||
onCleanup(() => {
|
|
||||||
abort.abort()
|
|
||||||
})
|
|
||||||
|
|
||||||
return { directory: props.directory, client: sdk, event: emitter, url: globalSDK.url }
|
return { directory: props.directory, client: sdk, event: emitter, url: globalSDK.url }
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { produce } from "solid-js/store"
|
import { produce } from "solid-js/store"
|
||||||
import { createMemo } from "solid-js"
|
import { createMemo } from "solid-js"
|
||||||
import { Binary } from "@opencode-ai/util/binary"
|
import { Binary } from "@opencode-ai/util/binary"
|
||||||
|
import { retry } from "@opencode-ai/util/retry"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import { useGlobalSync } from "./global-sync"
|
import { useGlobalSync } from "./global-sync"
|
||||||
import { useSDK } from "./sdk"
|
import { useSDK } from "./sdk"
|
||||||
@@ -61,10 +62,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
},
|
},
|
||||||
async sync(sessionID: string, _isRetry = false) {
|
async sync(sessionID: string, _isRetry = false) {
|
||||||
const [session, messages, todo, diff] = await Promise.all([
|
const [session, messages, todo, diff] = await Promise.all([
|
||||||
sdk.client.session.get({ sessionID }, { throwOnError: true }),
|
retry(() => sdk.client.session.get({ sessionID })),
|
||||||
sdk.client.session.messages({ sessionID, limit: 100 }),
|
retry(() => sdk.client.session.messages({ sessionID, limit: 100 })),
|
||||||
sdk.client.session.todo({ sessionID }),
|
retry(() => sdk.client.session.todo({ sessionID })),
|
||||||
sdk.client.session.diff({ sessionID }),
|
retry(() => sdk.client.session.diff({ sessionID })),
|
||||||
])
|
])
|
||||||
setStore(
|
setStore(
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ const platform: Platform = {
|
|||||||
openLink(url: string) {
|
openLink(url: string) {
|
||||||
window.open(url, "_blank")
|
window.open(url, "_blank")
|
||||||
},
|
},
|
||||||
|
restart: async () => {
|
||||||
|
window.location.reload()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
render(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { TextField } from "@opencode-ai/ui/text-field"
|
import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
import { Logo } from "@opencode-ai/ui/logo"
|
import { Logo } from "@opencode-ai/ui/logo"
|
||||||
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Component } from "solid-js"
|
import { Component } from "solid-js"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
@@ -9,9 +10,17 @@ export type InitError = {
|
|||||||
data: Record<string, unknown>
|
data: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatError(error: InitError | undefined): string {
|
function isInitError(error: unknown): error is InitError {
|
||||||
if (!error) return "Unknown error"
|
return (
|
||||||
|
typeof error === "object" &&
|
||||||
|
error !== null &&
|
||||||
|
"name" in error &&
|
||||||
|
"data" in error &&
|
||||||
|
typeof (error as InitError).data === "object"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatInitError(error: InitError): string {
|
||||||
const data = error.data
|
const data = error.data
|
||||||
switch (error.name) {
|
switch (error.name) {
|
||||||
case "MCPFailed":
|
case "MCPFailed":
|
||||||
@@ -53,8 +62,36 @@ function formatError(error: InitError | undefined): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatErrorChain(error: unknown, depth = 0): string {
|
||||||
|
if (!error) return "Unknown error"
|
||||||
|
|
||||||
|
const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : ""
|
||||||
|
|
||||||
|
if (isInitError(error)) {
|
||||||
|
return indent + formatInitError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
const parts = [indent + `${error.name}: ${error.message}`]
|
||||||
|
if (error.stack) {
|
||||||
|
parts.push(error.stack)
|
||||||
|
}
|
||||||
|
if (error.cause) {
|
||||||
|
parts.push(formatErrorChain(error.cause, depth + 1))
|
||||||
|
}
|
||||||
|
return parts.join("\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof error === "string") return indent + error
|
||||||
|
return indent + JSON.stringify(error, null, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatError(error: unknown): string {
|
||||||
|
return formatErrorChain(error, 0)
|
||||||
|
}
|
||||||
|
|
||||||
interface ErrorPageProps {
|
interface ErrorPageProps {
|
||||||
error: InitError | undefined
|
error: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
||||||
@@ -76,6 +113,9 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
|||||||
label="Error Details"
|
label="Error Details"
|
||||||
hideLabel
|
hideLabel
|
||||||
/>
|
/>
|
||||||
|
<Button size="large" onClick={platform.restart}>
|
||||||
|
Restart
|
||||||
|
</Button>
|
||||||
<div class="flex items-center justify-center gap-1">
|
<div class="flex items-center justify-center gap-1">
|
||||||
Please report this error to the OpenCode team
|
Please report this error to the OpenCode team
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
createSignal,
|
createSignal,
|
||||||
For,
|
For,
|
||||||
Match,
|
Match,
|
||||||
|
onCleanup,
|
||||||
onMount,
|
onMount,
|
||||||
ParentProps,
|
ParentProps,
|
||||||
Show,
|
Show,
|
||||||
@@ -12,7 +13,7 @@ import {
|
|||||||
} from "solid-js"
|
} from "solid-js"
|
||||||
import { DateTime } from "luxon"
|
import { DateTime } from "luxon"
|
||||||
import { A, useNavigate, useParams } from "@solidjs/router"
|
import { A, useNavigate, useParams } from "@solidjs/router"
|
||||||
import { useLayout, getAvatarColors } from "@/context/layout"
|
import { useLayout, getAvatarColors, LocalProject } from "@/context/layout"
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
import { base64Decode, base64Encode } from "@opencode-ai/util/encode"
|
import { base64Decode, base64Encode } from "@opencode-ai/util/encode"
|
||||||
import { Avatar } from "@opencode-ai/ui/avatar"
|
import { Avatar } from "@opencode-ai/ui/avatar"
|
||||||
@@ -26,7 +27,7 @@ import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
|||||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
import { Spinner } from "@opencode-ai/ui/spinner"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
||||||
import { Session, Project } from "@opencode-ai/sdk/v2/client"
|
import { Session } from "@opencode-ai/sdk/v2/client"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import { createStore, produce } from "solid-js/store"
|
import { createStore, produce } from "solid-js/store"
|
||||||
import {
|
import {
|
||||||
@@ -53,9 +54,29 @@ export default function Layout(props: ParentProps) {
|
|||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
lastSession: {} as { [directory: string]: string },
|
lastSession: {} as { [directory: string]: string },
|
||||||
activeDraggable: undefined as string | undefined,
|
activeDraggable: undefined as string | undefined,
|
||||||
|
mobileSidebarOpen: false,
|
||||||
|
mobileProjectsExpanded: {} as Record<string, boolean>,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const mobileSidebar = {
|
||||||
|
open: () => store.mobileSidebarOpen,
|
||||||
|
show: () => setStore("mobileSidebarOpen", true),
|
||||||
|
hide: () => setStore("mobileSidebarOpen", false),
|
||||||
|
toggle: () => setStore("mobileSidebarOpen", (x) => !x),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mobileProjects = {
|
||||||
|
expanded: (directory: string) => store.mobileProjectsExpanded[directory] ?? true,
|
||||||
|
expand: (directory: string) => setStore("mobileProjectsExpanded", directory, true),
|
||||||
|
collapse: (directory: string) => setStore("mobileProjectsExpanded", directory, false),
|
||||||
|
}
|
||||||
|
|
||||||
let scrollContainerRef: HTMLDivElement | undefined
|
let scrollContainerRef: HTMLDivElement | undefined
|
||||||
|
const xlQuery = window.matchMedia("(min-width: 1280px)")
|
||||||
|
const [isLargeViewport, setIsLargeViewport] = createSignal(xlQuery.matches)
|
||||||
|
const handleViewportChange = (e: MediaQueryListEvent) => setIsLargeViewport(e.matches)
|
||||||
|
xlQuery.addEventListener("change", handleViewportChange)
|
||||||
|
onCleanup(() => xlQuery.removeEventListener("change", handleViewportChange))
|
||||||
|
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
@@ -69,7 +90,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (platform.checkUpdate && platform.update) {
|
if (platform.checkUpdate && platform.update && platform.restart) {
|
||||||
const { updateAvailable, version } = await platform.checkUpdate()
|
const { updateAvailable, version } = await platform.checkUpdate()
|
||||||
if (updateAvailable) {
|
if (updateAvailable) {
|
||||||
showToast({
|
showToast({
|
||||||
@@ -80,7 +101,10 @@ export default function Layout(props: ParentProps) {
|
|||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
label: "Install and restart",
|
label: "Install and restart",
|
||||||
onClick: () => platform!.update!(),
|
onClick: async () => {
|
||||||
|
await platform.update!()
|
||||||
|
await platform.restart!()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Not yet",
|
label: "Not yet",
|
||||||
@@ -267,11 +291,13 @@ export default function Layout(props: ParentProps) {
|
|||||||
if (!directory) return
|
if (!directory) return
|
||||||
const lastSession = store.lastSession[directory]
|
const lastSession = store.lastSession[directory]
|
||||||
navigate(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`)
|
navigate(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`)
|
||||||
|
mobileSidebar.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateToSession(session: Session | undefined) {
|
function navigateToSession(session: Session | undefined) {
|
||||||
if (!session) return
|
if (!session) return
|
||||||
navigate(`/${params.dir}/session/${session?.id}`)
|
navigate(`/${params.dir}/session/${session?.id}`)
|
||||||
|
mobileSidebar.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
function openProject(directory: string, navigate = true) {
|
function openProject(directory: string, navigate = true) {
|
||||||
@@ -310,8 +336,12 @@ export default function Layout(props: ParentProps) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48
|
if (isLargeViewport()) {
|
||||||
document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`)
|
const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48
|
||||||
|
document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`)
|
||||||
|
} else {
|
||||||
|
document.documentElement.style.setProperty("--dialog-left-margin", "0px")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function getDraggableId(event: unknown): string | undefined {
|
function getDraggableId(event: unknown): string | undefined {
|
||||||
@@ -345,7 +375,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ProjectAvatar = (props: {
|
const ProjectAvatar = (props: {
|
||||||
project: Project
|
project: LocalProject
|
||||||
class?: string
|
class?: string
|
||||||
expandable?: boolean
|
expandable?: boolean
|
||||||
notify?: boolean
|
notify?: boolean
|
||||||
@@ -388,7 +418,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProjectVisual = (props: { project: Project & { expanded: boolean }; class?: string }): JSX.Element => {
|
const ProjectVisual = (props: { project: LocalProject; class?: string }): JSX.Element => {
|
||||||
const name = createMemo(() => getFilename(props.project.worktree))
|
const name = createMemo(() => getFilename(props.project.worktree))
|
||||||
const current = createMemo(() => base64Decode(params.dir ?? ""))
|
const current = createMemo(() => base64Decode(params.dir ?? ""))
|
||||||
return (
|
return (
|
||||||
@@ -424,9 +454,10 @@ export default function Layout(props: ParentProps) {
|
|||||||
const SessionItem = (props: {
|
const SessionItem = (props: {
|
||||||
session: Session
|
session: Session
|
||||||
slug: string
|
slug: string
|
||||||
project: Project
|
project: LocalProject
|
||||||
depth?: number
|
depth?: number
|
||||||
childrenMap: Map<string, Session[]>
|
childrenMap: Map<string, Session[]>
|
||||||
|
mobile?: boolean
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const notification = useNotification()
|
const notification = useNotification()
|
||||||
const depth = props.depth ?? 0
|
const depth = props.depth ?? 0
|
||||||
@@ -447,7 +478,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
hover:bg-surface-raised-base-hover focus-within:bg-surface-raised-base-hover has-[.active]:bg-surface-raised-base-hover"
|
hover:bg-surface-raised-base-hover focus-within:bg-surface-raised-base-hover has-[.active]:bg-surface-raised-base-hover"
|
||||||
style={{ "padding-left": `${16 + depth * 12}px` }}
|
style={{ "padding-left": `${16 + depth * 12}px` }}
|
||||||
>
|
>
|
||||||
<Tooltip placement="right" value={props.session.title} gutter={10}>
|
<Tooltip placement={props.mobile ? "bottom" : "right"} value={props.session.title} gutter={10}>
|
||||||
<A
|
<A
|
||||||
href={`${props.slug}/session/${props.session.id}`}
|
href={`${props.slug}/session/${props.session.id}`}
|
||||||
class="flex flex-col min-w-0 text-left w-full focus:outline-none"
|
class="flex flex-col min-w-0 text-left w-full focus:outline-none"
|
||||||
@@ -494,7 +525,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
</A>
|
</A>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div class="hidden group-hover/session:flex group-active/session:flex group-focus-within/session:flex text-text-base gap-1 items-center absolute top-1 right-1">
|
<div class="hidden group-hover/session:flex group-active/session:flex group-focus-within/session:flex text-text-base gap-1 items-center absolute top-1 right-1">
|
||||||
<Tooltip placement="right" value="Archive session">
|
<Tooltip placement={props.mobile ? "bottom" : "right"} value="Archive session">
|
||||||
<IconButton icon="archive" variant="ghost" onClick={() => archiveSession(props.session)} />
|
<IconButton icon="archive" variant="ghost" onClick={() => archiveSession(props.session)} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
@@ -507,6 +538,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
project={props.project}
|
project={props.project}
|
||||||
depth={depth + 1}
|
depth={depth + 1}
|
||||||
childrenMap={props.childrenMap}
|
childrenMap={props.childrenMap}
|
||||||
|
mobile={props.mobile}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
@@ -514,8 +546,9 @@ export default function Layout(props: ParentProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SortableProject = (props: { project: Project & { expanded: boolean } }): JSX.Element => {
|
const SortableProject = (props: { project: LocalProject; mobile?: boolean }): JSX.Element => {
|
||||||
const sortable = createSortable(props.project.worktree)
|
const sortable = createSortable(props.project.worktree)
|
||||||
|
const showExpanded = createMemo(() => props.mobile || layout.sidebar.opened())
|
||||||
const slug = createMemo(() => base64Encode(props.project.worktree))
|
const slug = createMemo(() => base64Encode(props.project.worktree))
|
||||||
const name = createMemo(() => getFilename(props.project.worktree))
|
const name = createMemo(() => getFilename(props.project.worktree))
|
||||||
const [store, setProjectStore] = globalSync.child(props.project.worktree)
|
const [store, setProjectStore] = globalSync.child(props.project.worktree)
|
||||||
@@ -539,13 +572,24 @@ export default function Layout(props: ParentProps) {
|
|||||||
setProjectStore("limit", (limit) => limit + 5)
|
setProjectStore("limit", (limit) => limit + 5)
|
||||||
await globalSync.project.loadSessions(props.project.worktree)
|
await globalSync.project.loadSessions(props.project.worktree)
|
||||||
}
|
}
|
||||||
const [expanded, setExpanded] = createSignal(true)
|
const isExpanded = createMemo(() =>
|
||||||
|
props.mobile ? mobileProjects.expanded(props.project.worktree) : props.project.expanded,
|
||||||
|
)
|
||||||
|
const handleOpenChange = (open: boolean) => {
|
||||||
|
if (props.mobile) {
|
||||||
|
if (open) mobileProjects.expand(props.project.worktree)
|
||||||
|
else mobileProjects.collapse(props.project.worktree)
|
||||||
|
} else {
|
||||||
|
if (open) layout.projects.expand(props.project.worktree)
|
||||||
|
else layout.projects.collapse(props.project.worktree)
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
|
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={layout.sidebar.opened()}>
|
<Match when={showExpanded()}>
|
||||||
<Collapsible variant="ghost" defaultOpen class="gap-2 shrink-0" onOpenChange={setExpanded}>
|
<Collapsible variant="ghost" open={isExpanded()} class="gap-2 shrink-0" onOpenChange={handleOpenChange}>
|
||||||
<Button
|
<Button
|
||||||
as={"div"}
|
as={"div"}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -556,7 +600,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
project={props.project}
|
project={props.project}
|
||||||
class="group-hover/session:hidden"
|
class="group-hover/session:hidden"
|
||||||
expandable
|
expandable
|
||||||
notify={!expanded()}
|
notify={!isExpanded()}
|
||||||
/>
|
/>
|
||||||
<span class="truncate text-14-medium text-text-strong">{name()}</span>
|
<span class="truncate text-14-medium text-text-strong">{name()}</span>
|
||||||
</Collapsible.Trigger>
|
</Collapsible.Trigger>
|
||||||
@@ -585,6 +629,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
slug={slug()}
|
slug={slug()}
|
||||||
project={props.project}
|
project={props.project}
|
||||||
childrenMap={childSessionsByParent()}
|
childrenMap={childSessionsByParent()}
|
||||||
|
mobile={props.mobile}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
@@ -595,7 +640,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
>
|
>
|
||||||
<div class="flex items-center self-stretch w-full">
|
<div class="flex items-center self-stretch w-full">
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0">
|
||||||
<Tooltip placement="right" value="New session">
|
<Tooltip placement={props.mobile ? "bottom" : "right"} value="New session">
|
||||||
<A
|
<A
|
||||||
href={`${slug()}/session`}
|
href={`${slug()}/session`}
|
||||||
class="flex flex-col gap-1 min-w-0 text-left w-full focus:outline-none"
|
class="flex flex-col gap-1 min-w-0 text-left w-full focus:outline-none"
|
||||||
@@ -650,31 +695,23 @@ export default function Layout(props: ParentProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const SidebarContent = (sidebarProps: { mobile?: boolean }) => {
|
||||||
<div class="relative flex-1 min-h-0 flex flex-col">
|
const expanded = () => sidebarProps.mobile || layout.sidebar.opened()
|
||||||
<Header navigateToProject={navigateToProject} navigateToSession={navigateToSession} />
|
return (
|
||||||
<div class="flex-1 min-h-0 flex">
|
<>
|
||||||
<div
|
<div class="flex flex-col items-start self-stretch gap-4 p-2 min-h-0 overflow-hidden">
|
||||||
classList={{
|
<Show when={!sidebarProps.mobile}>
|
||||||
"relative @container w-12 pb-5 shrink-0 bg-background-base": true,
|
<Tooltip
|
||||||
"flex flex-col gap-5.5 items-start self-stretch justify-between": true,
|
class="shrink-0"
|
||||||
"border-r border-border-weak-base contain-strict": true,
|
placement="right"
|
||||||
}}
|
value={
|
||||||
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
|
<div class="flex items-center gap-2">
|
||||||
>
|
<span>Toggle sidebar</span>
|
||||||
<Show when={layout.sidebar.opened()}>
|
<span class="text-icon-base text-12-medium">{command.keybind("sidebar.toggle")}</span>
|
||||||
<ResizeHandle
|
</div>
|
||||||
direction="horizontal"
|
}
|
||||||
size={layout.sidebar.width()}
|
inactive={expanded()}
|
||||||
min={150}
|
>
|
||||||
max={window.innerWidth * 0.3}
|
|
||||||
collapseThreshold={80}
|
|
||||||
onResize={layout.sidebar.resize}
|
|
||||||
onCollapse={layout.sidebar.close}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
<div class="flex flex-col items-start self-stretch gap-4 p-2 min-h-0 overflow-hidden">
|
|
||||||
<Tooltip class="shrink-0" placement="right" value="Toggle sidebar" inactive={layout.sidebar.opened()}>
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -705,101 +742,160 @@ export default function Layout(props: ParentProps) {
|
|||||||
</Show>
|
</Show>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<DragDropProvider
|
</Show>
|
||||||
onDragStart={handleDragStart}
|
<DragDropProvider
|
||||||
onDragEnd={handleDragEnd}
|
onDragStart={handleDragStart}
|
||||||
onDragOver={handleDragOver}
|
onDragEnd={handleDragEnd}
|
||||||
collisionDetector={closestCenter}
|
onDragOver={handleDragOver}
|
||||||
|
collisionDetector={closestCenter}
|
||||||
|
>
|
||||||
|
<DragDropSensors />
|
||||||
|
<ConstrainDragXAxis />
|
||||||
|
<div
|
||||||
|
ref={sidebarProps.mobile ? undefined : scrollContainerRef}
|
||||||
|
class="w-full min-w-8 flex flex-col gap-2 min-h-0 overflow-y-auto no-scrollbar"
|
||||||
>
|
>
|
||||||
<DragDropSensors />
|
<SortableProvider ids={layout.projects.list().map((p) => p.worktree)}>
|
||||||
<ConstrainDragXAxis />
|
<For each={layout.projects.list()}>
|
||||||
<div
|
{(project) => <SortableProject project={project} mobile={sidebarProps.mobile} />}
|
||||||
ref={scrollContainerRef}
|
</For>
|
||||||
class="w-full min-w-8 flex flex-col gap-2 min-h-0 overflow-y-auto no-scrollbar"
|
</SortableProvider>
|
||||||
>
|
</div>
|
||||||
<SortableProvider ids={layout.projects.list().map((p) => p.worktree)}>
|
<DragOverlay>
|
||||||
<For each={layout.projects.list()}>{(project) => <SortableProject project={project} />}</For>
|
<ProjectDragOverlay />
|
||||||
</SortableProvider>
|
</DragOverlay>
|
||||||
</div>
|
</DragDropProvider>
|
||||||
<DragOverlay>
|
</div>
|
||||||
<ProjectDragOverlay />
|
<div class="flex flex-col gap-1.5 self-stretch items-start shrink-0 px-2 py-3">
|
||||||
</DragOverlay>
|
<Switch>
|
||||||
</DragDropProvider>
|
<Match when={!providers.paid().length && expanded()}>
|
||||||
</div>
|
<div class="rounded-md bg-background-stronger shadow-xs-border-base">
|
||||||
<div class="flex flex-col gap-1.5 self-stretch items-start shrink-0 px-2 py-3">
|
<div class="p-3 flex flex-col gap-2">
|
||||||
<Switch>
|
<div class="text-12-medium text-text-strong">Getting started</div>
|
||||||
<Match when={!providers.paid().length && layout.sidebar.opened()}>
|
<div class="text-text-base">OpenCode includes free models so you can start immediately.</div>
|
||||||
<div class="rounded-md bg-background-stronger shadow-xs-border-base">
|
<div class="text-text-base">Connect any provider to use models, inc. Claude, GPT, Gemini etc.</div>
|
||||||
<div class="p-3 flex flex-col gap-2">
|
|
||||||
<div class="text-12-medium text-text-strong">Getting started</div>
|
|
||||||
<div class="text-text-base">OpenCode includes free models so you can start immediately.</div>
|
|
||||||
<div class="text-text-base">Connect any provider to use models, inc. Claude, GPT, Gemini etc.</div>
|
|
||||||
</div>
|
|
||||||
<Tooltip placement="right" value="Connect provider" inactive={layout.sidebar.opened()}>
|
|
||||||
<Button
|
|
||||||
class="flex w-full text-left justify-start text-12-medium text-text-strong stroke-[1.5px] rounded-lg rounded-t-none shadow-none border-t border-border-weak-base pl-2.25 pb-px"
|
|
||||||
size="large"
|
|
||||||
icon="plus"
|
|
||||||
onClick={connectProvider}
|
|
||||||
>
|
|
||||||
<Show when={layout.sidebar.opened()}>Connect provider</Show>
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
<Tooltip placement="right" value="Connect provider" inactive={expanded()}>
|
||||||
<Match when={true}>
|
|
||||||
<Tooltip placement="right" value="Connect provider" inactive={layout.sidebar.opened()}>
|
|
||||||
<Button
|
<Button
|
||||||
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
|
class="flex w-full text-left justify-start text-12-medium text-text-strong stroke-[1.5px] rounded-lg rounded-t-none shadow-none border-t border-border-weak-base pl-2.25 pb-px"
|
||||||
variant="ghost"
|
|
||||||
size="large"
|
size="large"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
onClick={connectProvider}
|
onClick={connectProvider}
|
||||||
>
|
>
|
||||||
<Show when={layout.sidebar.opened()}>Connect provider</Show>
|
Connect provider
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Match>
|
</div>
|
||||||
</Switch>
|
</Match>
|
||||||
<Show when={platform.openDirectoryPickerDialog}>
|
<Match when={true}>
|
||||||
<Tooltip placement="right" value="Open project" inactive={layout.sidebar.opened()}>
|
<Tooltip placement="right" value="Connect provider" inactive={expanded()}>
|
||||||
<Button
|
<Button
|
||||||
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
|
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="large"
|
size="large"
|
||||||
icon="folder-add-left"
|
icon="plus"
|
||||||
onClick={chooseProject}
|
onClick={connectProvider}
|
||||||
>
|
>
|
||||||
<Show when={layout.sidebar.opened()}>Open project</Show>
|
<Show when={expanded()}>Connect provider</Show>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Show>
|
</Match>
|
||||||
{/* <Tooltip placement="right" value="Settings" inactive={layout.sidebar.opened()}> */}
|
</Switch>
|
||||||
{/* <Button */}
|
<Show when={platform.openDirectoryPickerDialog}>
|
||||||
{/* disabled */}
|
<Tooltip
|
||||||
{/* class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg px-2" */}
|
placement="right"
|
||||||
{/* variant="ghost" */}
|
value={
|
||||||
{/* size="large" */}
|
<div class="flex items-center gap-2">
|
||||||
{/* icon="settings-gear" */}
|
<span>Open project</span>
|
||||||
{/* > */}
|
<Show when={!sidebarProps.mobile}>
|
||||||
{/* <Show when={layout.sidebar.opened()}>Settings</Show> */}
|
<span class="text-icon-base text-12-medium">{command.keybind("project.open")}</span>
|
||||||
{/* </Button> */}
|
</Show>
|
||||||
{/* </Tooltip> */}
|
</div>
|
||||||
<Tooltip placement="right" value="Share feedback" inactive={layout.sidebar.opened()}>
|
}
|
||||||
|
inactive={expanded()}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
as={"a"}
|
|
||||||
href="https://opencode.ai/desktop-feedback"
|
|
||||||
target="_blank"
|
|
||||||
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
|
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="large"
|
size="large"
|
||||||
icon="bubble-5"
|
icon="folder-add-left"
|
||||||
|
onClick={chooseProject}
|
||||||
>
|
>
|
||||||
<Show when={layout.sidebar.opened()}>Share feedback</Show>
|
<Show when={expanded()}>Open project</Show>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
</Show>
|
||||||
|
<Tooltip placement="right" value="Share feedback" inactive={expanded()}>
|
||||||
|
<Button
|
||||||
|
as={"a"}
|
||||||
|
href="https://opencode.ai/desktop-feedback"
|
||||||
|
target="_blank"
|
||||||
|
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
|
||||||
|
variant="ghost"
|
||||||
|
size="large"
|
||||||
|
icon="bubble-5"
|
||||||
|
>
|
||||||
|
<Show when={expanded()}>Share feedback</Show>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="relative flex-1 min-h-0 flex flex-col">
|
||||||
|
<Header
|
||||||
|
navigateToProject={navigateToProject}
|
||||||
|
navigateToSession={navigateToSession}
|
||||||
|
onMobileMenuToggle={mobileSidebar.toggle}
|
||||||
|
/>
|
||||||
|
<div class="flex-1 min-h-0 flex">
|
||||||
|
<div
|
||||||
|
classList={{
|
||||||
|
"hidden xl:flex": true,
|
||||||
|
"relative @container w-12 pb-5 shrink-0 bg-background-base": true,
|
||||||
|
"flex-col gap-5.5 items-start self-stretch justify-between": true,
|
||||||
|
"border-r border-border-weak-base contain-strict": true,
|
||||||
|
}}
|
||||||
|
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
|
||||||
|
>
|
||||||
|
<Show when={layout.sidebar.opened()}>
|
||||||
|
<ResizeHandle
|
||||||
|
direction="horizontal"
|
||||||
|
size={layout.sidebar.width()}
|
||||||
|
min={150}
|
||||||
|
max={window.innerWidth * 0.3}
|
||||||
|
collapseThreshold={80}
|
||||||
|
onResize={layout.sidebar.resize}
|
||||||
|
onCollapse={layout.sidebar.close}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
<SidebarContent />
|
||||||
|
</div>
|
||||||
|
<div class="xl:hidden">
|
||||||
|
<div
|
||||||
|
classList={{
|
||||||
|
"fixed inset-0 bg-black/50 z-40 transition-opacity duration-200": true,
|
||||||
|
"opacity-100 pointer-events-auto": mobileSidebar.open(),
|
||||||
|
"opacity-0 pointer-events-none": !mobileSidebar.open(),
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (e.target === e.currentTarget) mobileSidebar.hide()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
classList={{
|
||||||
|
"@container fixed inset-y-0 left-0 z-50 w-72 bg-background-base border-r border-border-weak-base flex flex-col gap-5.5 items-start self-stretch justify-between pt-12 pb-5 transition-transform duration-200 ease-out": true,
|
||||||
|
"translate-x-0": mobileSidebar.open(),
|
||||||
|
"-translate-x-full": !mobileSidebar.open(),
|
||||||
|
}}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<SidebarContent mobile />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main class="size-full overflow-x-hidden flex flex-col items-start contain-strict">{props.children}</main>
|
<main class="size-full overflow-x-hidden flex flex-col items-start contain-strict">{props.children}</main>
|
||||||
</div>
|
</div>
|
||||||
<Toast.Region />
|
<Toast.Region />
|
||||||
|
|||||||
@@ -1,4 +1,18 @@
|
|||||||
import { For, onCleanup, onMount, Show, Match, Switch, createResource, createMemo, createEffect, on } from "solid-js"
|
import {
|
||||||
|
For,
|
||||||
|
onCleanup,
|
||||||
|
onMount,
|
||||||
|
Show,
|
||||||
|
Match,
|
||||||
|
Switch,
|
||||||
|
createResource,
|
||||||
|
createMemo,
|
||||||
|
createEffect,
|
||||||
|
on,
|
||||||
|
createRenderEffect,
|
||||||
|
batch,
|
||||||
|
} from "solid-js"
|
||||||
|
|
||||||
import { Dynamic } from "solid-js/web"
|
import { Dynamic } from "solid-js/web"
|
||||||
import { useLocal, type LocalFile } from "@/context/local"
|
import { useLocal, type LocalFile } from "@/context/local"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
@@ -9,11 +23,11 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
|
|||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||||
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
import { DiffChanges } from "@opencode-ai/ui/diff-changes"
|
||||||
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
|
|
||||||
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
||||||
import { Tabs } from "@opencode-ai/ui/tabs"
|
import { Tabs } from "@opencode-ai/ui/tabs"
|
||||||
import { useCodeComponent } from "@opencode-ai/ui/context/code"
|
import { useCodeComponent } from "@opencode-ai/ui/context/code"
|
||||||
import { SessionTurn } from "@opencode-ai/ui/session-turn"
|
import { SessionTurn } from "@opencode-ai/ui/session-turn"
|
||||||
|
import { createAutoScroll } from "@opencode-ai/ui/hooks"
|
||||||
import { SessionMessageRail } from "@opencode-ai/ui/session-message-rail"
|
import { SessionMessageRail } from "@opencode-ai/ui/session-message-rail"
|
||||||
import { SessionReview } from "@opencode-ai/ui/session-review"
|
import { SessionReview } from "@opencode-ai/ui/session-review"
|
||||||
import {
|
import {
|
||||||
@@ -37,7 +51,7 @@ import { DialogSelectFile } from "@/components/dialog-select-file"
|
|||||||
import { DialogSelectModel } from "@/components/dialog-select-model"
|
import { DialogSelectModel } from "@/components/dialog-select-model"
|
||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { useNavigate, useParams } from "@solidjs/router"
|
import { useNavigate, useParams } from "@solidjs/router"
|
||||||
import { AssistantMessage, UserMessage } from "@opencode-ai/sdk/v2"
|
import { UserMessage } from "@opencode-ai/sdk/v2"
|
||||||
import { useSDK } from "@/context/sdk"
|
import { useSDK } from "@/context/sdk"
|
||||||
import { usePrompt } from "@/context/prompt"
|
import { usePrompt } from "@/context/prompt"
|
||||||
import { extractPromptFromParts } from "@/utils/prompt"
|
import { extractPromptFromParts } from "@/utils/prompt"
|
||||||
@@ -58,7 +72,6 @@ export default function Page() {
|
|||||||
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey()))
|
const tabs = createMemo(() => layout.tabs(sessionKey()))
|
||||||
|
|
||||||
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
||||||
const revertMessageID = createMemo(() => info()?.revert?.messageID)
|
const revertMessageID = createMemo(() => info()?.revert?.messageID)
|
||||||
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
||||||
@@ -67,7 +80,6 @@ export default function Page() {
|
|||||||
.filter((m) => m.role === "user")
|
.filter((m) => m.role === "user")
|
||||||
.sort((a, b) => a.id.localeCompare(b.id)),
|
.sort((a, b) => a.id.localeCompare(b.id)),
|
||||||
)
|
)
|
||||||
// Visible user messages excludes reverted messages (those >= revertMessageID)
|
|
||||||
const visibleUserMessages = createMemo(() => {
|
const visibleUserMessages = createMemo(() => {
|
||||||
const revert = revertMessageID()
|
const revert = revertMessageID()
|
||||||
if (!revert) return userMessages()
|
if (!revert) return userMessages()
|
||||||
@@ -75,15 +87,24 @@ export default function Page() {
|
|||||||
})
|
})
|
||||||
const lastUserMessage = createMemo(() => visibleUserMessages()?.at(-1))
|
const lastUserMessage = createMemo(() => visibleUserMessages()?.at(-1))
|
||||||
|
|
||||||
const [messageStore, setMessageStore] = createStore<{ messageId?: string }>({})
|
const [store, setStore] = createStore({
|
||||||
|
clickTimer: undefined as number | undefined,
|
||||||
|
activeDraggable: undefined as string | undefined,
|
||||||
|
activeTerminalDraggable: undefined as string | undefined,
|
||||||
|
userInteracted: false,
|
||||||
|
stepsExpanded: true,
|
||||||
|
mobileStepsExpanded: {} as Record<string, boolean>,
|
||||||
|
messageId: undefined as string | undefined,
|
||||||
|
})
|
||||||
|
|
||||||
const activeMessage = createMemo(() => {
|
const activeMessage = createMemo(() => {
|
||||||
if (!messageStore.messageId) return lastUserMessage()
|
if (!store.messageId) return lastUserMessage()
|
||||||
// If the stored message is no longer visible (e.g., was reverted), fall back to last visible
|
// If the stored message is no longer visible (e.g., was reverted), fall back to last visible
|
||||||
const found = visibleUserMessages()?.find((m) => m.id === messageStore.messageId)
|
const found = visibleUserMessages()?.find((m) => m.id === store.messageId)
|
||||||
return found ?? lastUserMessage()
|
return found ?? lastUserMessage()
|
||||||
})
|
})
|
||||||
const setActiveMessage = (message: UserMessage | undefined) => {
|
const setActiveMessage = (message: UserMessage | undefined) => {
|
||||||
setMessageStore("messageId", message?.id)
|
setStore("messageId", message?.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateMessageByOffset(offset: number) {
|
function navigateMessageByOffset(offset: number) {
|
||||||
@@ -105,33 +126,8 @@ export default function Page() {
|
|||||||
setActiveMessage(msgs[targetIndex])
|
setActiveMessage(msgs[targetIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
const last = createMemo(
|
|
||||||
() => messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage,
|
|
||||||
)
|
|
||||||
const model = createMemo(() =>
|
|
||||||
last() ? sync.data.provider.all.find((x) => x.id === last().providerID)?.models[last().modelID] : undefined,
|
|
||||||
)
|
|
||||||
const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : []))
|
const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : []))
|
||||||
|
|
||||||
const tokens = createMemo(() => {
|
|
||||||
if (!last()) return
|
|
||||||
const t = last().tokens
|
|
||||||
return t.input + t.output + t.reasoning + t.cache.read + t.cache.write
|
|
||||||
})
|
|
||||||
|
|
||||||
const context = createMemo(() => {
|
|
||||||
const total = tokens()
|
|
||||||
const limit = model()?.limit.context
|
|
||||||
if (!total || !limit) return 0
|
|
||||||
return Math.round((total / limit) * 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
const [store, setStore] = createStore({
|
|
||||||
clickTimer: undefined as number | undefined,
|
|
||||||
activeDraggable: undefined as string | undefined,
|
|
||||||
activeTerminalDraggable: undefined as string | undefined,
|
|
||||||
stepsExpanded: false,
|
|
||||||
})
|
|
||||||
let inputRef!: HTMLDivElement
|
let inputRef!: HTMLDivElement
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
@@ -152,14 +148,35 @@ export default function Page() {
|
|||||||
() => visibleUserMessages().at(-1)?.id,
|
() => visibleUserMessages().at(-1)?.id,
|
||||||
(lastId, prevLastId) => {
|
(lastId, prevLastId) => {
|
||||||
if (lastId && prevLastId && lastId > prevLastId) {
|
if (lastId && prevLastId && lastId > prevLastId) {
|
||||||
setMessageStore("messageId", undefined)
|
setStore("messageId", undefined)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ defer: true },
|
{ defer: true },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
params.id
|
||||||
|
const status = sync.data.session_status[params.id ?? ""] ?? { type: "idle" }
|
||||||
|
batch(() => {
|
||||||
|
setStore("userInteracted", false)
|
||||||
|
setStore("stepsExpanded", status.type !== "idle")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const status = createMemo(() => sync.data.session_status[params.id ?? ""] ?? { type: "idle" })
|
const status = createMemo(() => sync.data.session_status[params.id ?? ""] ?? { type: "idle" })
|
||||||
|
const working = createMemo(() => status().type !== "idle" && activeMessage()?.id === lastUserMessage()?.id)
|
||||||
|
|
||||||
|
createRenderEffect((prev) => {
|
||||||
|
const isWorking = working()
|
||||||
|
if (!prev && isWorking) {
|
||||||
|
setStore("stepsExpanded", true)
|
||||||
|
}
|
||||||
|
if (prev && !isWorking && !store.userInteracted) {
|
||||||
|
setStore("stepsExpanded", false)
|
||||||
|
}
|
||||||
|
return isWorking
|
||||||
|
}, working())
|
||||||
|
|
||||||
command.register(() => [
|
command.register(() => [
|
||||||
{
|
{
|
||||||
@@ -516,278 +533,321 @@ export default function Page() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const wide = createMemo(() => layout.review.state() === "tab" || !diffs().length)
|
const showTabs = createMemo(() => diffs().length > 0 || tabs().all().length > 0)
|
||||||
|
|
||||||
|
const mobileWorking = createMemo(() => status().type !== "idle")
|
||||||
|
const mobileAutoScroll = createAutoScroll({
|
||||||
|
working: mobileWorking,
|
||||||
|
onUserInteracted: () => setStore("userInteracted", true),
|
||||||
|
})
|
||||||
|
|
||||||
|
const MobileTurns = () => (
|
||||||
|
<div
|
||||||
|
ref={mobileAutoScroll.scrollRef}
|
||||||
|
onScroll={mobileAutoScroll.handleScroll}
|
||||||
|
onClick={mobileAutoScroll.handleInteraction}
|
||||||
|
class="relative mt-2 min-w-0 w-full h-full overflow-y-auto no-scrollbar pb-12"
|
||||||
|
>
|
||||||
|
<div ref={mobileAutoScroll.contentRef} class="flex flex-col gap-45 items-start justify-start mt-4">
|
||||||
|
<For each={visibleUserMessages()}>
|
||||||
|
{(message) => (
|
||||||
|
<SessionTurn
|
||||||
|
sessionID={params.id!}
|
||||||
|
messageID={message.id}
|
||||||
|
stepsExpanded={store.mobileStepsExpanded[message.id] ?? false}
|
||||||
|
onStepsExpandedToggle={() => setStore("mobileStepsExpanded", message.id, (x) => !x)}
|
||||||
|
onUserInteracted={() => setStore("userInteracted", true)}
|
||||||
|
classes={{
|
||||||
|
root: "min-w-0 w-full relative",
|
||||||
|
content:
|
||||||
|
"flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
|
||||||
|
container: "px-4",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const NewSessionView = () => (
|
||||||
|
<div class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto px-6">
|
||||||
|
<div class="text-20-medium text-text-weaker">New session</div>
|
||||||
|
<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)}
|
||||||
|
<span class="text-text-strong">{getFilename(sync.data.path.directory)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Show when={sync.project}>
|
||||||
|
{(project) => (
|
||||||
|
<div class="flex justify-center items-center gap-3">
|
||||||
|
<Icon name="pencil-line" size="small" />
|
||||||
|
<div class="text-12-medium text-text-weak">
|
||||||
|
Last modified
|
||||||
|
<span class="text-text-strong">
|
||||||
|
{DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const DesktopSessionContent = () => (
|
||||||
|
<Switch>
|
||||||
|
<Match when={params.id}>
|
||||||
|
<div class="flex items-start justify-start h-full min-h-0">
|
||||||
|
<SessionMessageRail
|
||||||
|
messages={visibleUserMessages()}
|
||||||
|
current={activeMessage()}
|
||||||
|
onMessageSelect={setActiveMessage}
|
||||||
|
wide={!showTabs()}
|
||||||
|
/>
|
||||||
|
<Show when={activeMessage()}>
|
||||||
|
<SessionTurn
|
||||||
|
sessionID={params.id!}
|
||||||
|
messageID={activeMessage()!.id}
|
||||||
|
stepsExpanded={store.stepsExpanded}
|
||||||
|
onStepsExpandedToggle={() => setStore("stepsExpanded", (x) => !x)}
|
||||||
|
onUserInteracted={() => setStore("userInteracted", true)}
|
||||||
|
classes={{
|
||||||
|
root: "pb-20 flex-1 min-w-0",
|
||||||
|
content: "pb-20",
|
||||||
|
container:
|
||||||
|
"w-full " +
|
||||||
|
(!showTabs() ? "max-w-200 mx-auto px-6" : visibleUserMessages().length > 1 ? "pr-6 pl-18" : "px-6"),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Match>
|
||||||
|
<Match when={true}>
|
||||||
|
<NewSessionView />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative bg-background-base size-full overflow-x-hidden flex flex-col">
|
<div class="relative bg-background-base size-full overflow-hidden flex flex-col">
|
||||||
<div class="min-h-0 grow w-full">
|
<div class="md:hidden flex-1 min-h-0 flex flex-col bg-background-stronger">
|
||||||
<DragDropProvider
|
<Switch>
|
||||||
onDragStart={handleDragStart}
|
<Match when={!params.id}>
|
||||||
onDragEnd={handleDragEnd}
|
<div class="flex-1 min-h-0 overflow-hidden">
|
||||||
onDragOver={handleDragOver}
|
<NewSessionView />
|
||||||
collisionDetector={closestCenter}
|
|
||||||
>
|
|
||||||
<DragDropSensors />
|
|
||||||
<ConstrainDragYAxis />
|
|
||||||
<Tabs value={tabs().active() ?? "chat"} onChange={tabs().open}>
|
|
||||||
<div class="sticky top-0 shrink-0 flex">
|
|
||||||
<Tabs.List>
|
|
||||||
<Tabs.Trigger value="chat">
|
|
||||||
<div class="flex gap-x-[17px] items-center">
|
|
||||||
<div>Session</div>
|
|
||||||
<Tooltip
|
|
||||||
value={`${new Intl.NumberFormat("en-US", {
|
|
||||||
notation: "compact",
|
|
||||||
compactDisplay: "short",
|
|
||||||
}).format(tokens() ?? 0)} Tokens`}
|
|
||||||
class="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
<ProgressCircle percentage={context() ?? 0} />
|
|
||||||
<div class="text-14-regular text-text-weak text-left w-7">{context() ?? 0}%</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</Tabs.Trigger>
|
|
||||||
<Show when={layout.review.state() === "tab" && diffs().length}>
|
|
||||||
<Tabs.Trigger
|
|
||||||
value="review"
|
|
||||||
closeButton={
|
|
||||||
<Tooltip value="Close tab" placement="bottom">
|
|
||||||
<IconButton icon="collapse" size="normal" variant="ghost" onClick={layout.review.pane} />
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<Show when={diffs()}>
|
|
||||||
<DiffChanges changes={diffs()} variant="bars" />
|
|
||||||
</Show>
|
|
||||||
<div class="flex items-center gap-1.5">
|
|
||||||
<div>Review</div>
|
|
||||||
<Show when={info()?.summary?.files}>
|
|
||||||
<div class="text-12-medium text-text-strong h-4 px-2 flex flex-col items-center justify-center rounded-full bg-surface-base">
|
|
||||||
{info()?.summary?.files ?? 0}
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Tabs.Trigger>
|
|
||||||
</Show>
|
|
||||||
<SortableProvider ids={tabs().all() ?? []}>
|
|
||||||
<For each={tabs().all() ?? []}>
|
|
||||||
{(tab) => <SortableTab tab={tab} onTabClick={handleTabClick} onTabClose={tabs().close} />}
|
|
||||||
</For>
|
|
||||||
</SortableProvider>
|
|
||||||
<div class="bg-background-base h-full flex items-center justify-center border-b border-border-weak-base px-3">
|
|
||||||
<Tooltip value="Open file" class="flex items-center">
|
|
||||||
<IconButton
|
|
||||||
icon="plus-small"
|
|
||||||
variant="ghost"
|
|
||||||
iconSize="large"
|
|
||||||
onClick={() => dialog.show(() => <DialogSelectFile />)}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</Tabs.List>
|
|
||||||
</div>
|
</div>
|
||||||
<Tabs.Content
|
</Match>
|
||||||
value="chat"
|
<Match when={diffs().length > 0}>
|
||||||
class="@container select-text flex flex-col flex-1 min-h-0 overflow-y-hidden contain-strict"
|
<Tabs class="flex-1 min-h-0 flex flex-col pb-28">
|
||||||
>
|
<Tabs.List>
|
||||||
<div
|
<Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}>
|
||||||
classList={{
|
Session
|
||||||
"w-full flex-1 min-h-0": true,
|
</Tabs.Trigger>
|
||||||
grid: layout.review.state() === "tab",
|
<Tabs.Trigger value="review" class="w-1/2 !border-r-0" classes={{ button: "w-full" }}>
|
||||||
flex: layout.review.state() === "pane",
|
{diffs().length} Files Changed
|
||||||
}}
|
</Tabs.Trigger>
|
||||||
>
|
</Tabs.List>
|
||||||
<div
|
<Tabs.Content value="session" class="flex-1 !overflow-hidden">
|
||||||
classList={{
|
<MobileTurns />
|
||||||
"relative shrink-0 py-3 flex flex-col gap-6 flex-1 min-h-0 w-full": true,
|
</Tabs.Content>
|
||||||
"max-w-200 mx-auto": !wide(),
|
<Tabs.Content forceMount value="review" class="flex-1 !overflow-hidden hidden data-[selected]:block">
|
||||||
}}
|
<div class="relative h-full mt-6 overflow-y-auto no-scrollbar">
|
||||||
>
|
|
||||||
<Switch>
|
|
||||||
<Match when={params.id}>
|
|
||||||
<div class="flex items-start justify-start h-full min-h-0">
|
|
||||||
<SessionMessageRail
|
|
||||||
messages={visibleUserMessages()}
|
|
||||||
current={activeMessage()}
|
|
||||||
onMessageSelect={setActiveMessage}
|
|
||||||
wide={wide()}
|
|
||||||
/>
|
|
||||||
<Show when={activeMessage()}>
|
|
||||||
<SessionTurn
|
|
||||||
sessionID={params.id!}
|
|
||||||
messageID={activeMessage()!.id}
|
|
||||||
stepsExpanded={store.stepsExpanded}
|
|
||||||
onStepsExpandedChange={(expanded) => setStore("stepsExpanded", expanded)}
|
|
||||||
classes={{
|
|
||||||
root: "pb-20 flex-1 min-w-0",
|
|
||||||
content: "pb-20",
|
|
||||||
container:
|
|
||||||
"w-full " +
|
|
||||||
(wide()
|
|
||||||
? "max-w-200 mx-auto px-6"
|
|
||||||
: visibleUserMessages().length > 1
|
|
||||||
? "pr-6 pl-18"
|
|
||||||
: "px-6"),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</Match>
|
|
||||||
<Match when={true}>
|
|
||||||
<div class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto px-6">
|
|
||||||
<div class="text-20-medium text-text-weaker">New session</div>
|
|
||||||
<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)}
|
|
||||||
<span class="text-text-strong">{getFilename(sync.data.path.directory)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Show when={sync.project}>
|
|
||||||
{(project) => (
|
|
||||||
<div class="flex justify-center items-center gap-3">
|
|
||||||
<Icon name="pencil-line" size="small" />
|
|
||||||
<div class="text-12-medium text-text-weak">
|
|
||||||
Last modified
|
|
||||||
<span class="text-text-strong">
|
|
||||||
{DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</Match>
|
|
||||||
</Switch>
|
|
||||||
<div class="absolute inset-x-0 bottom-8 flex flex-col justify-center items-center z-50">
|
|
||||||
<div class="w-full max-w-200 px-6">
|
|
||||||
<PromptInput
|
|
||||||
ref={(el) => {
|
|
||||||
inputRef = el
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Show when={layout.review.state() === "pane" && diffs().length}>
|
|
||||||
<div
|
|
||||||
classList={{
|
|
||||||
"relative grow pt-3 flex-1 min-h-0 border-l border-border-weak-base contain-strict": true,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SessionReview
|
|
||||||
classes={{
|
|
||||||
root: "pb-20",
|
|
||||||
header: "px-6",
|
|
||||||
container: "px-6",
|
|
||||||
}}
|
|
||||||
diffs={diffs()}
|
|
||||||
actions={
|
|
||||||
<Tooltip value="Open in tab">
|
|
||||||
<IconButton
|
|
||||||
icon="expand"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
layout.review.tab()
|
|
||||||
tabs().setActive("review")
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</Tabs.Content>
|
|
||||||
<Show when={layout.review.state() === "tab" && diffs().length}>
|
|
||||||
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden contain-strict">
|
|
||||||
<div
|
|
||||||
classList={{
|
|
||||||
"relative pt-3 flex-1 min-h-0 overflow-hidden": true,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SessionReview
|
<SessionReview
|
||||||
classes={{
|
|
||||||
root: "pb-40",
|
|
||||||
header: "px-6",
|
|
||||||
container: "px-6",
|
|
||||||
}}
|
|
||||||
diffs={diffs()}
|
diffs={diffs()}
|
||||||
split
|
classes={{
|
||||||
|
root: "pb-32",
|
||||||
|
header: "px-4",
|
||||||
|
container: "px-4",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
</Show>
|
</Tabs>
|
||||||
<For each={tabs().all()}>
|
</Match>
|
||||||
{(tab) => {
|
<Match when={true}>
|
||||||
const [file] = createResource(
|
<div class="flex-1 min-h-0 overflow-hidden">
|
||||||
() => tab,
|
<MobileTurns />
|
||||||
async (tab) => {
|
</div>
|
||||||
if (tab.startsWith("file://")) {
|
</Match>
|
||||||
return local.file.node(tab.replace("file://", ""))
|
</Switch>
|
||||||
}
|
<div class="absolute inset-x-0 bottom-4 flex flex-col justify-center items-center z-50 px-4">
|
||||||
return undefined
|
<div class="w-full">
|
||||||
},
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
<Tabs.Content value={tab} class="select-text mt-3">
|
|
||||||
<Switch>
|
|
||||||
<Match when={file()}>
|
|
||||||
{(f) => (
|
|
||||||
<Dynamic
|
|
||||||
component={codeComponent}
|
|
||||||
file={{
|
|
||||||
name: f().path,
|
|
||||||
contents: f().content?.content ?? "",
|
|
||||||
cacheKey: checksum(f().content?.content ?? ""),
|
|
||||||
}}
|
|
||||||
overflow="scroll"
|
|
||||||
class="pb-40"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Match>
|
|
||||||
</Switch>
|
|
||||||
</Tabs.Content>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</For>
|
|
||||||
</Tabs>
|
|
||||||
<DragOverlay>
|
|
||||||
<Show when={store.activeDraggable}>
|
|
||||||
{(draggedFile) => {
|
|
||||||
const [file] = createResource(
|
|
||||||
() => draggedFile(),
|
|
||||||
async (tab) => {
|
|
||||||
if (tab.startsWith("file://")) {
|
|
||||||
return local.file.node(tab.replace("file://", ""))
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
<div class="relative px-6 h-12 flex items-center bg-background-stronger border-x border-border-weak-base border-b border-b-transparent">
|
|
||||||
<Show when={file()}>{(f) => <FileVisual active file={f()} />}</Show>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</Show>
|
|
||||||
</DragOverlay>
|
|
||||||
</DragDropProvider>
|
|
||||||
<Show when={tabs().active()}>
|
|
||||||
<div class="absolute inset-x-0 px-6 max-w-200 flex flex-col justify-center items-center z-50 mx-auto bottom-8">
|
|
||||||
<PromptInput
|
<PromptInput
|
||||||
ref={(el) => {
|
ref={(el) => {
|
||||||
inputRef = el
|
inputRef = el
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden md:flex min-h-0 grow w-full">
|
||||||
|
<div
|
||||||
|
class="@container relative shrink-0 py-3 flex flex-col gap-6 min-h-0 h-full bg-background-stronger"
|
||||||
|
style={{ width: showTabs() ? `${layout.session.width()}px` : "100%" }}
|
||||||
|
>
|
||||||
|
<div class="flex-1 min-h-0 overflow-hidden">
|
||||||
|
<DesktopSessionContent />
|
||||||
|
</div>
|
||||||
|
<div class="absolute inset-x-0 bottom-8 flex flex-col justify-center items-center z-50">
|
||||||
|
<div
|
||||||
|
classList={{
|
||||||
|
"w-full px-6": true,
|
||||||
|
"max-w-200": !showTabs(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PromptInput
|
||||||
|
ref={(el) => {
|
||||||
|
inputRef = el
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Show when={showTabs()}>
|
||||||
|
<ResizeHandle
|
||||||
|
direction="horizontal"
|
||||||
|
size={layout.session.width()}
|
||||||
|
min={450}
|
||||||
|
max={window.innerWidth * 0.45}
|
||||||
|
onResize={layout.session.resize}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={showTabs()}>
|
||||||
|
<div class="relative flex-1 min-w-0 h-full border-l border-border-weak-base">
|
||||||
|
<DragDropProvider
|
||||||
|
onDragStart={handleDragStart}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
collisionDetector={closestCenter}
|
||||||
|
>
|
||||||
|
<DragDropSensors />
|
||||||
|
<ConstrainDragYAxis />
|
||||||
|
<Tabs value={tabs().active() ?? "review"} onChange={tabs().open}>
|
||||||
|
<div class="sticky top-0 shrink-0 flex">
|
||||||
|
<Tabs.List>
|
||||||
|
<Show when={diffs().length}>
|
||||||
|
<Tabs.Trigger value="review">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<Show when={diffs()}>
|
||||||
|
<DiffChanges changes={diffs()} variant="bars" />
|
||||||
|
</Show>
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<div>Review</div>
|
||||||
|
<Show when={info()?.summary?.files}>
|
||||||
|
<div class="text-12-medium text-text-strong h-4 px-2 flex flex-col items-center justify-center rounded-full bg-surface-base">
|
||||||
|
{info()?.summary?.files ?? 0}
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tabs.Trigger>
|
||||||
|
</Show>
|
||||||
|
<SortableProvider ids={tabs().all() ?? []}>
|
||||||
|
<For each={tabs().all() ?? []}>
|
||||||
|
{(tab) => <SortableTab tab={tab} onTabClick={handleTabClick} onTabClose={tabs().close} />}
|
||||||
|
</For>
|
||||||
|
</SortableProvider>
|
||||||
|
<div class="bg-background-base h-full flex items-center justify-center border-b border-border-weak-base px-3">
|
||||||
|
<Tooltip
|
||||||
|
value={
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span>Open file</span>
|
||||||
|
<span class="text-icon-base text-12-medium">{command.keybind("file.open")}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
class="flex items-center"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
icon="plus-small"
|
||||||
|
variant="ghost"
|
||||||
|
iconSize="large"
|
||||||
|
onClick={() => dialog.show(() => <DialogSelectFile />)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</Tabs.List>
|
||||||
|
</div>
|
||||||
|
<Show when={diffs().length}>
|
||||||
|
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden contain-strict">
|
||||||
|
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
|
||||||
|
<SessionReview
|
||||||
|
classes={{
|
||||||
|
root: "pb-40",
|
||||||
|
header: "px-6",
|
||||||
|
container: "px-6",
|
||||||
|
}}
|
||||||
|
diffs={diffs()}
|
||||||
|
split
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tabs.Content>
|
||||||
|
</Show>
|
||||||
|
<For each={tabs().all()}>
|
||||||
|
{(tab) => {
|
||||||
|
const [file] = createResource(
|
||||||
|
() => tab,
|
||||||
|
async (tab) => {
|
||||||
|
if (tab.startsWith("file://")) {
|
||||||
|
return local.file.node(tab.replace("file://", ""))
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Tabs.Content value={tab} class="select-text mt-3">
|
||||||
|
<Switch>
|
||||||
|
<Match when={file()}>
|
||||||
|
{(f) => (
|
||||||
|
<Dynamic
|
||||||
|
component={codeComponent}
|
||||||
|
file={{
|
||||||
|
name: f().path,
|
||||||
|
contents: f().content?.content ?? "",
|
||||||
|
cacheKey: checksum(f().content?.content ?? ""),
|
||||||
|
}}
|
||||||
|
overflow="scroll"
|
||||||
|
class="pb-40"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</Tabs.Content>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</Tabs>
|
||||||
|
<DragOverlay>
|
||||||
|
<Show when={store.activeDraggable}>
|
||||||
|
{(draggedFile) => {
|
||||||
|
const [file] = createResource(
|
||||||
|
() => draggedFile(),
|
||||||
|
async (tab) => {
|
||||||
|
if (tab.startsWith("file://")) {
|
||||||
|
return local.file.node(tab.replace("file://", ""))
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<div class="relative px-6 h-12 flex items-center bg-background-stronger border-x border-border-weak-base border-b border-b-transparent">
|
||||||
|
<Show when={file()}>{(f) => <FileVisual active file={f()} />}</Show>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Show>
|
||||||
|
</DragOverlay>
|
||||||
|
</DragDropProvider>
|
||||||
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={layout.terminal.opened()}>
|
<Show when={layout.terminal.opened()}>
|
||||||
<div
|
<div
|
||||||
class="relative w-full flex flex-col shrink-0 border-t border-border-weak-base"
|
class="hidden md:flex relative w-full flex-col shrink-0 border-t border-border-weak-base"
|
||||||
style={{ height: `${layout.terminal.height()}px` }}
|
style={{ height: `${layout.terminal.height()}px` }}
|
||||||
>
|
>
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
@@ -813,7 +873,15 @@ export default function Page() {
|
|||||||
<For each={terminal.all()}>{(pty) => <SortableTerminalTab terminal={pty} />}</For>
|
<For each={terminal.all()}>{(pty) => <SortableTerminalTab terminal={pty} />}</For>
|
||||||
</SortableProvider>
|
</SortableProvider>
|
||||||
<div class="h-full flex items-center justify-center">
|
<div class="h-full flex items-center justify-center">
|
||||||
<Tooltip value="New Terminal" class="flex items-center">
|
<Tooltip
|
||||||
|
value={
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span>New terminal</span>
|
||||||
|
<span class="text-icon-base text-12-medium">{command.keybind("terminal.new")}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
class="flex items-center"
|
||||||
|
>
|
||||||
<IconButton icon="plus-small" variant="ghost" iconSize="large" onClick={terminal.new} />
|
<IconButton icon="plus-small" variant="ghost" iconSize="large" onClick={terminal.new} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import z from "zod"
|
||||||
|
|
||||||
|
const prefixes = {
|
||||||
|
session: "ses",
|
||||||
|
message: "msg",
|
||||||
|
permission: "per",
|
||||||
|
user: "usr",
|
||||||
|
part: "prt",
|
||||||
|
pty: "pty",
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const LENGTH = 26
|
||||||
|
let lastTimestamp = 0
|
||||||
|
let counter = 0
|
||||||
|
|
||||||
|
type Prefix = keyof typeof prefixes
|
||||||
|
export namespace Identifier {
|
||||||
|
export function schema(prefix: Prefix) {
|
||||||
|
return z.string().startsWith(prefixes[prefix])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ascending(prefix: Prefix, given?: string) {
|
||||||
|
return generateID(prefix, false, given)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function descending(prefix: Prefix, given?: string) {
|
||||||
|
return generateID(prefix, true, given)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateID(prefix: Prefix, descending: boolean, given?: string): string {
|
||||||
|
if (!given) {
|
||||||
|
return create(prefix, descending)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!given.startsWith(prefixes[prefix])) {
|
||||||
|
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return given
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(prefix: Prefix, descending: boolean, timestamp?: number): string {
|
||||||
|
const currentTimestamp = timestamp ?? Date.now()
|
||||||
|
|
||||||
|
if (currentTimestamp !== lastTimestamp) {
|
||||||
|
lastTimestamp = currentTimestamp
|
||||||
|
counter = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter)
|
||||||
|
|
||||||
|
if (descending) {
|
||||||
|
now = ~now
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeBytes = new Uint8Array(6)
|
||||||
|
for (let i = 0; i < 6; i += 1) {
|
||||||
|
timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefixes[prefix] + "_" + bytesToHex(timeBytes) + randomBase62(LENGTH - 12)
|
||||||
|
}
|
||||||
|
|
||||||
|
function bytesToHex(bytes: Uint8Array): string {
|
||||||
|
let hex = ""
|
||||||
|
for (let i = 0; i < bytes.length; i += 1) {
|
||||||
|
hex += bytes[i].toString(16).padStart(2, "0")
|
||||||
|
}
|
||||||
|
return hex
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomBase62(length: number): string {
|
||||||
|
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
const bytes = getRandomBytes(length)
|
||||||
|
let result = ""
|
||||||
|
for (let i = 0; i < length; i += 1) {
|
||||||
|
result += chars[bytes[i] % 62]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomBytes(length: number): Uint8Array {
|
||||||
|
const bytes = new Uint8Array(length)
|
||||||
|
const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : undefined
|
||||||
|
|
||||||
|
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
||||||
|
cryptoObj.getRandomValues(bytes)
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i += 1) {
|
||||||
|
bytes[i] = Math.floor(Math.random() * 256)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
@@ -10,5 +10,6 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
target: "esnext",
|
target: "esnext",
|
||||||
|
sourcemap: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ export default function () {
|
|||||||
{iife(() => {
|
{iife(() => {
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
messageId: undefined as string | undefined,
|
messageId: undefined as string | undefined,
|
||||||
|
expandedSteps: {} as Record<string, boolean>,
|
||||||
})
|
})
|
||||||
const messages = createMemo(() =>
|
const messages = createMemo(() =>
|
||||||
data().sessionID
|
data().sessionID
|
||||||
@@ -253,20 +254,22 @@ export default function () {
|
|||||||
|
|
||||||
const title = () => (
|
const title = () => (
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="h-8 flex gap-4 items-center justify-start self-stretch">
|
<div class="flex flex-col gap-2 sm:flex-row sm:gap-4 sm:items-center sm:h-8 justify-start self-stretch">
|
||||||
<div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
|
<div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base w-fit">
|
||||||
<Mark class="shrink-0 w-3 my-0.5" />
|
<Mark class="shrink-0 w-3 my-0.5" />
|
||||||
<div class="text-12-mono text-text-base">v{info().version}</div>
|
<div class="text-12-mono text-text-base">v{info().version}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2 items-center">
|
<div class="flex gap-4 items-center">
|
||||||
<ProviderIcon
|
<div class="flex gap-2 items-center">
|
||||||
id={provider() as IconName}
|
<ProviderIcon
|
||||||
class="size-3.5 shrink-0 text-icon-strong-base"
|
id={provider() as IconName}
|
||||||
/>
|
class="size-3.5 shrink-0 text-icon-strong-base"
|
||||||
<div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
|
/>
|
||||||
</div>
|
<div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
|
||||||
<div class="text-12-regular text-text-weaker">
|
</div>
|
||||||
{DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
|
<div class="text-12-regular text-text-weaker">
|
||||||
|
{DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-left text-16-medium text-text-strong">{info().title}</div>
|
<div class="text-left text-16-medium text-text-strong">{info().title}</div>
|
||||||
@@ -282,6 +285,8 @@ export default function () {
|
|||||||
<SessionTurn
|
<SessionTurn
|
||||||
sessionID={data().sessionID}
|
sessionID={data().sessionID}
|
||||||
messageID={message.id}
|
messageID={message.id}
|
||||||
|
stepsExpanded={store.expandedSteps[message.id] ?? false}
|
||||||
|
onStepsExpandedToggle={() => setStore("expandedSteps", message.id, (v) => !v)}
|
||||||
classes={{
|
classes={{
|
||||||
root: "min-w-0 w-full relative",
|
root: "min-w-0 w-full relative",
|
||||||
content:
|
content:
|
||||||
@@ -359,6 +364,13 @@ export default function () {
|
|||||||
<SessionTurn
|
<SessionTurn
|
||||||
sessionID={data().sessionID}
|
sessionID={data().sessionID}
|
||||||
messageID={store.messageId ?? firstUserMessage()!.id!}
|
messageID={store.messageId ?? firstUserMessage()!.id!}
|
||||||
|
stepsExpanded={
|
||||||
|
store.expandedSteps[store.messageId ?? firstUserMessage()!.id!] ?? false
|
||||||
|
}
|
||||||
|
onStepsExpandedToggle={() => {
|
||||||
|
const id = store.messageId ?? firstUserMessage()!.id!
|
||||||
|
setStore("expandedSteps", id, (v) => !v)
|
||||||
|
}}
|
||||||
classes={{
|
classes={{
|
||||||
root: "grow",
|
root: "grow",
|
||||||
content: "flex flex-col justify-between",
|
content: "flex flex-col justify-between",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
id = "opencode"
|
id = "opencode"
|
||||||
name = "OpenCode"
|
name = "OpenCode"
|
||||||
description = "The open source coding agent."
|
description = "The open source coding agent."
|
||||||
version = "1.0.167"
|
version = "1.0.187"
|
||||||
schema_version = 1
|
schema_version = 1
|
||||||
authors = ["Anomaly"]
|
authors = ["Anomaly"]
|
||||||
repository = "https://github.com/sst/opencode"
|
repository = "https://github.com/sst/opencode"
|
||||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
|||||||
icon = "./icons/opencode.svg"
|
icon = "./icons/opencode.svg"
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-aarch64]
|
[agent_servers.opencode.targets.darwin-aarch64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.167/opencode-darwin-arm64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-darwin-arm64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-x86_64]
|
[agent_servers.opencode.targets.darwin-x86_64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.167/opencode-darwin-x64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-darwin-x64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-aarch64]
|
[agent_servers.opencode.targets.linux-aarch64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.167/opencode-linux-arm64.tar.gz"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-linux-arm64.tar.gz"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-x86_64]
|
[agent_servers.opencode.targets.linux-x86_64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.167/opencode-linux-x64.tar.gz"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-linux-x64.tar.gz"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.windows-x86_64]
|
[agent_servers.opencode.targets.windows-x86_64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.167/opencode-windows-x64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-windows-x64.zip"
|
||||||
cmd = "./opencode.exe"
|
cmd = "./opencode.exe"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.0.167",
|
"version": "1.0.187",
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsgo --noEmit",
|
"typecheck": "tsgo --noEmit",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
"build": "./script/build.ts",
|
"build": "bun run script/build.ts",
|
||||||
"dev": "bun run --conditions=browser ./src/index.ts",
|
"dev": "bun run --conditions=browser ./src/index.ts",
|
||||||
"random": "echo 'Random script updated at $(date)' && echo 'Change queued successfully' && echo 'Another change made' && echo 'Yet another change' && echo 'One more change' && echo 'Final change' && echo 'Another final change' && echo 'Yet another final change'",
|
"random": "echo 'Random script updated at $(date)' && echo 'Change queued successfully' && echo 'Another change made' && echo 'Yet another change' && echo 'One more change' && echo 'Final change' && echo 'Another final change' && echo 'Yet another final change'",
|
||||||
"clean": "echo 'Cleaning up...' && rm -rf node_modules dist",
|
"clean": "echo 'Cleaning up...' && rm -rf node_modules dist",
|
||||||
@@ -55,10 +55,12 @@
|
|||||||
"@ai-sdk/google": "2.0.44",
|
"@ai-sdk/google": "2.0.44",
|
||||||
"@ai-sdk/google-vertex": "3.0.81",
|
"@ai-sdk/google-vertex": "3.0.81",
|
||||||
"@ai-sdk/mcp": "0.0.8",
|
"@ai-sdk/mcp": "0.0.8",
|
||||||
|
"@ai-sdk/mistral": "2.0.26",
|
||||||
"@ai-sdk/openai": "2.0.71",
|
"@ai-sdk/openai": "2.0.71",
|
||||||
"@ai-sdk/openai-compatible": "1.0.27",
|
"@ai-sdk/openai-compatible": "1.0.27",
|
||||||
"@ai-sdk/provider": "2.0.0",
|
"@ai-sdk/provider": "2.0.0",
|
||||||
"@ai-sdk/provider-utils": "3.0.18",
|
"@ai-sdk/provider-utils": "3.0.18",
|
||||||
|
"@ai-sdk/xai": "2.0.42",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
@@ -71,8 +73,8 @@
|
|||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
"@openrouter/ai-sdk-provider": "1.5.2",
|
"@openrouter/ai-sdk-provider": "1.5.2",
|
||||||
"@opentui/core": "0.1.61",
|
"@opentui/core": "0.1.63",
|
||||||
"@opentui/solid": "0.1.61",
|
"@opentui/solid": "0.1.63",
|
||||||
"@parcel/watcher": "2.5.1",
|
"@parcel/watcher": "2.5.1",
|
||||||
"@pierre/diffs": "catalog:",
|
"@pierre/diffs": "catalog:",
|
||||||
"@solid-primitives/event-bus": "1.1.2",
|
"@solid-primitives/event-bus": "1.1.2",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import pkg from "../package.json"
|
|||||||
import { Script } from "@opencode-ai/script"
|
import { Script } from "@opencode-ai/script"
|
||||||
|
|
||||||
const singleFlag = process.argv.includes("--single")
|
const singleFlag = process.argv.includes("--single")
|
||||||
|
const baselineFlag = process.argv.includes("--baseline")
|
||||||
const skipInstall = process.argv.includes("--skip-install")
|
const skipInstall = process.argv.includes("--skip-install")
|
||||||
|
|
||||||
const allTargets: {
|
const allTargets: {
|
||||||
@@ -78,7 +79,19 @@ const allTargets: {
|
|||||||
]
|
]
|
||||||
|
|
||||||
const targets = singleFlag
|
const targets = singleFlag
|
||||||
? allTargets.filter((item) => item.os === process.platform && item.arch === process.arch)
|
? allTargets.filter((item) => {
|
||||||
|
if (item.os !== process.platform || item.arch !== process.arch) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// When building for the current platform, prefer a single native binary by default.
|
||||||
|
// Baseline binaries require additional Bun artifacts and can be flaky to download.
|
||||||
|
if (item.avx2 === false) {
|
||||||
|
return baselineFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
: allTargets
|
: allTargets
|
||||||
|
|
||||||
await $`rm -rf dist`
|
await $`rm -rf dist`
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
import { $ } from "bun"
|
||||||
|
import { Script } from "@opencode-ai/script"
|
||||||
|
|
||||||
|
if (!Script.preview) {
|
||||||
|
// Calculate SHA values
|
||||||
|
const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim())
|
||||||
|
const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.tar.gz | 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)
|
||||||
|
|
||||||
|
// arch
|
||||||
|
const binaryPkgbuild = [
|
||||||
|
"# Maintainer: dax",
|
||||||
|
"# Maintainer: adam",
|
||||||
|
"",
|
||||||
|
"pkgname='opencode-bin'",
|
||||||
|
`pkgver=${pkgver}`,
|
||||||
|
`_subver=${_subver}`,
|
||||||
|
"options=('!debug' '!strip')",
|
||||||
|
"pkgrel=1",
|
||||||
|
"pkgdesc='The AI coding agent built for the terminal.'",
|
||||||
|
"url='https://github.com/sst/opencode'",
|
||||||
|
"arch=('aarch64' 'x86_64')",
|
||||||
|
"license=('MIT')",
|
||||||
|
"provides=('opencode')",
|
||||||
|
"conflicts=('opencode')",
|
||||||
|
"depends=('ripgrep')",
|
||||||
|
"",
|
||||||
|
`source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-arm64.tar.gz")`,
|
||||||
|
`sha256sums_aarch64=('${arm64Sha}')`,
|
||||||
|
|
||||||
|
`source_x86_64=("\${pkgname}_\${pkgver}_x86_64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-x64.tar.gz")`,
|
||||||
|
`sha256sums_x86_64=('${x64Sha}')`,
|
||||||
|
"",
|
||||||
|
"package() {",
|
||||||
|
' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"',
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
// Source-based PKGBUILD for opencode
|
||||||
|
const sourcePkgbuild = [
|
||||||
|
"# Maintainer: dax",
|
||||||
|
"# Maintainer: adam",
|
||||||
|
"",
|
||||||
|
"pkgname='opencode'",
|
||||||
|
`pkgver=${pkgver}`,
|
||||||
|
`_subver=${_subver}`,
|
||||||
|
"options=('!debug' '!strip')",
|
||||||
|
"pkgrel=1",
|
||||||
|
"pkgdesc='The AI coding agent built for the terminal.'",
|
||||||
|
"url='https://github.com/sst/opencode'",
|
||||||
|
"arch=('aarch64' 'x86_64')",
|
||||||
|
"license=('MIT')",
|
||||||
|
"provides=('opencode')",
|
||||||
|
"conflicts=('opencode-bin')",
|
||||||
|
"depends=('ripgrep')",
|
||||||
|
"makedepends=('git' 'bun-bin' 'go')",
|
||||||
|
"",
|
||||||
|
`source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v\${pkgver}\${_subver}.tar.gz")`,
|
||||||
|
`sha256sums=('SKIP')`,
|
||||||
|
"",
|
||||||
|
"build() {",
|
||||||
|
` cd "opencode-\${pkgver}"`,
|
||||||
|
` bun install`,
|
||||||
|
" cd ./packages/opencode",
|
||||||
|
` OPENCODE_CHANNEL=latest OPENCODE_VERSION=${pkgver} bun run ./script/build.ts --single`,
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
"package() {",
|
||||||
|
` cd "opencode-\${pkgver}/packages/opencode"`,
|
||||||
|
' mkdir -p "${pkgdir}/usr/bin"',
|
||||||
|
' target_arch="x64"',
|
||||||
|
' case "$CARCH" in',
|
||||||
|
' x86_64) target_arch="x64" ;;',
|
||||||
|
' aarch64) target_arch="arm64" ;;',
|
||||||
|
' *) printf "unsupported architecture: %s\\n" "$CARCH" >&2 ; return 1 ;;',
|
||||||
|
" esac",
|
||||||
|
' libc=""',
|
||||||
|
" if command -v ldd >/dev/null 2>&1; then",
|
||||||
|
" if ldd --version 2>&1 | grep -qi musl; then",
|
||||||
|
' libc="-musl"',
|
||||||
|
" fi",
|
||||||
|
" fi",
|
||||||
|
' if [ -z "$libc" ] && ls /lib/ld-musl-* >/dev/null 2>&1; then',
|
||||||
|
' libc="-musl"',
|
||||||
|
" fi",
|
||||||
|
' base=""',
|
||||||
|
' if [ "$target_arch" = "x64" ]; then',
|
||||||
|
" if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then",
|
||||||
|
' base="-baseline"',
|
||||||
|
" fi",
|
||||||
|
" fi",
|
||||||
|
' bin="dist/opencode-linux-${target_arch}${base}${libc}/bin/opencode"',
|
||||||
|
' if [ ! -f "$bin" ]; then',
|
||||||
|
' printf "unable to find binary for %s%s%s\\n" "$target_arch" "$base" "$libc" >&2',
|
||||||
|
" return 1",
|
||||||
|
" fi",
|
||||||
|
' install -Dm755 "$bin" "${pkgdir}/usr/bin/opencode"',
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
for (const [pkg, pkgbuild] of [
|
||||||
|
["opencode-bin", binaryPkgbuild],
|
||||||
|
["opencode", sourcePkgbuild],
|
||||||
|
]) {
|
||||||
|
for (let i = 0; i < 30; i++) {
|
||||||
|
try {
|
||||||
|
await $`rm -rf ./dist/aur-${pkg}`
|
||||||
|
await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}`
|
||||||
|
await $`cd ./dist/aur-${pkg} && git checkout master`
|
||||||
|
await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild)
|
||||||
|
await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
|
||||||
|
await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
|
||||||
|
await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"`
|
||||||
|
await $`cd ./dist/aur-${pkg} && git push`
|
||||||
|
break
|
||||||
|
} catch (e) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Homebrew formula
|
||||||
|
const homebrewFormula = [
|
||||||
|
"# typed: false",
|
||||||
|
"# frozen_string_literal: true",
|
||||||
|
"",
|
||||||
|
"# This file was generated by GoReleaser. DO NOT EDIT.",
|
||||||
|
"class Opencode < Formula",
|
||||||
|
` desc "The AI coding agent built for the terminal."`,
|
||||||
|
` homepage "https://github.com/sst/opencode"`,
|
||||||
|
` version "${Script.version.split("-")[0]}"`,
|
||||||
|
"",
|
||||||
|
` depends_on "ripgrep"`,
|
||||||
|
"",
|
||||||
|
" on_macos do",
|
||||||
|
" if Hardware::CPU.intel?",
|
||||||
|
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`,
|
||||||
|
` sha256 "${macX64Sha}"`,
|
||||||
|
"",
|
||||||
|
" def install",
|
||||||
|
' bin.install "opencode"',
|
||||||
|
" end",
|
||||||
|
" end",
|
||||||
|
" if Hardware::CPU.arm?",
|
||||||
|
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`,
|
||||||
|
` sha256 "${macArm64Sha}"`,
|
||||||
|
"",
|
||||||
|
" def install",
|
||||||
|
' bin.install "opencode"',
|
||||||
|
" end",
|
||||||
|
" end",
|
||||||
|
" end",
|
||||||
|
"",
|
||||||
|
" on_linux do",
|
||||||
|
" if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?",
|
||||||
|
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.tar.gz"`,
|
||||||
|
` sha256 "${x64Sha}"`,
|
||||||
|
" def install",
|
||||||
|
' bin.install "opencode"',
|
||||||
|
" end",
|
||||||
|
" end",
|
||||||
|
" if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?",
|
||||||
|
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.tar.gz"`,
|
||||||
|
` sha256 "${arm64Sha}"`,
|
||||||
|
" def install",
|
||||||
|
' bin.install "opencode"',
|
||||||
|
" end",
|
||||||
|
" end",
|
||||||
|
" end",
|
||||||
|
"end",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
await $`rm -rf ./dist/homebrew-tap`
|
||||||
|
await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap`
|
||||||
|
await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula)
|
||||||
|
await $`cd ./dist/homebrew-tap && git add opencode.rb`
|
||||||
|
await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"`
|
||||||
|
await $`cd ./dist/homebrew-tap && git push`
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ const tags = [Script.channel]
|
|||||||
|
|
||||||
const tasks = Object.entries(binaries).map(async ([name]) => {
|
const tasks = Object.entries(binaries).map(async ([name]) => {
|
||||||
if (process.platform !== "win32") {
|
if (process.platform !== "win32") {
|
||||||
await $`chmod 755 -R .`.cwd(`./dist/${name}`)
|
await $`chmod -R 755 .`.cwd(`./dist/${name}`)
|
||||||
}
|
}
|
||||||
await $`bun pm pack`.cwd(`./dist/${name}`)
|
await $`bun pm pack`.cwd(`./dist/${name}`)
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
@@ -53,196 +53,15 @@ for (const tag of tags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!Script.preview) {
|
if (!Script.preview) {
|
||||||
|
// Create archives for GitHub release
|
||||||
for (const key of Object.keys(binaries)) {
|
for (const key of Object.keys(binaries)) {
|
||||||
if (key.includes("linux")) {
|
if (key.includes("linux")) {
|
||||||
await $`cd dist/${key}/bin && tar -czf ../../${key}.tar.gz *`
|
await $`tar -czf ../../${key}.tar.gz *`.cwd(`dist/${key}/bin`)
|
||||||
} else {
|
} else {
|
||||||
await $`cd dist/${key}/bin && zip -r ../../${key}.zip *`
|
await $`zip -r ../../${key}.zip *`.cwd(`dist/${key}/bin`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate SHA values
|
|
||||||
const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim())
|
|
||||||
const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.tar.gz | 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)
|
|
||||||
|
|
||||||
// arch
|
|
||||||
const binaryPkgbuild = [
|
|
||||||
"# Maintainer: dax",
|
|
||||||
"# Maintainer: adam",
|
|
||||||
"",
|
|
||||||
"pkgname='opencode-bin'",
|
|
||||||
`pkgver=${pkgver}`,
|
|
||||||
`_subver=${_subver}`,
|
|
||||||
"options=('!debug' '!strip')",
|
|
||||||
"pkgrel=1",
|
|
||||||
"pkgdesc='The AI coding agent built for the terminal.'",
|
|
||||||
"url='https://github.com/sst/opencode'",
|
|
||||||
"arch=('aarch64' 'x86_64')",
|
|
||||||
"license=('MIT')",
|
|
||||||
"provides=('opencode')",
|
|
||||||
"conflicts=('opencode')",
|
|
||||||
"depends=('ripgrep')",
|
|
||||||
"",
|
|
||||||
`source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-arm64.tar.gz")`,
|
|
||||||
`sha256sums_aarch64=('${arm64Sha}')`,
|
|
||||||
|
|
||||||
`source_x86_64=("\${pkgname}_\${pkgver}_x86_64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-x64.tar.gz")`,
|
|
||||||
`sha256sums_x86_64=('${x64Sha}')`,
|
|
||||||
"",
|
|
||||||
"package() {",
|
|
||||||
' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"',
|
|
||||||
"}",
|
|
||||||
"",
|
|
||||||
].join("\n")
|
|
||||||
|
|
||||||
// Source-based PKGBUILD for opencode
|
|
||||||
const sourcePkgbuild = [
|
|
||||||
"# Maintainer: dax",
|
|
||||||
"# Maintainer: adam",
|
|
||||||
"",
|
|
||||||
"pkgname='opencode'",
|
|
||||||
`pkgver=${pkgver}`,
|
|
||||||
`_subver=${_subver}`,
|
|
||||||
"options=('!debug' '!strip')",
|
|
||||||
"pkgrel=1",
|
|
||||||
"pkgdesc='The AI coding agent built for the terminal.'",
|
|
||||||
"url='https://github.com/sst/opencode'",
|
|
||||||
"arch=('aarch64' 'x86_64')",
|
|
||||||
"license=('MIT')",
|
|
||||||
"provides=('opencode')",
|
|
||||||
"conflicts=('opencode-bin')",
|
|
||||||
"depends=('ripgrep')",
|
|
||||||
"makedepends=('git' 'bun-bin' 'go')",
|
|
||||||
"",
|
|
||||||
`source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v\${pkgver}\${_subver}.tar.gz")`,
|
|
||||||
`sha256sums=('SKIP')`,
|
|
||||||
"",
|
|
||||||
"build() {",
|
|
||||||
` cd "opencode-\${pkgver}"`,
|
|
||||||
` bun install`,
|
|
||||||
" cd ./packages/opencode",
|
|
||||||
` OPENCODE_CHANNEL=latest OPENCODE_VERSION=${pkgver} bun run ./script/build.ts --single`,
|
|
||||||
"}",
|
|
||||||
"",
|
|
||||||
"package() {",
|
|
||||||
` cd "opencode-\${pkgver}/packages/opencode"`,
|
|
||||||
' mkdir -p "${pkgdir}/usr/bin"',
|
|
||||||
' target_arch="x64"',
|
|
||||||
' case "$CARCH" in',
|
|
||||||
' x86_64) target_arch="x64" ;;',
|
|
||||||
' aarch64) target_arch="arm64" ;;',
|
|
||||||
' *) printf "unsupported architecture: %s\\n" "$CARCH" >&2 ; return 1 ;;',
|
|
||||||
" esac",
|
|
||||||
' libc=""',
|
|
||||||
" if command -v ldd >/dev/null 2>&1; then",
|
|
||||||
" if ldd --version 2>&1 | grep -qi musl; then",
|
|
||||||
' libc="-musl"',
|
|
||||||
" fi",
|
|
||||||
" fi",
|
|
||||||
' if [ -z "$libc" ] && ls /lib/ld-musl-* >/dev/null 2>&1; then',
|
|
||||||
' libc="-musl"',
|
|
||||||
" fi",
|
|
||||||
' base=""',
|
|
||||||
' if [ "$target_arch" = "x64" ]; then',
|
|
||||||
" if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then",
|
|
||||||
' base="-baseline"',
|
|
||||||
" fi",
|
|
||||||
" fi",
|
|
||||||
' bin="dist/opencode-linux-${target_arch}${base}${libc}/bin/opencode"',
|
|
||||||
' if [ ! -f "$bin" ]; then',
|
|
||||||
' printf "unable to find binary for %s%s%s\\n" "$target_arch" "$base" "$libc" >&2',
|
|
||||||
" return 1",
|
|
||||||
" fi",
|
|
||||||
' install -Dm755 "$bin" "${pkgdir}/usr/bin/opencode"',
|
|
||||||
"}",
|
|
||||||
"",
|
|
||||||
].join("\n")
|
|
||||||
|
|
||||||
for (const [pkg, pkgbuild] of [
|
|
||||||
["opencode-bin", binaryPkgbuild],
|
|
||||||
["opencode", sourcePkgbuild],
|
|
||||||
]) {
|
|
||||||
for (let i = 0; i < 30; i++) {
|
|
||||||
try {
|
|
||||||
await $`rm -rf ./dist/aur-${pkg}`
|
|
||||||
await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}`
|
|
||||||
await $`cd ./dist/aur-${pkg} && git checkout master`
|
|
||||||
await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild)
|
|
||||||
await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
|
|
||||||
await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
|
|
||||||
await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"`
|
|
||||||
await $`cd ./dist/aur-${pkg} && git push`
|
|
||||||
break
|
|
||||||
} catch (e) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Homebrew formula
|
|
||||||
const homebrewFormula = [
|
|
||||||
"# typed: false",
|
|
||||||
"# frozen_string_literal: true",
|
|
||||||
"",
|
|
||||||
"# This file was generated by GoReleaser. DO NOT EDIT.",
|
|
||||||
"class Opencode < Formula",
|
|
||||||
` desc "The AI coding agent built for the terminal."`,
|
|
||||||
` homepage "https://github.com/sst/opencode"`,
|
|
||||||
` version "${Script.version.split("-")[0]}"`,
|
|
||||||
"",
|
|
||||||
` depends_on "ripgrep"`,
|
|
||||||
"",
|
|
||||||
" on_macos do",
|
|
||||||
" if Hardware::CPU.intel?",
|
|
||||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`,
|
|
||||||
` sha256 "${macX64Sha}"`,
|
|
||||||
"",
|
|
||||||
" def install",
|
|
||||||
' bin.install "opencode"',
|
|
||||||
" end",
|
|
||||||
" end",
|
|
||||||
" if Hardware::CPU.arm?",
|
|
||||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`,
|
|
||||||
` sha256 "${macArm64Sha}"`,
|
|
||||||
"",
|
|
||||||
" def install",
|
|
||||||
' bin.install "opencode"',
|
|
||||||
" end",
|
|
||||||
" end",
|
|
||||||
" end",
|
|
||||||
"",
|
|
||||||
" on_linux do",
|
|
||||||
" if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?",
|
|
||||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.tar.gz"`,
|
|
||||||
` sha256 "${x64Sha}"`,
|
|
||||||
" def install",
|
|
||||||
' bin.install "opencode"',
|
|
||||||
" end",
|
|
||||||
" end",
|
|
||||||
" if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?",
|
|
||||||
` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.tar.gz"`,
|
|
||||||
` sha256 "${arm64Sha}"`,
|
|
||||||
" def install",
|
|
||||||
' bin.install "opencode"',
|
|
||||||
" end",
|
|
||||||
" end",
|
|
||||||
" end",
|
|
||||||
"end",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
].join("\n")
|
|
||||||
|
|
||||||
await $`rm -rf ./dist/homebrew-tap`
|
|
||||||
await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap`
|
|
||||||
await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula)
|
|
||||||
await $`cd ./dist/homebrew-tap && git add opencode.rb`
|
|
||||||
await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"`
|
|
||||||
await $`cd ./dist/homebrew-tap && git push`
|
|
||||||
|
|
||||||
const image = "ghcr.io/sst/opencode"
|
const image = "ghcr.io/sst/opencode"
|
||||||
const platforms = "linux/amd64,linux/arm64"
|
const platforms = "linux/amd64,linux/arm64"
|
||||||
const tags = [`${image}:${Script.version}`, `${image}:latest`]
|
const tags = [`${image}:${Script.version}`, `${image}:latest`]
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { Log } from "../util/log"
|
|||||||
import { ACPSessionManager } from "./session"
|
import { ACPSessionManager } from "./session"
|
||||||
import type { ACPConfig, ACPSessionState } from "./types"
|
import type { ACPConfig, ACPSessionState } from "./types"
|
||||||
import { Provider } from "../provider/provider"
|
import { Provider } from "../provider/provider"
|
||||||
|
import { Agent as AgentModule } from "../agent/agent"
|
||||||
import { Installation } from "@/installation"
|
import { Installation } from "@/installation"
|
||||||
import { MessageV2 } from "@/session/message-v2"
|
import { MessageV2 } from "@/session/message-v2"
|
||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
@@ -698,14 +699,15 @@ export namespace ACP {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const availableModes = agents
|
const availableModes = agents
|
||||||
.filter((agent) => agent.mode !== "subagent")
|
.filter((agent) => agent.mode !== "subagent" && !agent.hidden)
|
||||||
.map((agent) => ({
|
.map((agent) => ({
|
||||||
id: agent.name,
|
id: agent.name,
|
||||||
name: agent.name,
|
name: agent.name,
|
||||||
description: agent.description,
|
description: agent.description,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const currentModeId = availableModes.find((m) => m.name === "build")?.id ?? availableModes[0].id
|
const defaultAgentName = await AgentModule.defaultAgent()
|
||||||
|
const currentModeId = availableModes.find((m) => m.name === defaultAgentName)?.id ?? availableModes[0].id
|
||||||
|
|
||||||
const mcpServers: Record<string, Config.Mcp> = {}
|
const mcpServers: Record<string, Config.Mcp> = {}
|
||||||
for (const server of params.mcpServers) {
|
for (const server of params.mcpServers) {
|
||||||
@@ -807,7 +809,7 @@ export namespace ACP {
|
|||||||
if (!current) {
|
if (!current) {
|
||||||
this.sessionManager.setModel(session.id, model)
|
this.sessionManager.setModel(session.id, model)
|
||||||
}
|
}
|
||||||
const agent = session.modeId ?? "build"
|
const agent = session.modeId ?? (await AgentModule.defaultAgent())
|
||||||
|
|
||||||
const parts: Array<
|
const parts: Array<
|
||||||
{ type: "text"; text: string } | { type: "file"; url: string; filename: string; mime: string }
|
{ type: "text"; text: string } | { type: "file"; url: string; filename: string; mime: string }
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { generateObject, type ModelMessage } from "ai"
|
|||||||
import { SystemPrompt } from "../session/system"
|
import { SystemPrompt } from "../session/system"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { mergeDeep } from "remeda"
|
import { mergeDeep } from "remeda"
|
||||||
|
import { Log } from "../util/log"
|
||||||
|
|
||||||
|
const log = Log.create({ service: "agent" })
|
||||||
|
|
||||||
import PROMPT_GENERATE from "./generate.txt"
|
import PROMPT_GENERATE from "./generate.txt"
|
||||||
import PROMPT_COMPACTION from "./prompt/compaction.txt"
|
import PROMPT_COMPACTION from "./prompt/compaction.txt"
|
||||||
@@ -20,12 +23,14 @@ export namespace Agent {
|
|||||||
mode: z.enum(["subagent", "primary", "all"]),
|
mode: z.enum(["subagent", "primary", "all"]),
|
||||||
native: z.boolean().optional(),
|
native: z.boolean().optional(),
|
||||||
hidden: z.boolean().optional(),
|
hidden: z.boolean().optional(),
|
||||||
|
default: z.boolean().optional(),
|
||||||
topP: z.number().optional(),
|
topP: z.number().optional(),
|
||||||
temperature: z.number().optional(),
|
temperature: z.number().optional(),
|
||||||
color: z.string().optional(),
|
color: z.string().optional(),
|
||||||
permission: z.object({
|
permission: z.object({
|
||||||
edit: Config.Permission,
|
edit: Config.Permission,
|
||||||
bash: z.record(z.string(), Config.Permission),
|
bash: z.record(z.string(), Config.Permission),
|
||||||
|
skill: z.record(z.string(), Config.Permission),
|
||||||
webfetch: Config.Permission.optional(),
|
webfetch: Config.Permission.optional(),
|
||||||
doom_loop: Config.Permission.optional(),
|
doom_loop: Config.Permission.optional(),
|
||||||
external_directory: Config.Permission.optional(),
|
external_directory: Config.Permission.optional(),
|
||||||
@@ -54,6 +59,9 @@ export namespace Agent {
|
|||||||
bash: {
|
bash: {
|
||||||
"*": "allow",
|
"*": "allow",
|
||||||
},
|
},
|
||||||
|
skill: {
|
||||||
|
"*": "allow",
|
||||||
|
},
|
||||||
webfetch: "allow",
|
webfetch: "allow",
|
||||||
doom_loop: "ask",
|
doom_loop: "ask",
|
||||||
external_directory: "ask",
|
external_directory: "ask",
|
||||||
@@ -245,6 +253,27 @@ export namespace Agent {
|
|||||||
item.permission = mergeAgentPermissions(cfg.permission ?? {}, permission ?? {})
|
item.permission = mergeAgentPermissions(cfg.permission ?? {}, permission ?? {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark the default agent
|
||||||
|
const defaultName = cfg.default_agent ?? "build"
|
||||||
|
const defaultCandidate = result[defaultName]
|
||||||
|
if (defaultCandidate && defaultCandidate.mode !== "subagent") {
|
||||||
|
defaultCandidate.default = true
|
||||||
|
} else {
|
||||||
|
// Fall back to "build" if configured default is invalid
|
||||||
|
if (result["build"]) {
|
||||||
|
result["build"].default = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPrimaryAgents = Object.values(result).filter((a) => a.mode !== "subagent" && !a.hidden).length > 0
|
||||||
|
if (!hasPrimaryAgents) {
|
||||||
|
throw new Config.InvalidError({
|
||||||
|
path: "config",
|
||||||
|
message: "No primary agents are available. Please configure at least one agent with mode 'primary' or 'all'.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -256,6 +285,12 @@ export namespace Agent {
|
|||||||
return state().then((x) => Object.values(x))
|
return state().then((x) => Object.values(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function defaultAgent(): Promise<string> {
|
||||||
|
const agents = await state()
|
||||||
|
const defaultCandidate = Object.values(agents).find((a) => a.default)
|
||||||
|
return defaultCandidate?.name ?? "build"
|
||||||
|
}
|
||||||
|
|
||||||
export async function generate(input: { description: string; model?: { providerID: string; modelID: string } }) {
|
export async function generate(input: { description: string; model?: { providerID: string; modelID: string } }) {
|
||||||
const cfg = await Config.get()
|
const cfg = await Config.get()
|
||||||
const defaultModel = input.model ?? (await Provider.defaultModel())
|
const defaultModel = input.model ?? (await Provider.defaultModel())
|
||||||
@@ -306,6 +341,17 @@ function mergeAgentPermissions(basePermission: any, overridePermission: any): Ag
|
|||||||
"*": overridePermission.bash,
|
"*": overridePermission.bash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof basePermission.skill === "string") {
|
||||||
|
basePermission.skill = {
|
||||||
|
"*": basePermission.skill,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof overridePermission.skill === "string") {
|
||||||
|
overridePermission.skill = {
|
||||||
|
"*": overridePermission.skill,
|
||||||
|
}
|
||||||
|
}
|
||||||
const merged = mergeDeep(basePermission ?? {}, overridePermission ?? {}) as any
|
const merged = mergeDeep(basePermission ?? {}, overridePermission ?? {}) as any
|
||||||
let mergedBash
|
let mergedBash
|
||||||
if (merged.bash) {
|
if (merged.bash) {
|
||||||
@@ -323,10 +369,27 @@ function mergeAgentPermissions(basePermission: any, overridePermission: any): Ag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mergedSkill
|
||||||
|
if (merged.skill) {
|
||||||
|
if (typeof merged.skill === "string") {
|
||||||
|
mergedSkill = {
|
||||||
|
"*": merged.skill,
|
||||||
|
}
|
||||||
|
} else if (typeof merged.skill === "object") {
|
||||||
|
mergedSkill = mergeDeep(
|
||||||
|
{
|
||||||
|
"*": "allow",
|
||||||
|
},
|
||||||
|
merged.skill,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result: Agent.Info["permission"] = {
|
const result: Agent.Info["permission"] = {
|
||||||
edit: merged.edit ?? "allow",
|
edit: merged.edit ?? "allow",
|
||||||
webfetch: merged.webfetch ?? "allow",
|
webfetch: merged.webfetch ?? "allow",
|
||||||
bash: mergedBash ?? { "*": "allow" },
|
bash: mergedBash ?? { "*": "allow" },
|
||||||
|
skill: mergedSkill ?? { "*": "allow" },
|
||||||
doom_loop: merged.doom_loop,
|
doom_loop: merged.doom_loop,
|
||||||
external_directory: merged.external_directory,
|
external_directory: merged.external_directory,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
Summarize the following conversation into 2 sentences MAX explaining what the
|
Summarize what was done in this conversation. Write like a pull request description.
|
||||||
assistant did and why
|
|
||||||
Do not explain the user's input.
|
Rules:
|
||||||
Do not speak in the third person about the assistant.
|
- 2-3 sentences max
|
||||||
|
- Describe the changes made, not the process
|
||||||
|
- Do not mention running tests, builds, or other validation steps
|
||||||
|
- Do not explain what the user asked for
|
||||||
|
- Write in first person (I added..., I fixed...)
|
||||||
|
- Never ask questions or add new questions
|
||||||
|
- Only exception: if the conversation ends with an unanswered question to the user, preserve that exact question
|
||||||
|
|||||||
@@ -111,22 +111,4 @@ export namespace BunProc {
|
|||||||
await Bun.write(pkgjson.name!, JSON.stringify(parsed, null, 2))
|
await Bun.write(pkgjson.name!, JSON.stringify(parsed, null, 2))
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolve(pkg: string) {
|
|
||||||
const local = workspace(pkg)
|
|
||||||
if (local) return local
|
|
||||||
const dir = path.join(Global.Path.cache, "node_modules", pkg)
|
|
||||||
const pkgjson = Bun.file(path.join(dir, "package.json"))
|
|
||||||
const exists = await pkgjson.exists()
|
|
||||||
if (exists) return dir
|
|
||||||
}
|
|
||||||
|
|
||||||
function workspace(pkg: string) {
|
|
||||||
try {
|
|
||||||
const target = req.resolve(`${pkg}/package.json`)
|
|
||||||
return path.dirname(target)
|
|
||||||
} catch {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { FileCommand } from "./file"
|
|||||||
import { LSPCommand } from "./lsp"
|
import { LSPCommand } from "./lsp"
|
||||||
import { RipgrepCommand } from "./ripgrep"
|
import { RipgrepCommand } from "./ripgrep"
|
||||||
import { ScrapCommand } from "./scrap"
|
import { ScrapCommand } from "./scrap"
|
||||||
|
import { SkillCommand } from "./skill"
|
||||||
import { SnapshotCommand } from "./snapshot"
|
import { SnapshotCommand } from "./snapshot"
|
||||||
|
|
||||||
export const DebugCommand = cmd({
|
export const DebugCommand = cmd({
|
||||||
@@ -17,6 +18,7 @@ export const DebugCommand = cmd({
|
|||||||
.command(RipgrepCommand)
|
.command(RipgrepCommand)
|
||||||
.command(FileCommand)
|
.command(FileCommand)
|
||||||
.command(ScrapCommand)
|
.command(ScrapCommand)
|
||||||
|
.command(SkillCommand)
|
||||||
.command(SnapshotCommand)
|
.command(SnapshotCommand)
|
||||||
.command(PathsCommand)
|
.command(PathsCommand)
|
||||||
.command({
|
.command({
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { EOL } from "os"
|
||||||
|
import { Skill } from "../../../skill"
|
||||||
|
import { bootstrap } from "../../bootstrap"
|
||||||
|
import { cmd } from "../cmd"
|
||||||
|
|
||||||
|
export const SkillCommand = cmd({
|
||||||
|
command: "skill",
|
||||||
|
builder: (yargs) => yargs,
|
||||||
|
async handler() {
|
||||||
|
await bootstrap(process.cwd(), async () => {
|
||||||
|
const skills = await Skill.all()
|
||||||
|
process.stdout.write(JSON.stringify(skills, null, 2) + EOL)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -7,7 +7,7 @@ import { graphql } from "@octokit/graphql"
|
|||||||
import * as core from "@actions/core"
|
import * as core from "@actions/core"
|
||||||
import * as github from "@actions/github"
|
import * as github from "@actions/github"
|
||||||
import type { Context } from "@actions/github/lib/context"
|
import type { Context } from "@actions/github/lib/context"
|
||||||
import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types"
|
import type { IssueCommentEvent, PullRequestReviewCommentEvent, PullRequestEvent } from "@octokit/webhooks-types"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { ModelsDev } from "../../provider/models"
|
import { ModelsDev } from "../../provider/models"
|
||||||
@@ -127,6 +127,7 @@ type IssueQueryResponse = {
|
|||||||
const AGENT_USERNAME = "opencode-agent[bot]"
|
const AGENT_USERNAME = "opencode-agent[bot]"
|
||||||
const AGENT_REACTION = "eyes"
|
const AGENT_REACTION = "eyes"
|
||||||
const WORKFLOW_FILE = ".github/workflows/opencode.yml"
|
const WORKFLOW_FILE = ".github/workflows/opencode.yml"
|
||||||
|
const SUPPORTED_EVENTS = ["issue_comment", "pull_request_review_comment", "schedule", "pull_request"] as const
|
||||||
|
|
||||||
// Parses GitHub remote URLs in various formats:
|
// Parses GitHub remote URLs in various formats:
|
||||||
// - https://github.com/owner/repo.git
|
// - https://github.com/owner/repo.git
|
||||||
@@ -387,24 +388,30 @@ export const GithubRunCommand = cmd({
|
|||||||
const isMock = args.token || args.event
|
const isMock = args.token || args.event
|
||||||
|
|
||||||
const context = isMock ? (JSON.parse(args.event!) as Context) : github.context
|
const context = isMock ? (JSON.parse(args.event!) as Context) : github.context
|
||||||
if (context.eventName !== "issue_comment" && context.eventName !== "pull_request_review_comment") {
|
if (!SUPPORTED_EVENTS.includes(context.eventName as (typeof SUPPORTED_EVENTS)[number])) {
|
||||||
core.setFailed(`Unsupported event type: ${context.eventName}`)
|
core.setFailed(`Unsupported event type: ${context.eventName}`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
const isCommentEvent = ["issue_comment", "pull_request_review_comment"].includes(context.eventName)
|
||||||
|
const isScheduleEvent = context.eventName === "schedule"
|
||||||
|
|
||||||
const { providerID, modelID } = normalizeModel()
|
const { providerID, modelID } = normalizeModel()
|
||||||
const runId = normalizeRunId()
|
const runId = normalizeRunId()
|
||||||
const share = normalizeShare()
|
const share = normalizeShare()
|
||||||
const oidcBaseUrl = normalizeOidcBaseUrl()
|
const oidcBaseUrl = normalizeOidcBaseUrl()
|
||||||
const { owner, repo } = context.repo
|
const { owner, repo } = context.repo
|
||||||
const payload = context.payload as IssueCommentEvent | PullRequestReviewCommentEvent
|
// For schedule events, payload has no issue/comment data
|
||||||
const issueEvent = isIssueCommentEvent(payload) ? payload : undefined
|
const payload = isCommentEvent
|
||||||
const actor = context.actor
|
? (context.payload as IssueCommentEvent | PullRequestReviewCommentEvent)
|
||||||
|
: undefined
|
||||||
|
const issueEvent = payload && isIssueCommentEvent(payload) ? payload : undefined
|
||||||
|
const actor = isScheduleEvent ? undefined : context.actor
|
||||||
|
|
||||||
const issueId =
|
const issueId = isScheduleEvent
|
||||||
context.eventName === "pull_request_review_comment"
|
? undefined
|
||||||
? (payload as PullRequestReviewCommentEvent).pull_request.number
|
: context.eventName === "issue_comment"
|
||||||
: (payload as IssueCommentEvent).issue.number
|
? (payload as IssueCommentEvent).issue.number
|
||||||
|
: (payload as PullRequestEvent | PullRequestReviewCommentEvent).pull_request.number
|
||||||
const runUrl = `/${owner}/${repo}/actions/runs/${runId}`
|
const runUrl = `/${owner}/${repo}/actions/runs/${runId}`
|
||||||
const shareBaseUrl = isMock ? "https://dev.opencode.ai" : "https://opencode.ai"
|
const shareBaseUrl = isMock ? "https://dev.opencode.ai" : "https://opencode.ai"
|
||||||
|
|
||||||
@@ -416,8 +423,13 @@ export const GithubRunCommand = cmd({
|
|||||||
let shareId: string | undefined
|
let shareId: string | undefined
|
||||||
let exitCode = 0
|
let exitCode = 0
|
||||||
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
|
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
|
||||||
const triggerCommentId = payload.comment.id
|
const triggerCommentId = payload?.comment.id
|
||||||
const useGithubToken = normalizeUseGithubToken()
|
const useGithubToken = normalizeUseGithubToken()
|
||||||
|
const commentType = isCommentEvent
|
||||||
|
? context.eventName === "pull_request_review_comment"
|
||||||
|
? "pr_review"
|
||||||
|
: "issue"
|
||||||
|
: undefined
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (useGithubToken) {
|
if (useGithubToken) {
|
||||||
@@ -441,9 +453,11 @@ export const GithubRunCommand = cmd({
|
|||||||
if (!useGithubToken) {
|
if (!useGithubToken) {
|
||||||
await configureGit(appToken)
|
await configureGit(appToken)
|
||||||
}
|
}
|
||||||
await assertPermissions()
|
// Skip permission check for schedule events (no actor to check)
|
||||||
|
if (!isScheduleEvent) {
|
||||||
await addReaction()
|
await assertPermissions()
|
||||||
|
await addReaction(commentType)
|
||||||
|
}
|
||||||
|
|
||||||
// Setup opencode session
|
// Setup opencode session
|
||||||
const repoData = await fetchRepo()
|
const repoData = await fetchRepo()
|
||||||
@@ -457,11 +471,34 @@ export const GithubRunCommand = cmd({
|
|||||||
})()
|
})()
|
||||||
console.log("opencode session", session.id)
|
console.log("opencode session", session.id)
|
||||||
|
|
||||||
// Handle 3 cases
|
// Handle 4 cases
|
||||||
// 1. Issue
|
// 1. Schedule (no issue/PR context)
|
||||||
// 2. Local PR
|
// 2. Issue
|
||||||
// 3. Fork PR
|
// 3. Local PR
|
||||||
if (context.eventName === "pull_request_review_comment" || issueEvent?.issue.pull_request) {
|
// 4. Fork PR
|
||||||
|
if (isScheduleEvent) {
|
||||||
|
// Schedule event - no issue/PR context, output goes to logs
|
||||||
|
const branch = await checkoutNewBranch("schedule")
|
||||||
|
const head = (await $`git rev-parse HEAD`).stdout.toString().trim()
|
||||||
|
const response = await chat(userPrompt, promptFiles)
|
||||||
|
const { dirty, uncommittedChanges } = await branchIsDirty(head)
|
||||||
|
if (dirty) {
|
||||||
|
const summary = await summarize(response)
|
||||||
|
await pushToNewBranch(summary, branch, uncommittedChanges, true)
|
||||||
|
const pr = await createPR(
|
||||||
|
repoData.data.default_branch,
|
||||||
|
branch,
|
||||||
|
summary,
|
||||||
|
`${response}\n\nTriggered by scheduled workflow${footer({ image: true })}`,
|
||||||
|
)
|
||||||
|
console.log(`Created PR #${pr}`)
|
||||||
|
} else {
|
||||||
|
console.log("Response:", response)
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
["pull_request", "pull_request_review_comment"].includes(context.eventName) ||
|
||||||
|
issueEvent?.issue.pull_request
|
||||||
|
) {
|
||||||
const prData = await fetchPR()
|
const prData = await fetchPR()
|
||||||
// Local PR
|
// Local PR
|
||||||
if (prData.headRepository.nameWithOwner === prData.baseRepository.nameWithOwner) {
|
if (prData.headRepository.nameWithOwner === prData.baseRepository.nameWithOwner) {
|
||||||
@@ -476,7 +513,7 @@ export const GithubRunCommand = cmd({
|
|||||||
}
|
}
|
||||||
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`))
|
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`))
|
||||||
await createComment(`${response}${footer({ image: !hasShared })}`)
|
await createComment(`${response}${footer({ image: !hasShared })}`)
|
||||||
await removeReaction()
|
await removeReaction(commentType)
|
||||||
}
|
}
|
||||||
// Fork PR
|
// Fork PR
|
||||||
else {
|
else {
|
||||||
@@ -491,12 +528,12 @@ export const GithubRunCommand = cmd({
|
|||||||
}
|
}
|
||||||
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`))
|
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`))
|
||||||
await createComment(`${response}${footer({ image: !hasShared })}`)
|
await createComment(`${response}${footer({ image: !hasShared })}`)
|
||||||
await removeReaction()
|
await removeReaction(commentType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Issue
|
// Issue
|
||||||
else {
|
else {
|
||||||
const branch = await checkoutNewBranch()
|
const branch = await checkoutNewBranch("issue")
|
||||||
const head = (await $`git rev-parse HEAD`).stdout.toString().trim()
|
const head = (await $`git rev-parse HEAD`).stdout.toString().trim()
|
||||||
const issueData = await fetchIssue()
|
const issueData = await fetchIssue()
|
||||||
const dataPrompt = buildPromptDataForIssue(issueData)
|
const dataPrompt = buildPromptDataForIssue(issueData)
|
||||||
@@ -504,7 +541,7 @@ export const GithubRunCommand = cmd({
|
|||||||
const { dirty, uncommittedChanges } = await branchIsDirty(head)
|
const { dirty, uncommittedChanges } = await branchIsDirty(head)
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
const summary = await summarize(response)
|
const summary = await summarize(response)
|
||||||
await pushToNewBranch(summary, branch, uncommittedChanges)
|
await pushToNewBranch(summary, branch, uncommittedChanges, false)
|
||||||
const pr = await createPR(
|
const pr = await createPR(
|
||||||
repoData.data.default_branch,
|
repoData.data.default_branch,
|
||||||
branch,
|
branch,
|
||||||
@@ -512,10 +549,10 @@ export const GithubRunCommand = cmd({
|
|||||||
`${response}\n\nCloses #${issueId}${footer({ image: true })}`,
|
`${response}\n\nCloses #${issueId}${footer({ image: true })}`,
|
||||||
)
|
)
|
||||||
await createComment(`Created PR #${pr}${footer({ image: true })}`)
|
await createComment(`Created PR #${pr}${footer({ image: true })}`)
|
||||||
await removeReaction()
|
await removeReaction(commentType)
|
||||||
} else {
|
} else {
|
||||||
await createComment(`${response}${footer({ image: true })}`)
|
await createComment(`${response}${footer({ image: true })}`)
|
||||||
await removeReaction()
|
await removeReaction(commentType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -527,8 +564,10 @@ export const GithubRunCommand = cmd({
|
|||||||
} else if (e instanceof Error) {
|
} else if (e instanceof Error) {
|
||||||
msg = e.message
|
msg = e.message
|
||||||
}
|
}
|
||||||
await createComment(`${msg}${footer()}`)
|
if (!isScheduleEvent) {
|
||||||
await removeReaction()
|
await createComment(`${msg}${footer()}`)
|
||||||
|
await removeReaction(commentType)
|
||||||
|
}
|
||||||
core.setFailed(msg)
|
core.setFailed(msg)
|
||||||
// Also output the clean error message for the action to capture
|
// Also output the clean error message for the action to capture
|
||||||
//core.setOutput("prepare_error", e.message);
|
//core.setOutput("prepare_error", e.message);
|
||||||
@@ -604,6 +643,14 @@ export const GithubRunCommand = cmd({
|
|||||||
|
|
||||||
async function getUserPrompt() {
|
async function getUserPrompt() {
|
||||||
const customPrompt = process.env["PROMPT"]
|
const customPrompt = process.env["PROMPT"]
|
||||||
|
// For schedule events, PROMPT is required since there's no comment to extract from
|
||||||
|
if (isScheduleEvent) {
|
||||||
|
if (!customPrompt) {
|
||||||
|
throw new Error("PROMPT input is required for scheduled events")
|
||||||
|
}
|
||||||
|
return { userPrompt: customPrompt, promptFiles: [] }
|
||||||
|
}
|
||||||
|
|
||||||
if (customPrompt) {
|
if (customPrompt) {
|
||||||
return { userPrompt: customPrompt, promptFiles: [] }
|
return { userPrompt: customPrompt, promptFiles: [] }
|
||||||
}
|
}
|
||||||
@@ -614,7 +661,10 @@ export const GithubRunCommand = cmd({
|
|||||||
.map((m) => m.trim().toLowerCase())
|
.map((m) => m.trim().toLowerCase())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
let prompt = (() => {
|
let prompt = (() => {
|
||||||
const body = payload.comment.body.trim()
|
if (!isCommentEvent) {
|
||||||
|
return "Review this pull request"
|
||||||
|
}
|
||||||
|
const body = payload!.comment.body.trim()
|
||||||
const bodyLower = body.toLowerCase()
|
const bodyLower = body.toLowerCase()
|
||||||
if (mentions.some((m) => bodyLower === m)) {
|
if (mentions.some((m) => bodyLower === m)) {
|
||||||
if (reviewContext) {
|
if (reviewContext) {
|
||||||
@@ -761,7 +811,7 @@ export const GithubRunCommand = cmd({
|
|||||||
providerID,
|
providerID,
|
||||||
modelID,
|
modelID,
|
||||||
},
|
},
|
||||||
agent: "build",
|
// agent is omitted - server will use default_agent from config or fall back to "build"
|
||||||
parts: [
|
parts: [
|
||||||
{
|
{
|
||||||
id: Identifier.ascending("part"),
|
id: Identifier.ascending("part"),
|
||||||
@@ -864,9 +914,9 @@ export const GithubRunCommand = cmd({
|
|||||||
await $`git config --local ${config} "${gitConfig}"`
|
await $`git config --local ${config} "${gitConfig}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkoutNewBranch() {
|
async function checkoutNewBranch(type: "issue" | "schedule") {
|
||||||
console.log("Checking out new branch...")
|
console.log("Checking out new branch...")
|
||||||
const branch = generateBranchName("issue")
|
const branch = generateBranchName(type)
|
||||||
await $`git checkout -b ${branch}`
|
await $`git checkout -b ${branch}`
|
||||||
return branch
|
return branch
|
||||||
}
|
}
|
||||||
@@ -893,23 +943,32 @@ export const GithubRunCommand = cmd({
|
|||||||
await $`git checkout -b ${localBranch} fork/${remoteBranch}`
|
await $`git checkout -b ${localBranch} fork/${remoteBranch}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateBranchName(type: "issue" | "pr") {
|
function generateBranchName(type: "issue" | "pr" | "schedule") {
|
||||||
const timestamp = new Date()
|
const timestamp = new Date()
|
||||||
.toISOString()
|
.toISOString()
|
||||||
.replace(/[:-]/g, "")
|
.replace(/[:-]/g, "")
|
||||||
.replace(/\.\d{3}Z/, "")
|
.replace(/\.\d{3}Z/, "")
|
||||||
.split("T")
|
.split("T")
|
||||||
.join("")
|
.join("")
|
||||||
|
if (type === "schedule") {
|
||||||
|
const hex = crypto.randomUUID().slice(0, 6)
|
||||||
|
return `opencode/scheduled-${hex}-${timestamp}`
|
||||||
|
}
|
||||||
return `opencode/${type}${issueId}-${timestamp}`
|
return `opencode/${type}${issueId}-${timestamp}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pushToNewBranch(summary: string, branch: string, commit: boolean) {
|
async function pushToNewBranch(summary: string, branch: string, commit: boolean, isSchedule: boolean) {
|
||||||
console.log("Pushing to new branch...")
|
console.log("Pushing to new branch...")
|
||||||
if (commit) {
|
if (commit) {
|
||||||
await $`git add .`
|
await $`git add .`
|
||||||
await $`git commit -m "${summary}
|
if (isSchedule) {
|
||||||
|
// No co-author for scheduled events - the schedule is operating as the repo
|
||||||
|
await $`git commit -m "${summary}"`
|
||||||
|
} else {
|
||||||
|
await $`git commit -m "${summary}
|
||||||
|
|
||||||
Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
|
Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await $`git push -u origin ${branch}`
|
await $`git push -u origin ${branch}`
|
||||||
}
|
}
|
||||||
@@ -957,6 +1016,7 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function assertPermissions() {
|
async function assertPermissions() {
|
||||||
|
// Only called for non-schedule events, so actor is defined
|
||||||
console.log(`Asserting permissions for user ${actor}...`)
|
console.log(`Asserting permissions for user ${actor}...`)
|
||||||
|
|
||||||
let permission
|
let permission
|
||||||
@@ -964,7 +1024,7 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
|
|||||||
const response = await octoRest.repos.getCollaboratorPermissionLevel({
|
const response = await octoRest.repos.getCollaboratorPermissionLevel({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
username: actor,
|
username: actor!,
|
||||||
})
|
})
|
||||||
|
|
||||||
permission = response.data.permission
|
permission = response.data.permission
|
||||||
@@ -977,42 +1037,99 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
|
|||||||
if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`)
|
if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addReaction() {
|
async function addReaction(commentType?: "issue" | "pr_review") {
|
||||||
|
// Only called for non-schedule events, so triggerCommentId is defined
|
||||||
console.log("Adding reaction...")
|
console.log("Adding reaction...")
|
||||||
return await octoRest.rest.reactions.createForIssueComment({
|
if (triggerCommentId) {
|
||||||
|
if (commentType === "pr_review") {
|
||||||
|
return await octoRest.rest.reactions.createForPullRequestReviewComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: triggerCommentId!,
|
||||||
|
content: AGENT_REACTION,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return await octoRest.rest.reactions.createForIssueComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: triggerCommentId!,
|
||||||
|
content: AGENT_REACTION,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return await octoRest.rest.reactions.createForIssue({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
comment_id: triggerCommentId,
|
issue_number: issueId!,
|
||||||
content: AGENT_REACTION,
|
content: AGENT_REACTION,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeReaction() {
|
async function removeReaction(commentType?: "issue" | "pr_review") {
|
||||||
|
// Only called for non-schedule events, so triggerCommentId is defined
|
||||||
console.log("Removing reaction...")
|
console.log("Removing reaction...")
|
||||||
const reactions = await octoRest.rest.reactions.listForIssueComment({
|
if (triggerCommentId) {
|
||||||
|
if (commentType === "pr_review") {
|
||||||
|
const reactions = await octoRest.rest.reactions.listForPullRequestReviewComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: triggerCommentId!,
|
||||||
|
content: AGENT_REACTION,
|
||||||
|
})
|
||||||
|
|
||||||
|
const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME)
|
||||||
|
if (!eyesReaction) return
|
||||||
|
|
||||||
|
return await octoRest.rest.reactions.deleteForPullRequestComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: triggerCommentId!,
|
||||||
|
reaction_id: eyesReaction.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const reactions = await octoRest.rest.reactions.listForIssueComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: triggerCommentId!,
|
||||||
|
content: AGENT_REACTION,
|
||||||
|
})
|
||||||
|
|
||||||
|
const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME)
|
||||||
|
if (!eyesReaction) return
|
||||||
|
|
||||||
|
return await octoRest.rest.reactions.deleteForIssueComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: triggerCommentId!,
|
||||||
|
reaction_id: eyesReaction.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const reactions = await octoRest.rest.reactions.listForIssue({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
comment_id: triggerCommentId,
|
issue_number: issueId!,
|
||||||
content: AGENT_REACTION,
|
content: AGENT_REACTION,
|
||||||
})
|
})
|
||||||
|
|
||||||
const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME)
|
const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME)
|
||||||
if (!eyesReaction) return
|
if (!eyesReaction) return
|
||||||
|
|
||||||
await octoRest.rest.reactions.deleteForIssueComment({
|
await octoRest.rest.reactions.deleteForIssue({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
comment_id: triggerCommentId,
|
issue_number: issueId!,
|
||||||
reaction_id: eyesReaction.id,
|
reaction_id: eyesReaction.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createComment(body: string) {
|
async function createComment(body: string) {
|
||||||
|
// Only called for non-schedule events, so issueId is defined
|
||||||
console.log("Creating comment...")
|
console.log("Creating comment...")
|
||||||
return await octoRest.rest.issues.createComment({
|
return await octoRest.rest.issues.createComment({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
issue_number: issueId,
|
issue_number: issueId!,
|
||||||
body,
|
body,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1090,10 +1207,11 @@ query($owner: String!, $repo: String!, $number: Int!) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildPromptDataForIssue(issue: GitHubIssue) {
|
function buildPromptDataForIssue(issue: GitHubIssue) {
|
||||||
|
// Only called for non-schedule events, so payload is defined
|
||||||
const comments = (issue.comments?.nodes || [])
|
const comments = (issue.comments?.nodes || [])
|
||||||
.filter((c) => {
|
.filter((c) => {
|
||||||
const id = parseInt(c.databaseId)
|
const id = parseInt(c.databaseId)
|
||||||
return id !== payload.comment.id
|
return id !== triggerCommentId
|
||||||
})
|
})
|
||||||
.map((c) => ` - ${c.author.login} at ${c.createdAt}: ${c.body}`)
|
.map((c) => ` - ${c.author.login} at ${c.createdAt}: ${c.body}`)
|
||||||
|
|
||||||
@@ -1217,10 +1335,11 @@ query($owner: String!, $repo: String!, $number: Int!) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildPromptDataForPR(pr: GitHubPullRequest) {
|
function buildPromptDataForPR(pr: GitHubPullRequest) {
|
||||||
|
// Only called for non-schedule events, so payload is defined
|
||||||
const comments = (pr.comments?.nodes || [])
|
const comments = (pr.comments?.nodes || [])
|
||||||
.filter((c) => {
|
.filter((c) => {
|
||||||
const id = parseInt(c.databaseId)
|
const id = parseInt(c.databaseId)
|
||||||
return id !== payload.comment.id
|
return id !== triggerCommentId
|
||||||
})
|
})
|
||||||
.map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`)
|
.map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { select } from "@clack/prompts"
|
|||||||
import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2"
|
import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2"
|
||||||
import { Server } from "../../server/server"
|
import { Server } from "../../server/server"
|
||||||
import { Provider } from "../../provider/provider"
|
import { Provider } from "../../provider/provider"
|
||||||
|
import { Agent } from "../../agent/agent"
|
||||||
|
|
||||||
const TOOL: Record<string, [string, string]> = {
|
const TOOL: Record<string, [string, string]> = {
|
||||||
todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD],
|
todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD],
|
||||||
@@ -223,10 +224,33 @@ export const RunCommand = cmd({
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
// Validate agent if specified
|
||||||
|
const resolvedAgent = await (async () => {
|
||||||
|
if (!args.agent) return undefined
|
||||||
|
const agent = await Agent.get(args.agent)
|
||||||
|
if (!agent) {
|
||||||
|
UI.println(
|
||||||
|
UI.Style.TEXT_WARNING_BOLD + "!",
|
||||||
|
UI.Style.TEXT_NORMAL,
|
||||||
|
`agent "${args.agent}" not found. Falling back to default agent`,
|
||||||
|
)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
if (agent.mode === "subagent") {
|
||||||
|
UI.println(
|
||||||
|
UI.Style.TEXT_WARNING_BOLD + "!",
|
||||||
|
UI.Style.TEXT_NORMAL,
|
||||||
|
`agent "${args.agent}" is a subagent, not a primary agent. Falling back to default agent`,
|
||||||
|
)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return args.agent
|
||||||
|
})()
|
||||||
|
|
||||||
if (args.command) {
|
if (args.command) {
|
||||||
await sdk.session.command({
|
await sdk.session.command({
|
||||||
sessionID,
|
sessionID,
|
||||||
agent: args.agent || "build",
|
agent: resolvedAgent,
|
||||||
model: args.model,
|
model: args.model,
|
||||||
command: args.command,
|
command: args.command,
|
||||||
arguments: message,
|
arguments: message,
|
||||||
@@ -235,7 +259,7 @@ export const RunCommand = cmd({
|
|||||||
const modelParam = args.model ? Provider.parseModel(args.model) : undefined
|
const modelParam = args.model ? Provider.parseModel(args.model) : undefined
|
||||||
await sdk.session.prompt({
|
await sdk.session.prompt({
|
||||||
sessionID,
|
sessionID,
|
||||||
agent: args.agent || "build",
|
agent: resolvedAgent,
|
||||||
model: modelParam,
|
model: modelParam,
|
||||||
parts: [...fileParts, { type: "text", text: message }],
|
parts: [...fileParts, { type: "text", text: message }],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ function App() {
|
|||||||
const local = useLocal()
|
const local = useLocal()
|
||||||
const kv = useKV()
|
const kv = useKV()
|
||||||
const command = useCommandDialog()
|
const command = useCommandDialog()
|
||||||
const { event } = useSDK()
|
const sdk = useSDK()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const { theme, mode, setMode } = useTheme()
|
const { theme, mode, setMode } = useTheme()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
@@ -229,7 +229,8 @@ function App() {
|
|||||||
|
|
||||||
let continued = false
|
let continued = false
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (continued || sync.status !== "complete" || !args.continue) return
|
// When using -c, session list is loaded in blocking phase, so we can navigate at "partial"
|
||||||
|
if (continued || sync.status === "loading" || !args.continue) return
|
||||||
const match = sync.data.session
|
const match = sync.data.session
|
||||||
.toSorted((a, b) => b.time.updated - a.time.updated)
|
.toSorted((a, b) => b.time.updated - a.time.updated)
|
||||||
.find((x) => x.parentID === undefined)?.id
|
.find((x) => x.parentID === undefined)?.id
|
||||||
@@ -416,6 +417,15 @@ function App() {
|
|||||||
},
|
},
|
||||||
category: "System",
|
category: "System",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Open WebUI",
|
||||||
|
value: "webui.open",
|
||||||
|
onSelect: () => {
|
||||||
|
open(sdk.url).catch(() => {})
|
||||||
|
dialog.clear()
|
||||||
|
},
|
||||||
|
category: "System",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Exit the app",
|
title: "Exit the app",
|
||||||
value: "app.exit",
|
value: "app.exit",
|
||||||
@@ -486,11 +496,11 @@ function App() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
event.on(TuiEvent.CommandExecute.type, (evt) => {
|
sdk.event.on(TuiEvent.CommandExecute.type, (evt) => {
|
||||||
command.trigger(evt.properties.command)
|
command.trigger(evt.properties.command)
|
||||||
})
|
})
|
||||||
|
|
||||||
event.on(TuiEvent.ToastShow.type, (evt) => {
|
sdk.event.on(TuiEvent.ToastShow.type, (evt) => {
|
||||||
toast.show({
|
toast.show({
|
||||||
title: evt.properties.title,
|
title: evt.properties.title,
|
||||||
message: evt.properties.message,
|
message: evt.properties.message,
|
||||||
@@ -499,7 +509,7 @@ function App() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
event.on(SessionApi.Event.Deleted.type, (evt) => {
|
sdk.event.on(SessionApi.Event.Deleted.type, (evt) => {
|
||||||
if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) {
|
if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) {
|
||||||
route.navigate({ type: "home" })
|
route.navigate({ type: "home" })
|
||||||
toast.show({
|
toast.show({
|
||||||
@@ -509,7 +519,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
event.on(SessionApi.Event.Error.type, (evt) => {
|
sdk.event.on(SessionApi.Event.Error.type, (evt) => {
|
||||||
const error = evt.properties.error
|
const error = evt.properties.error
|
||||||
const message = (() => {
|
const message = (() => {
|
||||||
if (!error) return "An error occured"
|
if (!error) return "An error occured"
|
||||||
@@ -530,7 +540,7 @@ function App() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
event.on(Installation.Event.Updated.type, (evt) => {
|
sdk.event.on(Installation.Event.Updated.type, (evt) => {
|
||||||
toast.show({
|
toast.show({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: "Update Complete",
|
title: "Update Complete",
|
||||||
@@ -539,7 +549,7 @@ function App() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
event.on(Installation.Event.UpdateAvailable.type, (evt) => {
|
sdk.event.on(Installation.Event.UpdateAvailable.type, (evt) => {
|
||||||
toast.show({
|
toast.show({
|
||||||
variant: "info",
|
variant: "info",
|
||||||
title: "Update Available",
|
title: "Update Available",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { BoxRenderable, TextareaRenderable, KeyEvent } from "@opentui/core"
|
import type { BoxRenderable, TextareaRenderable, KeyEvent, ScrollBoxRenderable } from "@opentui/core"
|
||||||
import fuzzysort from "fuzzysort"
|
import fuzzysort from "fuzzysort"
|
||||||
import { firstBy } from "remeda"
|
import { firstBy } from "remeda"
|
||||||
import { createMemo, createResource, createEffect, onMount, onCleanup, For, Show, createSignal } from "solid-js"
|
import { createMemo, createResource, createEffect, onMount, onCleanup, For, Show, createSignal } from "solid-js"
|
||||||
@@ -369,7 +369,7 @@ export function Autocomplete(props: {
|
|||||||
store.visible === "@" ? [...agents(), ...(files() || [])] : [...commands()]
|
store.visible === "@" ? [...agents(), ...(files() || [])] : [...commands()]
|
||||||
).filter((x) => x.disabled !== true)
|
).filter((x) => x.disabled !== true)
|
||||||
const currentFilter = filter()
|
const currentFilter = filter()
|
||||||
if (!currentFilter) return mixed.slice(0, 10)
|
if (!currentFilter) return mixed
|
||||||
const result = fuzzysort.go(currentFilter, mixed, {
|
const result = fuzzysort.go(currentFilter, mixed, {
|
||||||
keys: [(obj) => obj.display.trimEnd(), "description", (obj) => obj.aliases?.join(" ") ?? ""],
|
keys: [(obj) => obj.display.trimEnd(), "description", (obj) => obj.aliases?.join(" ") ?? ""],
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@@ -395,7 +395,19 @@ export function Autocomplete(props: {
|
|||||||
let next = store.selected + direction
|
let next = store.selected + direction
|
||||||
if (next < 0) next = options().length - 1
|
if (next < 0) next = options().length - 1
|
||||||
if (next >= options().length) next = 0
|
if (next >= options().length) next = 0
|
||||||
|
moveTo(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveTo(next: number) {
|
||||||
setStore("selected", next)
|
setStore("selected", next)
|
||||||
|
if (!scroll) return
|
||||||
|
const viewportHeight = Math.min(height(), options().length)
|
||||||
|
const scrollBottom = scroll.scrollTop + viewportHeight
|
||||||
|
if (next < scroll.scrollTop) {
|
||||||
|
scroll.scrollBy(next - scroll.scrollTop)
|
||||||
|
} else if (next + 1 > scrollBottom) {
|
||||||
|
scroll.scrollBy(next + 1 - scrollBottom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function select() {
|
function select() {
|
||||||
@@ -497,6 +509,8 @@ export function Autocomplete(props: {
|
|||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let scroll: ScrollBoxRenderable
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box
|
<box
|
||||||
visible={store.visible !== false}
|
visible={store.visible !== false}
|
||||||
@@ -508,7 +522,12 @@ export function Autocomplete(props: {
|
|||||||
{...SplitBorder}
|
{...SplitBorder}
|
||||||
borderColor={theme.border}
|
borderColor={theme.border}
|
||||||
>
|
>
|
||||||
<box backgroundColor={theme.backgroundMenu} height={height()}>
|
<scrollbox
|
||||||
|
ref={(r: ScrollBoxRenderable) => (scroll = r)}
|
||||||
|
backgroundColor={theme.backgroundMenu}
|
||||||
|
height={height()}
|
||||||
|
scrollbarOptions={{ visible: false }}
|
||||||
|
>
|
||||||
<For
|
<For
|
||||||
each={options()}
|
each={options()}
|
||||||
fallback={
|
fallback={
|
||||||
@@ -535,7 +554,7 @@ export function Autocomplete(props: {
|
|||||||
</box>
|
</box>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</box>
|
</scrollbox>
|
||||||
</box>
|
</box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -731,6 +731,23 @@ export function Prompt(props: PromptProps) {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Handle clipboard paste (Ctrl+V) - check for images first on Windows
|
||||||
|
// This is needed because Windows terminal doesn't properly send image data
|
||||||
|
// through bracketed paste, so we need to intercept the keypress and
|
||||||
|
// directly read from clipboard before the terminal handles it
|
||||||
|
if (keybind.match("input_paste", e)) {
|
||||||
|
const content = await Clipboard.read()
|
||||||
|
if (content?.mime.startsWith("image/")) {
|
||||||
|
e.preventDefault()
|
||||||
|
await pasteImage({
|
||||||
|
filename: "clipboard",
|
||||||
|
mime: content.mime,
|
||||||
|
content: content.data,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If no image, let the default paste behavior continue
|
||||||
|
}
|
||||||
if (keybind.match("input_clear", e) && store.prompt.input !== "") {
|
if (keybind.match("input_clear", e) && store.prompt.input !== "") {
|
||||||
input.clear()
|
input.clear()
|
||||||
input.extmarks.clear()
|
input.extmarks.clear()
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
const [agentStore, setAgentStore] = createStore<{
|
const [agentStore, setAgentStore] = createStore<{
|
||||||
current: string
|
current: string
|
||||||
}>({
|
}>({
|
||||||
current: agents()[0].name,
|
current: agents().find((x) => x.default)?.name ?? agents()[0].name,
|
||||||
})
|
})
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const colors = createMemo(() => [
|
const colors = createMemo(() => [
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export type HomeRoute = {
|
|||||||
export type SessionRoute = {
|
export type SessionRoute = {
|
||||||
type: "session"
|
type: "session"
|
||||||
sessionID: string
|
sessionID: string
|
||||||
|
initialPrompt?: PromptInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Route = HomeRoute | SessionRoute
|
export type Route = HomeRoute | SessionRoute
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2"
|
|||||||
import { createSimpleContext } from "./helper"
|
import { createSimpleContext } from "./helper"
|
||||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
import { batch, onCleanup, onMount } from "solid-js"
|
import { batch, onCleanup, onMount } from "solid-js"
|
||||||
import { iife } from "@/util/iife"
|
|
||||||
|
|
||||||
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||||
name: "SDK",
|
name: "SDK",
|
||||||
@@ -70,6 +69,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||||||
abort.abort()
|
abort.abort()
|
||||||
})
|
})
|
||||||
|
|
||||||
return { client: sdk, event: emitter }
|
return { client: sdk, event: emitter, url: props.url }
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { Binary } from "@opencode-ai/util/binary"
|
|||||||
import { createSimpleContext } from "./helper"
|
import { createSimpleContext } from "./helper"
|
||||||
import type { Snapshot } from "@/snapshot"
|
import type { Snapshot } from "@/snapshot"
|
||||||
import { useExit } from "./exit"
|
import { useExit } from "./exit"
|
||||||
|
import { useArgs } from "./args"
|
||||||
import { batch, onMount } from "solid-js"
|
import { batch, onMount } from "solid-js"
|
||||||
import { Log } from "@/util/log"
|
import { Log } from "@/util/log"
|
||||||
import type { Path } from "@opencode-ai/sdk"
|
import type { Path } from "@opencode-ai/sdk"
|
||||||
@@ -254,10 +255,18 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const exit = useExit()
|
const exit = useExit()
|
||||||
|
const args = useArgs()
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
// blocking
|
const sessionListPromise = sdk.client.session.list().then((x) =>
|
||||||
await Promise.all([
|
setStore(
|
||||||
|
"session",
|
||||||
|
(x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// blocking - include session.list when continuing a session
|
||||||
|
const blockingRequests: Promise<unknown>[] = [
|
||||||
sdk.client.config.providers({}, { throwOnError: true }).then((x) => {
|
sdk.client.config.providers({}, { throwOnError: true }).then((x) => {
|
||||||
batch(() => {
|
batch(() => {
|
||||||
setStore("provider", x.data!.providers)
|
setStore("provider", x.data!.providers)
|
||||||
@@ -271,17 +280,15 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
}),
|
}),
|
||||||
sdk.client.app.agents({}, { throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
|
sdk.client.app.agents({}, { throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
|
||||||
sdk.client.config.get({}, { throwOnError: true }).then((x) => setStore("config", x.data!)),
|
sdk.client.config.get({}, { throwOnError: true }).then((x) => setStore("config", x.data!)),
|
||||||
])
|
...(args.continue ? [sessionListPromise] : []),
|
||||||
|
]
|
||||||
|
|
||||||
|
await Promise.all(blockingRequests)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (store.status !== "complete") setStore("status", "partial")
|
if (store.status !== "complete") setStore("status", "partial")
|
||||||
// non-blocking
|
// non-blocking
|
||||||
Promise.all([
|
Promise.all([
|
||||||
sdk.client.session.list().then((x) =>
|
...(args.continue ? [] : [sessionListPromise]),
|
||||||
setStore(
|
|
||||||
"session",
|
|
||||||
(x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
sdk.client.command.list().then((x) => setStore("command", x.data ?? [])),
|
sdk.client.command.list().then((x) => setStore("command", x.data ?? [])),
|
||||||
sdk.client.lsp.status().then((x) => setStore("lsp", x.data!)),
|
sdk.client.lsp.status().then((x) => setStore("lsp", x.data!)),
|
||||||
sdk.client.mcp.status().then((x) => setStore("mcp", x.data!)),
|
sdk.client.mcp.status().then((x) => setStore("mcp", x.data!)),
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import { createSimpleContext } from "./helper"
|
|||||||
import aura from "./theme/aura.json" with { type: "json" }
|
import aura from "./theme/aura.json" with { type: "json" }
|
||||||
import ayu from "./theme/ayu.json" with { type: "json" }
|
import ayu from "./theme/ayu.json" with { type: "json" }
|
||||||
import catppuccin from "./theme/catppuccin.json" with { type: "json" }
|
import catppuccin from "./theme/catppuccin.json" with { type: "json" }
|
||||||
|
import catppuccinFrappe from "./theme/catppuccin-frappe.json" with { type: "json" }
|
||||||
import catppuccinMacchiato from "./theme/catppuccin-macchiato.json" with { type: "json" }
|
import catppuccinMacchiato from "./theme/catppuccin-macchiato.json" with { type: "json" }
|
||||||
import cobalt2 from "./theme/cobalt2.json" with { type: "json" }
|
import cobalt2 from "./theme/cobalt2.json" with { type: "json" }
|
||||||
|
import cursor from "./theme/cursor.json" with { type: "json" }
|
||||||
import dracula from "./theme/dracula.json" with { type: "json" }
|
import dracula from "./theme/dracula.json" with { type: "json" }
|
||||||
import everforest from "./theme/everforest.json" with { type: "json" }
|
import everforest from "./theme/everforest.json" with { type: "json" }
|
||||||
import flexoki from "./theme/flexoki.json" with { type: "json" }
|
import flexoki from "./theme/flexoki.json" with { type: "json" }
|
||||||
@@ -136,8 +138,10 @@ export const DEFAULT_THEMES: Record<string, ThemeJson> = {
|
|||||||
aura,
|
aura,
|
||||||
ayu,
|
ayu,
|
||||||
catppuccin,
|
catppuccin,
|
||||||
|
["catppuccin-frappe"]: catppuccinFrappe,
|
||||||
["catppuccin-macchiato"]: catppuccinMacchiato,
|
["catppuccin-macchiato"]: catppuccinMacchiato,
|
||||||
cobalt2,
|
cobalt2,
|
||||||
|
cursor,
|
||||||
dracula,
|
dracula,
|
||||||
everforest,
|
everforest,
|
||||||
flexoki,
|
flexoki,
|
||||||
@@ -279,14 +283,23 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
|
|||||||
ready: false,
|
ready: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(async () => {
|
createEffect(() => {
|
||||||
const custom = await getCustomThemes()
|
getCustomThemes()
|
||||||
setStore(
|
.then((custom) => {
|
||||||
produce((draft) => {
|
setStore(
|
||||||
Object.assign(draft.themes, custom)
|
produce((draft) => {
|
||||||
draft.ready = true
|
Object.assign(draft.themes, custom)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setStore("active", "opencode")
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (store.active !== "system") {
|
||||||
|
setStore("ready", true)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const renderer = useRenderer()
|
const renderer = useRenderer()
|
||||||
@@ -295,8 +308,25 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
|
|||||||
size: 16,
|
size: 16,
|
||||||
})
|
})
|
||||||
.then((colors) => {
|
.then((colors) => {
|
||||||
if (!colors.palette[0]) return
|
if (!colors.palette[0]) {
|
||||||
setStore("themes", "system", generateSystem(colors, store.mode))
|
if (store.active === "system") {
|
||||||
|
setStore(
|
||||||
|
produce((draft) => {
|
||||||
|
draft.active = "opencode"
|
||||||
|
draft.ready = true
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setStore(
|
||||||
|
produce((draft) => {
|
||||||
|
draft.themes.system = generateSystem(colors, store.mode)
|
||||||
|
if (store.active === "system") {
|
||||||
|
draft.ready = true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const values = createMemo(() => {
|
const values = createMemo(() => {
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/theme.json",
|
||||||
|
"defs": {
|
||||||
|
"frappeRosewater": "#f2d5cf",
|
||||||
|
"frappeFlamingo": "#eebebe",
|
||||||
|
"frappePink": "#f4b8e4",
|
||||||
|
"frappeMauve": "#ca9ee6",
|
||||||
|
"frappeRed": "#e78284",
|
||||||
|
"frappeMaroon": "#ea999c",
|
||||||
|
"frappePeach": "#ef9f76",
|
||||||
|
"frappeYellow": "#e5c890",
|
||||||
|
"frappeGreen": "#a6d189",
|
||||||
|
"frappeTeal": "#81c8be",
|
||||||
|
"frappeSky": "#99d1db",
|
||||||
|
"frappeSapphire": "#85c1dc",
|
||||||
|
"frappeBlue": "#8da4e2",
|
||||||
|
"frappeLavender": "#babbf1",
|
||||||
|
"frappeText": "#c6d0f5",
|
||||||
|
"frappeSubtext1": "#b5bfe2",
|
||||||
|
"frappeSubtext0": "#a5adce",
|
||||||
|
"frappeOverlay2": "#949cb8",
|
||||||
|
"frappeOverlay1": "#838ba7",
|
||||||
|
"frappeOverlay0": "#737994",
|
||||||
|
"frappeSurface2": "#626880",
|
||||||
|
"frappeSurface1": "#51576d",
|
||||||
|
"frappeSurface0": "#414559",
|
||||||
|
"frappeBase": "#303446",
|
||||||
|
"frappeMantle": "#292c3c",
|
||||||
|
"frappeCrust": "#232634"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"primary": {
|
||||||
|
"dark": "frappeBlue",
|
||||||
|
"light": "frappeBlue"
|
||||||
|
},
|
||||||
|
"secondary": {
|
||||||
|
"dark": "frappeMauve",
|
||||||
|
"light": "frappeMauve"
|
||||||
|
},
|
||||||
|
"accent": {
|
||||||
|
"dark": "frappePink",
|
||||||
|
"light": "frappePink"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"dark": "frappeRed",
|
||||||
|
"light": "frappeRed"
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"dark": "frappeYellow",
|
||||||
|
"light": "frappeYellow"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"dark": "frappeGreen",
|
||||||
|
"light": "frappeGreen"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"dark": "frappeTeal",
|
||||||
|
"light": "frappeTeal"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"dark": "frappeText",
|
||||||
|
"light": "frappeText"
|
||||||
|
},
|
||||||
|
"textMuted": {
|
||||||
|
"dark": "frappeSubtext1",
|
||||||
|
"light": "frappeSubtext1"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"dark": "frappeBase",
|
||||||
|
"light": "frappeBase"
|
||||||
|
},
|
||||||
|
"backgroundPanel": {
|
||||||
|
"dark": "frappeMantle",
|
||||||
|
"light": "frappeMantle"
|
||||||
|
},
|
||||||
|
"backgroundElement": {
|
||||||
|
"dark": "frappeCrust",
|
||||||
|
"light": "frappeCrust"
|
||||||
|
},
|
||||||
|
"border": {
|
||||||
|
"dark": "frappeSurface0",
|
||||||
|
"light": "frappeSurface0"
|
||||||
|
},
|
||||||
|
"borderActive": {
|
||||||
|
"dark": "frappeSurface1",
|
||||||
|
"light": "frappeSurface1"
|
||||||
|
},
|
||||||
|
"borderSubtle": {
|
||||||
|
"dark": "frappeSurface2",
|
||||||
|
"light": "frappeSurface2"
|
||||||
|
},
|
||||||
|
"diffAdded": {
|
||||||
|
"dark": "frappeGreen",
|
||||||
|
"light": "frappeGreen"
|
||||||
|
},
|
||||||
|
"diffRemoved": {
|
||||||
|
"dark": "frappeRed",
|
||||||
|
"light": "frappeRed"
|
||||||
|
},
|
||||||
|
"diffContext": {
|
||||||
|
"dark": "frappeOverlay2",
|
||||||
|
"light": "frappeOverlay2"
|
||||||
|
},
|
||||||
|
"diffHunkHeader": {
|
||||||
|
"dark": "frappePeach",
|
||||||
|
"light": "frappePeach"
|
||||||
|
},
|
||||||
|
"diffHighlightAdded": {
|
||||||
|
"dark": "frappeGreen",
|
||||||
|
"light": "frappeGreen"
|
||||||
|
},
|
||||||
|
"diffHighlightRemoved": {
|
||||||
|
"dark": "frappeRed",
|
||||||
|
"light": "frappeRed"
|
||||||
|
},
|
||||||
|
"diffAddedBg": {
|
||||||
|
"dark": "#29342b",
|
||||||
|
"light": "#29342b"
|
||||||
|
},
|
||||||
|
"diffRemovedBg": {
|
||||||
|
"dark": "#3a2a31",
|
||||||
|
"light": "#3a2a31"
|
||||||
|
},
|
||||||
|
"diffContextBg": {
|
||||||
|
"dark": "frappeMantle",
|
||||||
|
"light": "frappeMantle"
|
||||||
|
},
|
||||||
|
"diffLineNumber": {
|
||||||
|
"dark": "frappeSurface1",
|
||||||
|
"light": "frappeSurface1"
|
||||||
|
},
|
||||||
|
"diffAddedLineNumberBg": {
|
||||||
|
"dark": "#223025",
|
||||||
|
"light": "#223025"
|
||||||
|
},
|
||||||
|
"diffRemovedLineNumberBg": {
|
||||||
|
"dark": "#2f242b",
|
||||||
|
"light": "#2f242b"
|
||||||
|
},
|
||||||
|
"markdownText": {
|
||||||
|
"dark": "frappeText",
|
||||||
|
"light": "frappeText"
|
||||||
|
},
|
||||||
|
"markdownHeading": {
|
||||||
|
"dark": "frappeMauve",
|
||||||
|
"light": "frappeMauve"
|
||||||
|
},
|
||||||
|
"markdownLink": {
|
||||||
|
"dark": "frappeBlue",
|
||||||
|
"light": "frappeBlue"
|
||||||
|
},
|
||||||
|
"markdownLinkText": {
|
||||||
|
"dark": "frappeSky",
|
||||||
|
"light": "frappeSky"
|
||||||
|
},
|
||||||
|
"markdownCode": {
|
||||||
|
"dark": "frappeGreen",
|
||||||
|
"light": "frappeGreen"
|
||||||
|
},
|
||||||
|
"markdownBlockQuote": {
|
||||||
|
"dark": "frappeYellow",
|
||||||
|
"light": "frappeYellow"
|
||||||
|
},
|
||||||
|
"markdownEmph": {
|
||||||
|
"dark": "frappeYellow",
|
||||||
|
"light": "frappeYellow"
|
||||||
|
},
|
||||||
|
"markdownStrong": {
|
||||||
|
"dark": "frappePeach",
|
||||||
|
"light": "frappePeach"
|
||||||
|
},
|
||||||
|
"markdownHorizontalRule": {
|
||||||
|
"dark": "frappeSubtext0",
|
||||||
|
"light": "frappeSubtext0"
|
||||||
|
},
|
||||||
|
"markdownListItem": {
|
||||||
|
"dark": "frappeBlue",
|
||||||
|
"light": "frappeBlue"
|
||||||
|
},
|
||||||
|
"markdownListEnumeration": {
|
||||||
|
"dark": "frappeSky",
|
||||||
|
"light": "frappeSky"
|
||||||
|
},
|
||||||
|
"markdownImage": {
|
||||||
|
"dark": "frappeBlue",
|
||||||
|
"light": "frappeBlue"
|
||||||
|
},
|
||||||
|
"markdownImageText": {
|
||||||
|
"dark": "frappeSky",
|
||||||
|
"light": "frappeSky"
|
||||||
|
},
|
||||||
|
"markdownCodeBlock": {
|
||||||
|
"dark": "frappeText",
|
||||||
|
"light": "frappeText"
|
||||||
|
},
|
||||||
|
"syntaxComment": {
|
||||||
|
"dark": "frappeOverlay2",
|
||||||
|
"light": "frappeOverlay2"
|
||||||
|
},
|
||||||
|
"syntaxKeyword": {
|
||||||
|
"dark": "frappeMauve",
|
||||||
|
"light": "frappeMauve"
|
||||||
|
},
|
||||||
|
"syntaxFunction": {
|
||||||
|
"dark": "frappeBlue",
|
||||||
|
"light": "frappeBlue"
|
||||||
|
},
|
||||||
|
"syntaxVariable": {
|
||||||
|
"dark": "frappeRed",
|
||||||
|
"light": "frappeRed"
|
||||||
|
},
|
||||||
|
"syntaxString": {
|
||||||
|
"dark": "frappeGreen",
|
||||||
|
"light": "frappeGreen"
|
||||||
|
},
|
||||||
|
"syntaxNumber": {
|
||||||
|
"dark": "frappePeach",
|
||||||
|
"light": "frappePeach"
|
||||||
|
},
|
||||||
|
"syntaxType": {
|
||||||
|
"dark": "frappeYellow",
|
||||||
|
"light": "frappeYellow"
|
||||||
|
},
|
||||||
|
"syntaxOperator": {
|
||||||
|
"dark": "frappeSky",
|
||||||
|
"light": "frappeSky"
|
||||||
|
},
|
||||||
|
"syntaxPunctuation": {
|
||||||
|
"dark": "frappeText",
|
||||||
|
"light": "frappeText"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/theme.json",
|
||||||
|
"defs": {
|
||||||
|
"darkBg": "#181818",
|
||||||
|
"darkPanel": "#141414",
|
||||||
|
"darkElement": "#262626",
|
||||||
|
"darkFg": "#e4e4e4",
|
||||||
|
"darkMuted": "#e4e4e45e",
|
||||||
|
"darkBorder": "#e4e4e413",
|
||||||
|
"darkBorderActive": "#e4e4e426",
|
||||||
|
"darkCyan": "#88c0d0",
|
||||||
|
"darkBlue": "#81a1c1",
|
||||||
|
"darkGreen": "#3fa266",
|
||||||
|
"darkGreenBright": "#70b489",
|
||||||
|
"darkRed": "#e34671",
|
||||||
|
"darkRedBright": "#fc6b83",
|
||||||
|
"darkYellow": "#f1b467",
|
||||||
|
"darkOrange": "#d2943e",
|
||||||
|
"darkPink": "#E394DC",
|
||||||
|
"darkPurple": "#AAA0FA",
|
||||||
|
"darkTeal": "#82D2CE",
|
||||||
|
"darkSyntaxYellow": "#F8C762",
|
||||||
|
"darkSyntaxOrange": "#EFB080",
|
||||||
|
"darkSyntaxGreen": "#A8CC7C",
|
||||||
|
"darkSyntaxBlue": "#87C3FF",
|
||||||
|
"lightBg": "#fcfcfc",
|
||||||
|
"lightPanel": "#f3f3f3",
|
||||||
|
"lightElement": "#ededed",
|
||||||
|
"lightFg": "#141414",
|
||||||
|
"lightMuted": "#141414ad",
|
||||||
|
"lightBorder": "#14141413",
|
||||||
|
"lightBorderActive": "#14141426",
|
||||||
|
"lightTeal": "#6f9ba6",
|
||||||
|
"lightBlue": "#3c7cab",
|
||||||
|
"lightBlueDark": "#206595",
|
||||||
|
"lightGreen": "#1f8a65",
|
||||||
|
"lightGreenBright": "#55a583",
|
||||||
|
"lightRed": "#cf2d56",
|
||||||
|
"lightRedBright": "#e75e78",
|
||||||
|
"lightOrange": "#db704b",
|
||||||
|
"lightYellow": "#c08532",
|
||||||
|
"lightPurple": "#9e94d5",
|
||||||
|
"lightPurpleDark": "#6049b3",
|
||||||
|
"lightPink": "#b8448b",
|
||||||
|
"lightMagenta": "#b3003f"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"primary": {
|
||||||
|
"dark": "darkCyan",
|
||||||
|
"light": "lightTeal"
|
||||||
|
},
|
||||||
|
"secondary": {
|
||||||
|
"dark": "darkBlue",
|
||||||
|
"light": "lightBlue"
|
||||||
|
},
|
||||||
|
"accent": {
|
||||||
|
"dark": "darkCyan",
|
||||||
|
"light": "lightTeal"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"dark": "darkRed",
|
||||||
|
"light": "lightRed"
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"dark": "darkYellow",
|
||||||
|
"light": "lightOrange"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"dark": "darkGreen",
|
||||||
|
"light": "lightGreen"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"dark": "darkBlue",
|
||||||
|
"light": "lightBlue"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"dark": "darkFg",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"textMuted": {
|
||||||
|
"dark": "darkMuted",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"dark": "darkBg",
|
||||||
|
"light": "lightBg"
|
||||||
|
},
|
||||||
|
"backgroundPanel": {
|
||||||
|
"dark": "darkPanel",
|
||||||
|
"light": "lightPanel"
|
||||||
|
},
|
||||||
|
"backgroundElement": {
|
||||||
|
"dark": "darkElement",
|
||||||
|
"light": "lightElement"
|
||||||
|
},
|
||||||
|
"border": {
|
||||||
|
"dark": "darkBorder",
|
||||||
|
"light": "lightBorder"
|
||||||
|
},
|
||||||
|
"borderActive": {
|
||||||
|
"dark": "darkCyan",
|
||||||
|
"light": "lightTeal"
|
||||||
|
},
|
||||||
|
"borderSubtle": {
|
||||||
|
"dark": "#0f0f0f",
|
||||||
|
"light": "#e0e0e0"
|
||||||
|
},
|
||||||
|
"diffAdded": {
|
||||||
|
"dark": "darkGreen",
|
||||||
|
"light": "lightGreen"
|
||||||
|
},
|
||||||
|
"diffRemoved": {
|
||||||
|
"dark": "darkRed",
|
||||||
|
"light": "lightRed"
|
||||||
|
},
|
||||||
|
"diffContext": {
|
||||||
|
"dark": "darkMuted",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"diffHunkHeader": {
|
||||||
|
"dark": "darkMuted",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"diffHighlightAdded": {
|
||||||
|
"dark": "darkGreenBright",
|
||||||
|
"light": "lightGreenBright"
|
||||||
|
},
|
||||||
|
"diffHighlightRemoved": {
|
||||||
|
"dark": "darkRedBright",
|
||||||
|
"light": "lightRedBright"
|
||||||
|
},
|
||||||
|
"diffAddedBg": {
|
||||||
|
"dark": "#3fa26633",
|
||||||
|
"light": "#1f8a651f"
|
||||||
|
},
|
||||||
|
"diffRemovedBg": {
|
||||||
|
"dark": "#b8004933",
|
||||||
|
"light": "#cf2d5614"
|
||||||
|
},
|
||||||
|
"diffContextBg": {
|
||||||
|
"dark": "darkPanel",
|
||||||
|
"light": "lightPanel"
|
||||||
|
},
|
||||||
|
"diffLineNumber": {
|
||||||
|
"dark": "#e4e4e442",
|
||||||
|
"light": "#1414147a"
|
||||||
|
},
|
||||||
|
"diffAddedLineNumberBg": {
|
||||||
|
"dark": "#3fa26633",
|
||||||
|
"light": "#1f8a651f"
|
||||||
|
},
|
||||||
|
"diffRemovedLineNumberBg": {
|
||||||
|
"dark": "#b8004933",
|
||||||
|
"light": "#cf2d5614"
|
||||||
|
},
|
||||||
|
"markdownText": {
|
||||||
|
"dark": "darkFg",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"markdownHeading": {
|
||||||
|
"dark": "darkPurple",
|
||||||
|
"light": "lightBlueDark"
|
||||||
|
},
|
||||||
|
"markdownLink": {
|
||||||
|
"dark": "darkTeal",
|
||||||
|
"light": "lightBlueDark"
|
||||||
|
},
|
||||||
|
"markdownLinkText": {
|
||||||
|
"dark": "darkBlue",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"markdownCode": {
|
||||||
|
"dark": "darkPink",
|
||||||
|
"light": "lightGreen"
|
||||||
|
},
|
||||||
|
"markdownBlockQuote": {
|
||||||
|
"dark": "darkMuted",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"markdownEmph": {
|
||||||
|
"dark": "darkTeal",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"markdownStrong": {
|
||||||
|
"dark": "darkSyntaxYellow",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"markdownHorizontalRule": {
|
||||||
|
"dark": "darkMuted",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"markdownListItem": {
|
||||||
|
"dark": "darkFg",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"markdownListEnumeration": {
|
||||||
|
"dark": "darkCyan",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"markdownImage": {
|
||||||
|
"dark": "darkCyan",
|
||||||
|
"light": "lightBlueDark"
|
||||||
|
},
|
||||||
|
"markdownImageText": {
|
||||||
|
"dark": "darkBlue",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"markdownCodeBlock": {
|
||||||
|
"dark": "darkFg",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"syntaxComment": {
|
||||||
|
"dark": "darkMuted",
|
||||||
|
"light": "lightMuted"
|
||||||
|
},
|
||||||
|
"syntaxKeyword": {
|
||||||
|
"dark": "darkTeal",
|
||||||
|
"light": "lightMagenta"
|
||||||
|
},
|
||||||
|
"syntaxFunction": {
|
||||||
|
"dark": "darkSyntaxOrange",
|
||||||
|
"light": "lightOrange"
|
||||||
|
},
|
||||||
|
"syntaxVariable": {
|
||||||
|
"dark": "darkFg",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"syntaxString": {
|
||||||
|
"dark": "darkPink",
|
||||||
|
"light": "lightPurple"
|
||||||
|
},
|
||||||
|
"syntaxNumber": {
|
||||||
|
"dark": "darkSyntaxYellow",
|
||||||
|
"light": "lightPink"
|
||||||
|
},
|
||||||
|
"syntaxType": {
|
||||||
|
"dark": "darkSyntaxOrange",
|
||||||
|
"light": "lightBlueDark"
|
||||||
|
},
|
||||||
|
"syntaxOperator": {
|
||||||
|
"dark": "darkFg",
|
||||||
|
"light": "lightFg"
|
||||||
|
},
|
||||||
|
"syntaxPunctuation": {
|
||||||
|
"dark": "darkFg",
|
||||||
|
"light": "lightFg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { Locale } from "@/util/locale"
|
|||||||
import { useSDK } from "@tui/context/sdk"
|
import { useSDK } from "@tui/context/sdk"
|
||||||
import { useRoute } from "@tui/context/route"
|
import { useRoute } from "@tui/context/route"
|
||||||
import { useDialog } from "../../ui/dialog"
|
import { useDialog } from "../../ui/dialog"
|
||||||
|
import type { PromptInfo } from "@tui/component/prompt/history"
|
||||||
|
|
||||||
export function DialogForkFromTimeline(props: { sessionID: string; onMove: (messageID: string) => void }) {
|
export function DialogForkFromTimeline(props: { sessionID: string; onMove: (messageID: string) => void }) {
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
@@ -35,9 +36,21 @@ export function DialogForkFromTimeline(props: { sessionID: string; onMove: (mess
|
|||||||
sessionID: props.sessionID,
|
sessionID: props.sessionID,
|
||||||
messageID: message.id,
|
messageID: message.id,
|
||||||
})
|
})
|
||||||
|
const parts = sync.data.part[message.id] ?? []
|
||||||
|
const initialPrompt = parts.reduce(
|
||||||
|
(agg, part) => {
|
||||||
|
if (part.type === "text") {
|
||||||
|
if (!part.synthetic) agg.input += part.text
|
||||||
|
}
|
||||||
|
if (part.type === "file") agg.parts.push(part)
|
||||||
|
return agg
|
||||||
|
},
|
||||||
|
{ input: "", parts: [] as PromptInfo["parts"] },
|
||||||
|
)
|
||||||
route.navigate({
|
route.navigate({
|
||||||
sessionID: forked.data!.id,
|
sessionID: forked.data!.id,
|
||||||
type: "session",
|
type: "session",
|
||||||
|
initialPrompt,
|
||||||
})
|
})
|
||||||
dialog.clear()
|
dialog.clear()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function DialogMessage(props: {
|
|||||||
{
|
{
|
||||||
title: "Copy",
|
title: "Copy",
|
||||||
value: "message.copy",
|
value: "message.copy",
|
||||||
description: "copy message text to clipboard",
|
description: "message text to clipboard",
|
||||||
onSelect: async (dialog) => {
|
onSelect: async (dialog) => {
|
||||||
const msg = message()
|
const msg = message()
|
||||||
if (!msg) return
|
if (!msg) return
|
||||||
@@ -80,9 +80,25 @@ export function DialogMessage(props: {
|
|||||||
sessionID: props.sessionID,
|
sessionID: props.sessionID,
|
||||||
messageID: props.messageID,
|
messageID: props.messageID,
|
||||||
})
|
})
|
||||||
|
const initialPrompt = (() => {
|
||||||
|
const msg = message()
|
||||||
|
if (!msg) return undefined
|
||||||
|
const parts = sync.data.part[msg.id]
|
||||||
|
return parts.reduce(
|
||||||
|
(agg, part) => {
|
||||||
|
if (part.type === "text") {
|
||||||
|
if (!part.synthetic) agg.input += part.text
|
||||||
|
}
|
||||||
|
if (part.type === "file") agg.parts.push(part)
|
||||||
|
return agg
|
||||||
|
},
|
||||||
|
{ input: "", parts: [] as PromptInfo["parts"] },
|
||||||
|
)
|
||||||
|
})()
|
||||||
route.navigate({
|
route.navigate({
|
||||||
sessionID: result.data!.id,
|
sessionID: result.data!.id,
|
||||||
type: "session",
|
type: "session",
|
||||||
|
initialPrompt,
|
||||||
})
|
})
|
||||||
dialog.clear()
|
dialog.clear()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { DialogSelect } from "@tui/ui/dialog-select"
|
||||||
|
import { useRoute } from "@tui/context/route"
|
||||||
|
|
||||||
|
export function DialogSubagent(props: { sessionID: string }) {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogSelect
|
||||||
|
title="Subagent Actions"
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
title: "Open",
|
||||||
|
value: "subagent.view",
|
||||||
|
description: "the subagent's session",
|
||||||
|
onSelect: (dialog) => {
|
||||||
|
route.navigate({
|
||||||
|
type: "session",
|
||||||
|
sessionID: props.sessionID,
|
||||||
|
})
|
||||||
|
dialog.clear()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -81,6 +81,9 @@ export function Header() {
|
|||||||
<text fg={theme.text}>
|
<text fg={theme.text}>
|
||||||
<b>Subagent session</b>
|
<b>Subagent session</b>
|
||||||
</text>
|
</text>
|
||||||
|
<text fg={theme.text}>
|
||||||
|
Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span>
|
||||||
|
</text>
|
||||||
<text fg={theme.text}>
|
<text fg={theme.text}>
|
||||||
Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
|
Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
|
||||||
</text>
|
</text>
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import stripAnsi from "strip-ansi"
|
|||||||
import { Footer } from "./footer.tsx"
|
import { Footer } from "./footer.tsx"
|
||||||
import { usePromptRef } from "../../context/prompt"
|
import { usePromptRef } from "../../context/prompt"
|
||||||
import { Filesystem } from "@/util/filesystem"
|
import { Filesystem } from "@/util/filesystem"
|
||||||
|
import { DialogSubagent } from "./dialog-subagent.tsx"
|
||||||
|
|
||||||
addDefaultParsers(parsers.parsers)
|
addDefaultParsers(parsers.parsers)
|
||||||
|
|
||||||
@@ -86,6 +87,7 @@ const context = createContext<{
|
|||||||
showTimestamps: () => boolean
|
showTimestamps: () => boolean
|
||||||
usernameVisible: () => boolean
|
usernameVisible: () => boolean
|
||||||
showDetails: () => boolean
|
showDetails: () => boolean
|
||||||
|
userMessageMarkdown: () => boolean
|
||||||
diffWrapMode: () => "word" | "none"
|
diffWrapMode: () => "word" | "none"
|
||||||
sync: ReturnType<typeof useSync>
|
sync: ReturnType<typeof useSync>
|
||||||
}>()
|
}>()
|
||||||
@@ -123,6 +125,7 @@ export function Session() {
|
|||||||
const [usernameVisible, setUsernameVisible] = createSignal(kv.get("username_visible", true))
|
const [usernameVisible, setUsernameVisible] = createSignal(kv.get("username_visible", true))
|
||||||
const [showDetails, setShowDetails] = createSignal(kv.get("tool_details_visibility", true))
|
const [showDetails, setShowDetails] = createSignal(kv.get("tool_details_visibility", true))
|
||||||
const [showScrollbar, setShowScrollbar] = createSignal(kv.get("scrollbar_visible", false))
|
const [showScrollbar, setShowScrollbar] = createSignal(kv.get("scrollbar_visible", false))
|
||||||
|
const [userMessageMarkdown, setUserMessageMarkdown] = createSignal(kv.get("user_message_markdown", true))
|
||||||
const [diffWrapMode, setDiffWrapMode] = createSignal<"word" | "none">("word")
|
const [diffWrapMode, setDiffWrapMode] = createSignal<"word" | "none">("word")
|
||||||
|
|
||||||
const wide = createMemo(() => dimensions().width > 120)
|
const wide = createMemo(() => dimensions().width > 120)
|
||||||
@@ -165,6 +168,13 @@ export function Session() {
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
|
|
||||||
|
// Handle initial prompt from fork
|
||||||
|
createEffect(() => {
|
||||||
|
if (route.initialPrompt && prompt) {
|
||||||
|
prompt.set(route.initialPrompt)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Auto-navigate to whichever session currently needs permission input
|
// Auto-navigate to whichever session currently needs permission input
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const currentSession = session()
|
const currentSession = session()
|
||||||
@@ -191,6 +201,52 @@ export function Session() {
|
|||||||
let prompt: PromptRef
|
let prompt: PromptRef
|
||||||
const keybind = useKeybind()
|
const keybind = useKeybind()
|
||||||
|
|
||||||
|
// Helper: Find next visible message boundary in direction
|
||||||
|
const findNextVisibleMessage = (direction: "next" | "prev"): string | null => {
|
||||||
|
const children = scroll.getChildren()
|
||||||
|
const messagesList = messages()
|
||||||
|
const scrollTop = scroll.y
|
||||||
|
|
||||||
|
// Get visible messages sorted by position, filtering for valid non-synthetic, non-ignored content
|
||||||
|
const visibleMessages = children
|
||||||
|
.filter((c) => {
|
||||||
|
if (!c.id) return false
|
||||||
|
const message = messagesList.find((m) => m.id === c.id)
|
||||||
|
if (!message) return false
|
||||||
|
|
||||||
|
// Check if message has valid non-synthetic, non-ignored text parts
|
||||||
|
const parts = sync.data.part[message.id]
|
||||||
|
if (!parts || !Array.isArray(parts)) return false
|
||||||
|
|
||||||
|
return parts.some((part) => part && part.type === "text" && !part.synthetic && !part.ignored)
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.y - b.y)
|
||||||
|
|
||||||
|
if (visibleMessages.length === 0) return null
|
||||||
|
|
||||||
|
if (direction === "next") {
|
||||||
|
// Find first message below current position
|
||||||
|
return visibleMessages.find((c) => c.y > scrollTop + 10)?.id ?? null
|
||||||
|
}
|
||||||
|
// Find last message above current position
|
||||||
|
return [...visibleMessages].reverse().find((c) => c.y < scrollTop - 10)?.id ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Scroll to message in direction or fallback to page scroll
|
||||||
|
const scrollToMessage = (direction: "next" | "prev", dialog: ReturnType<typeof useDialog>) => {
|
||||||
|
const targetID = findNextVisibleMessage(direction)
|
||||||
|
|
||||||
|
if (!targetID) {
|
||||||
|
scroll.scrollBy(direction === "next" ? scroll.height : -scroll.height)
|
||||||
|
dialog.clear()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = scroll.getChildren().find((c) => c.id === targetID)
|
||||||
|
if (child) scroll.scrollBy(child.y - scroll.y - 1)
|
||||||
|
dialog.clear()
|
||||||
|
}
|
||||||
|
|
||||||
useKeyboard((evt) => {
|
useKeyboard((evt) => {
|
||||||
if (dialog.stack.length > 0) return
|
if (dialog.stack.length > 0) return
|
||||||
|
|
||||||
@@ -514,6 +570,19 @@ export function Session() {
|
|||||||
dialog.clear()
|
dialog.clear()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: userMessageMarkdown() ? "Disable user message markdown" : "Enable user message markdown",
|
||||||
|
value: "session.toggle.user_message_markdown",
|
||||||
|
category: "Session",
|
||||||
|
onSelect: (dialog) => {
|
||||||
|
setUserMessageMarkdown((prev) => {
|
||||||
|
const next = !prev
|
||||||
|
kv.set("user_message_markdown", next)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
dialog.clear()
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Page up",
|
title: "Page up",
|
||||||
value: "session.page.up",
|
value: "session.page.up",
|
||||||
@@ -611,6 +680,22 @@ export function Session() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Next message",
|
||||||
|
value: "session.message.next",
|
||||||
|
keybind: "messages_next",
|
||||||
|
category: "Session",
|
||||||
|
disabled: true,
|
||||||
|
onSelect: (dialog) => scrollToMessage("next", dialog),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Previous message",
|
||||||
|
value: "session.message.previous",
|
||||||
|
keybind: "messages_previous",
|
||||||
|
category: "Session",
|
||||||
|
disabled: true,
|
||||||
|
onSelect: (dialog) => scrollToMessage("prev", dialog),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Copy last assistant message",
|
title: "Copy last assistant message",
|
||||||
value: "messages.copy",
|
value: "messages.copy",
|
||||||
@@ -785,6 +870,23 @@ export function Session() {
|
|||||||
dialog.clear()
|
dialog.clear()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Go to parent session",
|
||||||
|
value: "session.parent",
|
||||||
|
keybind: "session_parent",
|
||||||
|
category: "Session",
|
||||||
|
disabled: true,
|
||||||
|
onSelect: (dialog) => {
|
||||||
|
const parentID = session()?.parentID
|
||||||
|
if (parentID) {
|
||||||
|
navigate({
|
||||||
|
type: "session",
|
||||||
|
sessionID: parentID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
dialog.clear()
|
||||||
|
},
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const revertInfo = createMemo(() => session()?.revert)
|
const revertInfo = createMemo(() => session()?.revert)
|
||||||
@@ -851,6 +953,7 @@ export function Session() {
|
|||||||
showTimestamps,
|
showTimestamps,
|
||||||
usernameVisible,
|
usernameVisible,
|
||||||
showDetails,
|
showDetails,
|
||||||
|
userMessageMarkdown,
|
||||||
diffWrapMode,
|
diffWrapMode,
|
||||||
sync,
|
sync,
|
||||||
}}
|
}}
|
||||||
@@ -1024,7 +1127,7 @@ function UserMessage(props: {
|
|||||||
const text = createMemo(() => props.parts.flatMap((x) => (x.type === "text" && !x.synthetic ? [x] : []))[0])
|
const text = createMemo(() => props.parts.flatMap((x) => (x.type === "text" && !x.synthetic ? [x] : []))[0])
|
||||||
const files = createMemo(() => props.parts.flatMap((x) => (x.type === "file" ? [x] : [])))
|
const files = createMemo(() => props.parts.flatMap((x) => (x.type === "file" ? [x] : [])))
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const { theme } = useTheme()
|
const { theme, syntax } = useTheme()
|
||||||
const [hover, setHover] = createSignal(false)
|
const [hover, setHover] = createSignal(false)
|
||||||
const queued = createMemo(() => props.pending && props.message.id > props.pending)
|
const queued = createMemo(() => props.pending && props.message.id > props.pending)
|
||||||
const color = createMemo(() => (queued() ? theme.accent : local.agent.color(props.message.agent)))
|
const color = createMemo(() => (queued() ? theme.accent : local.agent.color(props.message.agent)))
|
||||||
@@ -1055,7 +1158,22 @@ function UserMessage(props: {
|
|||||||
backgroundColor={hover() ? theme.backgroundElement : theme.backgroundPanel}
|
backgroundColor={hover() ? theme.backgroundElement : theme.backgroundPanel}
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
<text fg={theme.text}>{text()?.text}</text>
|
<Switch>
|
||||||
|
<Match when={ctx.userMessageMarkdown()}>
|
||||||
|
<code
|
||||||
|
filetype="markdown"
|
||||||
|
drawUnstyledText={false}
|
||||||
|
streaming={false}
|
||||||
|
syntaxStyle={syntax()}
|
||||||
|
content={text()?.text ?? ""}
|
||||||
|
conceal={ctx.conceal()}
|
||||||
|
fg={theme.text}
|
||||||
|
/>
|
||||||
|
</Match>
|
||||||
|
<Match when={!ctx.userMessageMarkdown()}>
|
||||||
|
<text fg={theme.text}>{text()?.text}</text>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
<Show when={files().length}>
|
<Show when={files().length}>
|
||||||
<box flexDirection="row" paddingBottom={1} paddingTop={1} gap={1} flexWrap="wrap">
|
<box flexDirection="row" paddingBottom={1} paddingTop={1} gap={1} flexWrap="wrap">
|
||||||
<For each={files()}>
|
<For each={files()}>
|
||||||
@@ -1528,13 +1646,33 @@ ToolRegistry.register<typeof ListTool>({
|
|||||||
|
|
||||||
ToolRegistry.register<typeof TaskTool>({
|
ToolRegistry.register<typeof TaskTool>({
|
||||||
name: "task",
|
name: "task",
|
||||||
container: "block",
|
container: "inline",
|
||||||
render(props) {
|
render(props) {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const keybind = useKeybind()
|
const keybind = useKeybind()
|
||||||
|
const dialog = useDialog()
|
||||||
|
const renderer = useRenderer()
|
||||||
|
const [hover, setHover] = createSignal(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<box
|
||||||
|
border={["left"]}
|
||||||
|
customBorderChars={SplitBorder.customBorderChars}
|
||||||
|
borderColor={theme.background}
|
||||||
|
paddingTop={1}
|
||||||
|
paddingBottom={1}
|
||||||
|
paddingLeft={2}
|
||||||
|
marginTop={1}
|
||||||
|
gap={1}
|
||||||
|
backgroundColor={hover() ? theme.backgroundElement : theme.backgroundPanel}
|
||||||
|
onMouseOver={() => setHover(true)}
|
||||||
|
onMouseOut={() => setHover(false)}
|
||||||
|
onMouseUp={() => {
|
||||||
|
const id = props.metadata.sessionId
|
||||||
|
if (renderer.getSelection()?.getSelectedText() || !id) return
|
||||||
|
dialog.replace(() => <DialogSubagent sessionID={id} />)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ToolTitle icon="◉" fallback="Delegating..." when={props.input.subagent_type ?? props.input.description}>
|
<ToolTitle icon="◉" fallback="Delegating..." when={props.input.subagent_type ?? props.input.description}>
|
||||||
{Locale.titlecase(props.input.subagent_type ?? "unknown")} Task "{props.input.description}"
|
{Locale.titlecase(props.input.subagent_type ?? "unknown")} Task "{props.input.description}"
|
||||||
</ToolTitle>
|
</ToolTitle>
|
||||||
@@ -1557,7 +1695,7 @@ ToolRegistry.register<typeof TaskTool>({
|
|||||||
{keybind.print("session_child_cycle")}, {keybind.print("session_child_cycle_reverse")}
|
{keybind.print("session_child_cycle")}, {keybind.print("session_child_cycle_reverse")}
|
||||||
<span style={{ fg: theme.textMuted }}> to navigate between subagent sessions</span>
|
<span style={{ fg: theme.textMuted }}> to navigate between subagent sessions</span>
|
||||||
</text>
|
</text>
|
||||||
</>
|
</box>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -154,7 +154,11 @@ export function Sidebar(props: { sessionID: string }) {
|
|||||||
</box>
|
</box>
|
||||||
<Show when={sync.data.lsp.length <= 2 || expanded.lsp}>
|
<Show when={sync.data.lsp.length <= 2 || expanded.lsp}>
|
||||||
<Show when={sync.data.lsp.length === 0}>
|
<Show when={sync.data.lsp.length === 0}>
|
||||||
<text fg={theme.textMuted}>LSPs will activate as files are read</text>
|
<text fg={theme.textMuted}>
|
||||||
|
{sync.data.config.lsp === false
|
||||||
|
? "LSPs have been disabled in settings"
|
||||||
|
: "LSPs will activate as files are read"}
|
||||||
|
</text>
|
||||||
</Show>
|
</Show>
|
||||||
<For each={sync.data.lsp}>
|
<For each={sync.data.lsp}>
|
||||||
{(item) => (
|
{(item) => (
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export function FormatError(input: unknown) {
|
|||||||
}
|
}
|
||||||
if (Config.InvalidError.isInstance(input))
|
if (Config.InvalidError.isInstance(input))
|
||||||
return [
|
return [
|
||||||
`Config file at ${input.data.path} is invalid` + (input.data.message ? `: ${input.data.message}` : ""),
|
`Configuration is invalid${input.data.path && input.data.path !== "config" ? ` at ${input.data.path}` : ""}` +
|
||||||
|
(input.data.message ? `: ${input.data.message}` : ""),
|
||||||
...(input.data.issues?.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")) ?? []),
|
...(input.data.issues?.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")) ?? []),
|
||||||
].join("\n")
|
].join("\n")
|
||||||
|
|
||||||
|
|||||||
@@ -28,31 +28,53 @@ Use best judgement when processing input.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Gathering Context
|
||||||
|
|
||||||
|
**Diffs alone are not enough.** After getting the diff, read the entire file(s) being modified to understand the full context. Code that looks wrong in isolation may be correct given surrounding logic—and vice versa.
|
||||||
|
|
||||||
|
- Use the diff to identify which files changed
|
||||||
|
- Read the full file to understand existing patterns, control flow, and error handling
|
||||||
|
- Check for existing style guide or conventions files (CONVENTIONS.md, AGENTS.md, .editorconfig, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## What to Look For
|
## What to Look For
|
||||||
|
|
||||||
**Bugs** - Your primary focus.
|
**Bugs** - Your primary focus.
|
||||||
- Logic errors, off-by-one mistakes, incorrect conditionals
|
- Logic errors, off-by-one mistakes, incorrect conditionals
|
||||||
- Edge cases: null/empty inputs, error conditions, race conditions
|
- If-else guards: missing guards, incorrect branching, unreachable code paths
|
||||||
|
- Edge cases: null/empty/undefined inputs, error conditions, race conditions
|
||||||
- Security issues: injection, auth bypass, data exposure
|
- Security issues: injection, auth bypass, data exposure
|
||||||
- Broken error handling that swallows failures
|
- Broken error handling that swallows failures, throws unexpectedly or returns error types that are not caught.
|
||||||
|
|
||||||
**Structure** - Does the code fit the codebase?
|
**Structure** - Does the code fit the codebase?
|
||||||
- Does it follow existing patterns and conventions?
|
- Does it follow existing patterns and conventions?
|
||||||
- Are there established abstractions it should use but doesn't?
|
- Are there established abstractions it should use but doesn't?
|
||||||
|
- Excessive nesting that could be flattened with early returns or extraction
|
||||||
|
|
||||||
**Performance** - Only flag if obviously problematic.
|
**Performance** - Only flag if obviously problematic.
|
||||||
- O(n²) on unbounded data, N+1 queries, blocking I/O on hot paths
|
- O(n²) on unbounded data, N+1 queries, blocking I/O on hot paths
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Before You Flag Something
|
## Before You Flag Something
|
||||||
|
|
||||||
Be certain. If you're going to call something a bug, you need to be confident it actually is one.
|
**Be certain.** If you're going to call something a bug, you need to be confident it actually is one.
|
||||||
|
|
||||||
- Only review the changes - do not review pre-existing code that wasn't modified
|
- Only review the changes - do not review pre-existing code that wasn't modified
|
||||||
- Don't flag something as a bug if you're unsure - investigate first
|
- Don't flag something as a bug if you're unsure - investigate first
|
||||||
- Don't flag style preferences as issues
|
|
||||||
- Don't invent hypothetical problems - if an edge case matters, explain the realistic scenario where it breaks
|
- Don't invent hypothetical problems - if an edge case matters, explain the realistic scenario where it breaks
|
||||||
- If you need more context to be sure, use the tools below to get it
|
- If you need more context to be sure, use the tools below to get it
|
||||||
|
|
||||||
|
**Don't be a zealot about style.** When checking code against conventions:
|
||||||
|
|
||||||
|
- Verify the code is *actually* in violation. Don't complain about else statements if early returns are already being used correctly.
|
||||||
|
- Some "violations" are acceptable when they're the simplest option. A `let` statement is fine if the alternative is convoluted.
|
||||||
|
- Excessive nesting is a legitimate concern regardless of other style choices.
|
||||||
|
- Don't flag style preferences as issues unless they clearly violate established project conventions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
Use these to inform your review:
|
Use these to inform your review:
|
||||||
@@ -63,11 +85,13 @@ Use these to inform your review:
|
|||||||
|
|
||||||
If you're uncertain about something and can't verify it with these tools, say "I'm not sure about X" rather than flagging it as a definite issue.
|
If you're uncertain about something and can't verify it with these tools, say "I'm not sure about X" rather than flagging it as a definite issue.
|
||||||
|
|
||||||
## Tone and Approach
|
---
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
1. If there is a bug, be direct and clear about why it is a bug.
|
1. If there is a bug, be direct and clear about why it is a bug.
|
||||||
2. You should clearly communicate severity of issues, do not claim issues are more severe than they actually are.
|
2. Clearly communicate severity of issues. Do not overstate severity.
|
||||||
3. Critiques should clearly and explicitly communicate the scenarios, environments, or inputs that are necessary for the bug to arise. The comment should immediately indicate that the issue's severity depends on these factors.
|
3. Critiques should clearly and explicitly communicate the scenarios, environments, or inputs that are necessary for the bug to arise. The comment should immediately indicate that the issue's severity depends on these factors.
|
||||||
4. Your tone should be matter-of-fact and not accusatory or overly positive. It should read as a helpful AI assistant suggestion without sounding too much like a human reviewer.
|
4. Your tone should be matter-of-fact and not accusatory or overly positive. It should read as a helpful AI assistant suggestion without sounding too much like a human reviewer.
|
||||||
5. Write in a manner that allows reader to quickly understand issue without reading too closely.
|
5. Write so the reader can quickly understand the issue without reading too closely.
|
||||||
6. AVOID flattery, do not give any comments that are not helpful to the reader. Avoid phrasing like "Great job ...", "Thanks for ...".
|
6. AVOID flattery, do not give any comments that are not helpful to the reader. Avoid phrasing like "Great job ...", "Thanks for ...".
|
||||||
|
|||||||
@@ -414,6 +414,7 @@ export namespace Config {
|
|||||||
.object({
|
.object({
|
||||||
edit: Permission.optional(),
|
edit: Permission.optional(),
|
||||||
bash: z.union([Permission, z.record(z.string(), Permission)]).optional(),
|
bash: z.union([Permission, z.record(z.string(), Permission)]).optional(),
|
||||||
|
skill: z.union([Permission, z.record(z.string(), Permission)]).optional(),
|
||||||
webfetch: Permission.optional(),
|
webfetch: Permission.optional(),
|
||||||
doom_loop: Permission.optional(),
|
doom_loop: Permission.optional(),
|
||||||
external_directory: Permission.optional(),
|
external_directory: Permission.optional(),
|
||||||
@@ -456,6 +457,8 @@ export namespace Config {
|
|||||||
.describe("Scroll messages down by half page"),
|
.describe("Scroll messages down by half page"),
|
||||||
messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"),
|
messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"),
|
||||||
messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"),
|
messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"),
|
||||||
|
messages_next: z.string().optional().default("none").describe("Navigate to next message"),
|
||||||
|
messages_previous: z.string().optional().default("none").describe("Navigate to previous message"),
|
||||||
messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"),
|
messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"),
|
||||||
messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
|
messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
|
||||||
messages_undo: z.string().optional().default("<leader>u").describe("Undo message"),
|
messages_undo: z.string().optional().default("<leader>u").describe("Undo message"),
|
||||||
@@ -560,6 +563,7 @@ export namespace Config {
|
|||||||
history_next: z.string().optional().default("down").describe("Next history item"),
|
history_next: z.string().optional().default("down").describe("Next history item"),
|
||||||
session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"),
|
session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"),
|
||||||
session_child_cycle_reverse: z.string().optional().default("<leader>left").describe("Previous child session"),
|
session_child_cycle_reverse: z.string().optional().default("<leader>left").describe("Previous child session"),
|
||||||
|
session_parent: z.string().optional().default("<leader>up").describe("Go to parent session"),
|
||||||
terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
|
terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
|
||||||
terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
|
terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
|
||||||
})
|
})
|
||||||
@@ -666,6 +670,12 @@ export namespace Config {
|
|||||||
.string()
|
.string()
|
||||||
.describe("Small model to use for tasks like title generation in the format of provider/model")
|
.describe("Small model to use for tasks like title generation in the format of provider/model")
|
||||||
.optional(),
|
.optional(),
|
||||||
|
default_agent: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid.",
|
||||||
|
),
|
||||||
username: z
|
username: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -755,6 +765,7 @@ export namespace Config {
|
|||||||
.object({
|
.object({
|
||||||
edit: Permission.optional(),
|
edit: Permission.optional(),
|
||||||
bash: z.union([Permission, z.record(z.string(), Permission)]).optional(),
|
bash: z.union([Permission, z.record(z.string(), Permission)]).optional(),
|
||||||
|
skill: z.union([Permission, z.record(z.string(), Permission)]).optional(),
|
||||||
webfetch: Permission.optional(),
|
webfetch: Permission.optional(),
|
||||||
doom_loop: Permission.optional(),
|
doom_loop: Permission.optional(),
|
||||||
external_directory: Permission.optional(),
|
external_directory: Permission.optional(),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import type { BunFile } from "bun"
|
import type { BunFile } from "bun"
|
||||||
@@ -74,6 +73,7 @@ export namespace File {
|
|||||||
|
|
||||||
async function shouldEncode(file: BunFile): Promise<boolean> {
|
async function shouldEncode(file: BunFile): Promise<boolean> {
|
||||||
const type = file.type?.toLowerCase()
|
const type = file.type?.toLowerCase()
|
||||||
|
log.info("shouldEncode", { type })
|
||||||
if (!type) return false
|
if (!type) return false
|
||||||
|
|
||||||
if (type.startsWith("text/")) return false
|
if (type.startsWith("text/")) return false
|
||||||
@@ -87,15 +87,12 @@ export namespace File {
|
|||||||
const tops = ["image", "audio", "video", "font", "model", "multipart"]
|
const tops = ["image", "audio", "video", "font", "model", "multipart"]
|
||||||
if (tops.includes(top)) return true
|
if (tops.includes(top)) return true
|
||||||
|
|
||||||
if (type === "application/octet-stream") return true
|
|
||||||
|
|
||||||
const bins = [
|
const bins = [
|
||||||
"zip",
|
"zip",
|
||||||
"gzip",
|
"gzip",
|
||||||
"bzip",
|
"bzip",
|
||||||
"compressed",
|
"compressed",
|
||||||
"binary",
|
"binary",
|
||||||
"stream",
|
|
||||||
"pdf",
|
"pdf",
|
||||||
"msword",
|
"msword",
|
||||||
"powerpoint",
|
"powerpoint",
|
||||||
@@ -125,6 +122,8 @@ export namespace File {
|
|||||||
let cache: Entry = { files: [], dirs: [] }
|
let cache: Entry = { files: [], dirs: [] }
|
||||||
let fetching = false
|
let fetching = false
|
||||||
const fn = async (result: Entry) => {
|
const fn = async (result: Entry) => {
|
||||||
|
// Disable scanning if in root of file system
|
||||||
|
if (Instance.directory === path.parse(Instance.directory).root) return
|
||||||
fetching = true
|
fetching = true
|
||||||
const set = new Set<string>()
|
const set = new Set<string>()
|
||||||
for await (const file of Ripgrep.files({ cwd: Instance.directory })) {
|
for await (const file of Ripgrep.files({ cwd: Instance.directory })) {
|
||||||
@@ -290,9 +289,11 @@ export namespace File {
|
|||||||
}
|
}
|
||||||
const resolved = dir ? path.join(Instance.directory, dir) : Instance.directory
|
const resolved = dir ? path.join(Instance.directory, dir) : Instance.directory
|
||||||
const nodes: Node[] = []
|
const nodes: Node[] = []
|
||||||
for (const entry of await fs.promises.readdir(resolved, {
|
for (const entry of await fs.promises
|
||||||
withFileTypes: true,
|
.readdir(resolved, {
|
||||||
})) {
|
withFileTypes: true,
|
||||||
|
})
|
||||||
|
.catch(() => [])) {
|
||||||
if (exclude.includes(entry.name)) continue
|
if (exclude.includes(entry.name)) continue
|
||||||
const fullPath = path.join(resolved, entry.name)
|
const fullPath = path.join(resolved, entry.name)
|
||||||
const relativePath = path.relative(Instance.directory, fullPath)
|
const relativePath = path.relative(Instance.directory, fullPath)
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export namespace Flag {
|
|||||||
export const OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS = number("OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS")
|
export const OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS = number("OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS")
|
||||||
export const OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX = number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX")
|
export const OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX = number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX")
|
||||||
export const OPENCODE_EXPERIMENTAL_OXFMT = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_OXFMT")
|
export const OPENCODE_EXPERIMENTAL_OXFMT = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_OXFMT")
|
||||||
|
export const OPENCODE_EXPERIMENTAL_LSP_TY = truthy("OPENCODE_EXPERIMENTAL_LSP_TY")
|
||||||
|
export const OPENCODE_EXPERIMENTAL_LSP_TOOL = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_LSP_TOOL")
|
||||||
|
|
||||||
function truthy(key: string) {
|
function truthy(key: string) {
|
||||||
const value = process.env[key]?.toLowerCase()
|
const value = process.env[key]?.toLowerCase()
|
||||||
|
|||||||
@@ -1,19 +1,73 @@
|
|||||||
import { Identifier as SharedIdentifier } from "@opencode-ai/util/identifier"
|
import z from "zod"
|
||||||
|
import { randomBytes } from "crypto"
|
||||||
|
|
||||||
export namespace Identifier {
|
export namespace Identifier {
|
||||||
export type Prefix = SharedIdentifier.Prefix
|
const prefixes = {
|
||||||
|
session: "ses",
|
||||||
|
message: "msg",
|
||||||
|
permission: "per",
|
||||||
|
user: "usr",
|
||||||
|
part: "prt",
|
||||||
|
pty: "pty",
|
||||||
|
} as const
|
||||||
|
|
||||||
export const schema = (prefix: Prefix) => SharedIdentifier.schema(prefix)
|
export function schema(prefix: keyof typeof prefixes) {
|
||||||
|
return z.string().startsWith(prefixes[prefix])
|
||||||
export function ascending(prefix: Prefix, given?: string) {
|
|
||||||
return SharedIdentifier.ascending(prefix, given)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function descending(prefix: Prefix, given?: string) {
|
const LENGTH = 26
|
||||||
return SharedIdentifier.descending(prefix, given)
|
|
||||||
|
// State for monotonic ID generation
|
||||||
|
let lastTimestamp = 0
|
||||||
|
let counter = 0
|
||||||
|
|
||||||
|
export function ascending(prefix: keyof typeof prefixes, given?: string) {
|
||||||
|
return generateID(prefix, false, given)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create(prefix: Prefix, descending: boolean, timestamp?: number) {
|
export function descending(prefix: keyof typeof prefixes, given?: string) {
|
||||||
return SharedIdentifier.createPrefixed(prefix, descending, timestamp)
|
return generateID(prefix, true, given)
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateID(prefix: keyof typeof prefixes, descending: boolean, given?: string): string {
|
||||||
|
if (!given) {
|
||||||
|
return create(prefix, descending)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!given.startsWith(prefixes[prefix])) {
|
||||||
|
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`)
|
||||||
|
}
|
||||||
|
return given
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomBase62(length: number): string {
|
||||||
|
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
let result = ""
|
||||||
|
const bytes = randomBytes(length)
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += chars[bytes[i] % 62]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export function create(prefix: keyof typeof prefixes, descending: boolean, timestamp?: number): string {
|
||||||
|
const currentTimestamp = timestamp ?? Date.now()
|
||||||
|
|
||||||
|
if (currentTimestamp !== lastTimestamp) {
|
||||||
|
lastTimestamp = currentTimestamp
|
||||||
|
counter = 0
|
||||||
|
}
|
||||||
|
counter++
|
||||||
|
|
||||||
|
let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter)
|
||||||
|
|
||||||
|
now = descending ? ~now : now
|
||||||
|
|
||||||
|
const timeBytes = Buffer.alloc(6)
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefixes[prefix] + "_" + timeBytes.toString("hex") + randomBase62(LENGTH - 12)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
@@ -66,23 +65,23 @@ export namespace Installation {
|
|||||||
const checks = [
|
const checks = [
|
||||||
{
|
{
|
||||||
name: "npm" as const,
|
name: "npm" as const,
|
||||||
command: () => $`npm list -g --depth=0`.throws(false).text(),
|
command: () => $`npm list -g --depth=0`.throws(false).quiet().text(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "yarn" as const,
|
name: "yarn" as const,
|
||||||
command: () => $`yarn global list`.throws(false).text(),
|
command: () => $`yarn global list`.throws(false).quiet().text(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pnpm" as const,
|
name: "pnpm" as const,
|
||||||
command: () => $`pnpm list -g --depth=0`.throws(false).text(),
|
command: () => $`pnpm list -g --depth=0`.throws(false).quiet().text(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bun" as const,
|
name: "bun" as const,
|
||||||
command: () => $`bun pm ls -g`.throws(false).text(),
|
command: () => $`bun pm ls -g`.throws(false).quiet().text(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "brew" as const,
|
name: "brew" as const,
|
||||||
command: () => $`brew list --formula opencode`.throws(false).text(),
|
command: () => $`brew list --formula opencode`.throws(false).quiet().text(),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -112,9 +111,9 @@ export namespace Installation {
|
|||||||
)
|
)
|
||||||
|
|
||||||
async function getBrewFormula() {
|
async function getBrewFormula() {
|
||||||
const tapFormula = await $`brew list --formula sst/tap/opencode`.throws(false).text()
|
const tapFormula = await $`brew list --formula sst/tap/opencode`.throws(false).quiet().text()
|
||||||
if (tapFormula.includes("opencode")) return "sst/tap/opencode"
|
if (tapFormula.includes("opencode")) return "sst/tap/opencode"
|
||||||
const coreFormula = await $`brew list --formula opencode`.throws(false).text()
|
const coreFormula = await $`brew list --formula opencode`.throws(false).quiet().text()
|
||||||
if (coreFormula.includes("opencode")) return "opencode"
|
if (coreFormula.includes("opencode")) return "opencode"
|
||||||
return "opencode"
|
return "opencode"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import z from "zod"
|
|||||||
import { Config } from "../config/config"
|
import { Config } from "../config/config"
|
||||||
import { spawn } from "child_process"
|
import { spawn } from "child_process"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
|
import { Flag } from "@/flag/flag"
|
||||||
|
|
||||||
export namespace LSP {
|
export namespace LSP {
|
||||||
const log = Log.create({ service: "lsp" })
|
const log = Log.create({ service: "lsp" })
|
||||||
@@ -60,6 +61,21 @@ export namespace LSP {
|
|||||||
})
|
})
|
||||||
export type DocumentSymbol = z.infer<typeof DocumentSymbol>
|
export type DocumentSymbol = z.infer<typeof DocumentSymbol>
|
||||||
|
|
||||||
|
const filterExperimentalServers = (servers: Record<string, LSPServer.Info>) => {
|
||||||
|
if (Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
|
||||||
|
// If experimental flag is enabled, disable pyright
|
||||||
|
if (servers["pyright"]) {
|
||||||
|
log.info("LSP server pyright is disabled because OPENCODE_EXPERIMENTAL_LSP_TY is enabled")
|
||||||
|
delete servers["pyright"]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If experimental flag is disabled, disable ty
|
||||||
|
if (servers["ty"]) {
|
||||||
|
delete servers["ty"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const state = Instance.state(
|
const state = Instance.state(
|
||||||
async () => {
|
async () => {
|
||||||
const clients: LSPClient.Info[] = []
|
const clients: LSPClient.Info[] = []
|
||||||
@@ -79,6 +95,9 @@ export namespace LSP {
|
|||||||
for (const server of Object.values(LSPServer)) {
|
for (const server of Object.values(LSPServer)) {
|
||||||
servers[server.id] = server
|
servers[server.id] = server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterExperimentalServers(servers)
|
||||||
|
|
||||||
for (const [name, item] of Object.entries(cfg.lsp ?? {})) {
|
for (const [name, item] of Object.entries(cfg.lsp ?? {})) {
|
||||||
const existing = servers[name]
|
const existing = servers[name]
|
||||||
if (item.disabled) {
|
if (item.disabled) {
|
||||||
@@ -204,6 +223,7 @@ export namespace LSP {
|
|||||||
|
|
||||||
for (const server of Object.values(s.servers)) {
|
for (const server of Object.values(s.servers)) {
|
||||||
if (server.extensions.length && !server.extensions.includes(extension)) continue
|
if (server.extensions.length && !server.extensions.includes(extension)) continue
|
||||||
|
|
||||||
const root = await server.root(file)
|
const root = await server.root(file)
|
||||||
if (!root) continue
|
if (!root) continue
|
||||||
if (s.broken.has(root + server.id)) continue
|
if (s.broken.has(root + server.id)) continue
|
||||||
@@ -241,23 +261,36 @@ export namespace LSP {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function hasClients(file: string) {
|
||||||
|
const s = await state()
|
||||||
|
const extension = path.parse(file).ext || file
|
||||||
|
for (const server of Object.values(s.servers)) {
|
||||||
|
if (server.extensions.length && !server.extensions.includes(extension)) continue
|
||||||
|
const root = await server.root(file)
|
||||||
|
if (!root) continue
|
||||||
|
if (s.broken.has(root + server.id)) continue
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
export async function touchFile(input: string, waitForDiagnostics?: boolean) {
|
export async function touchFile(input: string, waitForDiagnostics?: boolean) {
|
||||||
log.info("touching file", { file: input })
|
log.info("touching file", { file: input })
|
||||||
const clients = await getClients(input)
|
const clients = await getClients(input)
|
||||||
await run(async (client) => {
|
await Promise.all(
|
||||||
if (!clients.includes(client)) return
|
clients.map(async (client) => {
|
||||||
const wait = waitForDiagnostics ? client.waitForDiagnostics({ path: input }) : Promise.resolve()
|
const wait = waitForDiagnostics ? client.waitForDiagnostics({ path: input }) : Promise.resolve()
|
||||||
await client.notify.open({ path: input })
|
await client.notify.open({ path: input })
|
||||||
|
return wait
|
||||||
return wait
|
}),
|
||||||
}).catch((err) => {
|
).catch((err) => {
|
||||||
log.error("failed to touch file", { err, file: input })
|
log.error("failed to touch file", { err, file: input })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function diagnostics() {
|
export async function diagnostics() {
|
||||||
const results: Record<string, LSPClient.Diagnostic[]> = {}
|
const results: Record<string, LSPClient.Diagnostic[]> = {}
|
||||||
for (const result of await run(async (client) => client.diagnostics)) {
|
for (const result of await runAll(async (client) => client.diagnostics)) {
|
||||||
for (const [path, diagnostics] of result.entries()) {
|
for (const [path, diagnostics] of result.entries()) {
|
||||||
const arr = results[path] || []
|
const arr = results[path] || []
|
||||||
arr.push(...diagnostics)
|
arr.push(...diagnostics)
|
||||||
@@ -268,16 +301,18 @@ export namespace LSP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function hover(input: { file: string; line: number; character: number }) {
|
export async function hover(input: { file: string; line: number; character: number }) {
|
||||||
return run((client) => {
|
return run(input.file, (client) => {
|
||||||
return client.connection.sendRequest("textDocument/hover", {
|
return client.connection
|
||||||
textDocument: {
|
.sendRequest("textDocument/hover", {
|
||||||
uri: pathToFileURL(input.file).href,
|
textDocument: {
|
||||||
},
|
uri: pathToFileURL(input.file).href,
|
||||||
position: {
|
},
|
||||||
line: input.line,
|
position: {
|
||||||
character: input.character,
|
line: input.line,
|
||||||
},
|
character: input.character,
|
||||||
})
|
},
|
||||||
|
})
|
||||||
|
.catch(() => null)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +357,7 @@ export namespace LSP {
|
|||||||
]
|
]
|
||||||
|
|
||||||
export async function workspaceSymbol(query: string) {
|
export async function workspaceSymbol(query: string) {
|
||||||
return run((client) =>
|
return runAll((client) =>
|
||||||
client.connection
|
client.connection
|
||||||
.sendRequest("workspace/symbol", {
|
.sendRequest("workspace/symbol", {
|
||||||
query,
|
query,
|
||||||
@@ -334,7 +369,8 @@ export namespace LSP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function documentSymbol(uri: string) {
|
export async function documentSymbol(uri: string) {
|
||||||
return run((client) =>
|
const file = new URL(uri).pathname
|
||||||
|
return run(file, (client) =>
|
||||||
client.connection
|
client.connection
|
||||||
.sendRequest("textDocument/documentSymbol", {
|
.sendRequest("textDocument/documentSymbol", {
|
||||||
textDocument: {
|
textDocument: {
|
||||||
@@ -347,12 +383,89 @@ export namespace LSP {
|
|||||||
.then((result) => result.filter(Boolean))
|
.then((result) => result.filter(Boolean))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run<T>(input: (client: LSPClient.Info) => Promise<T>): Promise<T[]> {
|
export async function definition(input: { file: string; line: number; character: number }) {
|
||||||
|
return run(input.file, (client) =>
|
||||||
|
client.connection
|
||||||
|
.sendRequest("textDocument/definition", {
|
||||||
|
textDocument: { uri: pathToFileURL(input.file).href },
|
||||||
|
position: { line: input.line, character: input.character },
|
||||||
|
})
|
||||||
|
.catch(() => null),
|
||||||
|
).then((result) => result.flat().filter(Boolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function references(input: { file: string; line: number; character: number }) {
|
||||||
|
return run(input.file, (client) =>
|
||||||
|
client.connection
|
||||||
|
.sendRequest("textDocument/references", {
|
||||||
|
textDocument: { uri: pathToFileURL(input.file).href },
|
||||||
|
position: { line: input.line, character: input.character },
|
||||||
|
context: { includeDeclaration: true },
|
||||||
|
})
|
||||||
|
.catch(() => []),
|
||||||
|
).then((result) => result.flat().filter(Boolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function implementation(input: { file: string; line: number; character: number }) {
|
||||||
|
return run(input.file, (client) =>
|
||||||
|
client.connection
|
||||||
|
.sendRequest("textDocument/implementation", {
|
||||||
|
textDocument: { uri: pathToFileURL(input.file).href },
|
||||||
|
position: { line: input.line, character: input.character },
|
||||||
|
})
|
||||||
|
.catch(() => null),
|
||||||
|
).then((result) => result.flat().filter(Boolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareCallHierarchy(input: { file: string; line: number; character: number }) {
|
||||||
|
return run(input.file, (client) =>
|
||||||
|
client.connection
|
||||||
|
.sendRequest("textDocument/prepareCallHierarchy", {
|
||||||
|
textDocument: { uri: pathToFileURL(input.file).href },
|
||||||
|
position: { line: input.line, character: input.character },
|
||||||
|
})
|
||||||
|
.catch(() => []),
|
||||||
|
).then((result) => result.flat().filter(Boolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function incomingCalls(input: { file: string; line: number; character: number }) {
|
||||||
|
return run(input.file, async (client) => {
|
||||||
|
const items = (await client.connection
|
||||||
|
.sendRequest("textDocument/prepareCallHierarchy", {
|
||||||
|
textDocument: { uri: pathToFileURL(input.file).href },
|
||||||
|
position: { line: input.line, character: input.character },
|
||||||
|
})
|
||||||
|
.catch(() => [])) as any[]
|
||||||
|
if (!items?.length) return []
|
||||||
|
return client.connection.sendRequest("callHierarchy/incomingCalls", { item: items[0] }).catch(() => [])
|
||||||
|
}).then((result) => result.flat().filter(Boolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function outgoingCalls(input: { file: string; line: number; character: number }) {
|
||||||
|
return run(input.file, async (client) => {
|
||||||
|
const items = (await client.connection
|
||||||
|
.sendRequest("textDocument/prepareCallHierarchy", {
|
||||||
|
textDocument: { uri: pathToFileURL(input.file).href },
|
||||||
|
position: { line: input.line, character: input.character },
|
||||||
|
})
|
||||||
|
.catch(() => [])) as any[]
|
||||||
|
if (!items?.length) return []
|
||||||
|
return client.connection.sendRequest("callHierarchy/outgoingCalls", { item: items[0] }).catch(() => [])
|
||||||
|
}).then((result) => result.flat().filter(Boolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runAll<T>(input: (client: LSPClient.Info) => Promise<T>): Promise<T[]> {
|
||||||
const clients = await state().then((x) => x.clients)
|
const clients = await state().then((x) => x.clients)
|
||||||
const tasks = clients.map((x) => input(x))
|
const tasks = clients.map((x) => input(x))
|
||||||
return Promise.all(tasks)
|
return Promise.all(tasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function run<T>(file: string, input: (client: LSPClient.Info) => Promise<T>): Promise<T[]> {
|
||||||
|
const clients = await getClients(file)
|
||||||
|
const tasks = clients.map((x) => input(x))
|
||||||
|
return Promise.all(tasks)
|
||||||
|
}
|
||||||
|
|
||||||
export namespace Diagnostic {
|
export namespace Diagnostic {
|
||||||
export function pretty(diagnostic: LSPClient.Diagnostic) {
|
export function pretty(diagnostic: LSPClient.Diagnostic) {
|
||||||
const severityMap = {
|
const severityMap = {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = {
|
|||||||
".ex": "elixir",
|
".ex": "elixir",
|
||||||
".exs": "elixir",
|
".exs": "elixir",
|
||||||
".erl": "erlang",
|
".erl": "erlang",
|
||||||
|
".ets": "typescript",
|
||||||
".hrl": "erlang",
|
".hrl": "erlang",
|
||||||
".fs": "fsharp",
|
".fs": "fsharp",
|
||||||
".fsi": "fsharp",
|
".fsi": "fsharp",
|
||||||
@@ -109,4 +110,7 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = {
|
|||||||
".tf": "terraform",
|
".tf": "terraform",
|
||||||
".tfvars": "terraform-vars",
|
".tfvars": "terraform-vars",
|
||||||
".hcl": "hcl",
|
".hcl": "hcl",
|
||||||
|
".nix": "nix",
|
||||||
|
".typ": "typst",
|
||||||
|
".typc": "typst",
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import os from "os"
|
|||||||
import { Global } from "../global"
|
import { Global } from "../global"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { BunProc } from "../bun"
|
import { BunProc } from "../bun"
|
||||||
import { $ } from "bun"
|
import { $, readableStreamToText } from "bun"
|
||||||
import fs from "fs/promises"
|
import fs from "fs/promises"
|
||||||
import { Filesystem } from "../util/filesystem"
|
import { Filesystem } from "../util/filesystem"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
@@ -216,6 +216,77 @@ export namespace LSPServer {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Oxlint: Info = {
|
||||||
|
id: "oxlint",
|
||||||
|
root: NearestRoot([
|
||||||
|
".oxlintrc.json",
|
||||||
|
"package-lock.json",
|
||||||
|
"bun.lockb",
|
||||||
|
"bun.lock",
|
||||||
|
"pnpm-lock.yaml",
|
||||||
|
"yarn.lock",
|
||||||
|
"package.json",
|
||||||
|
]),
|
||||||
|
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".vue", ".astro", ".svelte"],
|
||||||
|
async spawn(root) {
|
||||||
|
const ext = process.platform === "win32" ? ".cmd" : ""
|
||||||
|
|
||||||
|
const serverTarget = path.join("node_modules", ".bin", "oxc_language_server" + ext)
|
||||||
|
const lintTarget = path.join("node_modules", ".bin", "oxlint" + ext)
|
||||||
|
|
||||||
|
const resolveBin = async (target: string) => {
|
||||||
|
const localBin = path.join(root, target)
|
||||||
|
if (await Bun.file(localBin).exists()) return localBin
|
||||||
|
|
||||||
|
const candidates = Filesystem.up({
|
||||||
|
targets: [target],
|
||||||
|
start: root,
|
||||||
|
stop: Instance.worktree,
|
||||||
|
})
|
||||||
|
const first = await candidates.next()
|
||||||
|
await candidates.return()
|
||||||
|
if (first.value) return first.value
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
let lintBin = await resolveBin(lintTarget)
|
||||||
|
if (!lintBin) {
|
||||||
|
const found = Bun.which("oxlint")
|
||||||
|
if (found) lintBin = found
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lintBin) {
|
||||||
|
const proc = Bun.spawn([lintBin, "--help"], { stdout: "pipe" })
|
||||||
|
await proc.exited
|
||||||
|
const help = await readableStreamToText(proc.stdout)
|
||||||
|
if (help.includes("--lsp")) {
|
||||||
|
return {
|
||||||
|
process: spawn(lintBin, ["--lsp"], {
|
||||||
|
cwd: root,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverBin = await resolveBin(serverTarget)
|
||||||
|
if (!serverBin) {
|
||||||
|
const found = Bun.which("oxc_language_server")
|
||||||
|
if (found) serverBin = found
|
||||||
|
}
|
||||||
|
if (serverBin) {
|
||||||
|
return {
|
||||||
|
process: spawn(serverBin, [], {
|
||||||
|
cwd: root,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("oxlint not found, please install oxlint")
|
||||||
|
return
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export const Biome: Info = {
|
export const Biome: Info = {
|
||||||
id: "biome",
|
id: "biome",
|
||||||
root: NearestRoot([
|
root: NearestRoot([
|
||||||
@@ -361,6 +432,70 @@ export namespace LSPServer {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Ty: Info = {
|
||||||
|
id: "ty",
|
||||||
|
extensions: [".py", ".pyi"],
|
||||||
|
root: NearestRoot([
|
||||||
|
"pyproject.toml",
|
||||||
|
"ty.toml",
|
||||||
|
"setup.py",
|
||||||
|
"setup.cfg",
|
||||||
|
"requirements.txt",
|
||||||
|
"Pipfile",
|
||||||
|
"pyrightconfig.json",
|
||||||
|
]),
|
||||||
|
async spawn(root) {
|
||||||
|
if (!Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
let binary = Bun.which("ty")
|
||||||
|
|
||||||
|
const initialization: Record<string, string> = {}
|
||||||
|
|
||||||
|
const potentialVenvPaths = [process.env["VIRTUAL_ENV"], path.join(root, ".venv"), path.join(root, "venv")].filter(
|
||||||
|
(p): p is string => p !== undefined,
|
||||||
|
)
|
||||||
|
for (const venvPath of potentialVenvPaths) {
|
||||||
|
const isWindows = process.platform === "win32"
|
||||||
|
const potentialPythonPath = isWindows
|
||||||
|
? path.join(venvPath, "Scripts", "python.exe")
|
||||||
|
: path.join(venvPath, "bin", "python")
|
||||||
|
if (await Bun.file(potentialPythonPath).exists()) {
|
||||||
|
initialization["pythonPath"] = potentialPythonPath
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!binary) {
|
||||||
|
for (const venvPath of potentialVenvPaths) {
|
||||||
|
const isWindows = process.platform === "win32"
|
||||||
|
const potentialTyPath = isWindows
|
||||||
|
? path.join(venvPath, "Scripts", "ty.exe")
|
||||||
|
: path.join(venvPath, "bin", "ty")
|
||||||
|
if (await Bun.file(potentialTyPath).exists()) {
|
||||||
|
binary = potentialTyPath
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!binary) {
|
||||||
|
log.error("ty not found, please install ty first")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const proc = spawn(binary, ["server"], {
|
||||||
|
cwd: root,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
process: proc,
|
||||||
|
initialization,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export const Pyright: Info = {
|
export const Pyright: Info = {
|
||||||
id: "pyright",
|
id: "pyright",
|
||||||
extensions: [".py", ".pyi"],
|
extensions: [".py", ".pyi"],
|
||||||
@@ -562,7 +697,7 @@ export namespace LSPServer {
|
|||||||
})
|
})
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
} else {
|
} else {
|
||||||
await $`tar -xf ${tempPath}`.cwd(Global.Path.bin).nothrow()
|
await $`tar -xf ${tempPath}`.cwd(Global.Path.bin).quiet().nothrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.rm(tempPath, { force: true })
|
await fs.rm(tempPath, { force: true })
|
||||||
@@ -575,7 +710,7 @@ export namespace LSPServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await $`chmod +x ${bin}`.nothrow()
|
await $`chmod +x ${bin}`.quiet().nothrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`installed zls`, { bin })
|
log.info(`installed zls`, { bin })
|
||||||
@@ -868,7 +1003,7 @@ export namespace LSPServer {
|
|||||||
if (!ok) return
|
if (!ok) return
|
||||||
}
|
}
|
||||||
if (tar) {
|
if (tar) {
|
||||||
await $`tar -xf ${archive}`.cwd(Global.Path.bin).nothrow()
|
await $`tar -xf ${archive}`.cwd(Global.Path.bin).quiet().nothrow()
|
||||||
}
|
}
|
||||||
await fs.rm(archive, { force: true })
|
await fs.rm(archive, { force: true })
|
||||||
|
|
||||||
@@ -879,7 +1014,7 @@ export namespace LSPServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await $`chmod +x ${bin}`.nothrow()
|
await $`chmod +x ${bin}`.quiet().nothrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.unlink(path.join(Global.Path.bin, "clangd")).catch(() => {})
|
await fs.unlink(path.join(Global.Path.bin, "clangd")).catch(() => {})
|
||||||
@@ -1445,7 +1580,7 @@ export namespace LSPServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await $`chmod +x ${bin}`.nothrow()
|
await $`chmod +x ${bin}`.quiet().nothrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`installed terraform-ls`, { bin })
|
log.info(`installed terraform-ls`, { bin })
|
||||||
@@ -1528,7 +1663,7 @@ export namespace LSPServer {
|
|||||||
if (!ok) return
|
if (!ok) return
|
||||||
}
|
}
|
||||||
if (ext === "tar.gz") {
|
if (ext === "tar.gz") {
|
||||||
await $`tar -xzf ${tempPath}`.cwd(Global.Path.bin).nothrow()
|
await $`tar -xzf ${tempPath}`.cwd(Global.Path.bin).quiet().nothrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.rm(tempPath, { force: true })
|
await fs.rm(tempPath, { force: true })
|
||||||
@@ -1541,7 +1676,7 @@ export namespace LSPServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await $`chmod +x ${bin}`.nothrow()
|
await $`chmod +x ${bin}`.quiet().nothrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("installed texlab", { bin })
|
log.info("installed texlab", { bin })
|
||||||
@@ -1611,4 +1746,150 @@ export namespace LSPServer {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Clojure: Info = {
|
||||||
|
id: "clojure-lsp",
|
||||||
|
extensions: [".clj", ".cljs", ".cljc", ".edn"],
|
||||||
|
root: NearestRoot(["deps.edn", "project.clj", "shadow-cljs.edn", "bb.edn", "build.boot"]),
|
||||||
|
async spawn(root) {
|
||||||
|
let bin = Bun.which("clojure-lsp")
|
||||||
|
if (!bin && process.platform === "win32") {
|
||||||
|
bin = Bun.which("clojure-lsp.exe")
|
||||||
|
}
|
||||||
|
if (!bin) {
|
||||||
|
log.info("clojure-lsp not found, please install clojure-lsp first")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
process: spawn(bin, ["listen"], {
|
||||||
|
cwd: root,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Nixd: Info = {
|
||||||
|
id: "nixd",
|
||||||
|
extensions: [".nix"],
|
||||||
|
root: async (file) => {
|
||||||
|
// First, look for flake.nix - the most reliable Nix project root indicator
|
||||||
|
const flakeRoot = await NearestRoot(["flake.nix"])(file)
|
||||||
|
if (flakeRoot && flakeRoot !== Instance.directory) return flakeRoot
|
||||||
|
|
||||||
|
// If no flake.nix, fall back to git repository root
|
||||||
|
if (Instance.worktree && Instance.worktree !== Instance.directory) return Instance.worktree
|
||||||
|
|
||||||
|
// Finally, use the instance directory as fallback
|
||||||
|
return Instance.directory
|
||||||
|
},
|
||||||
|
async spawn(root) {
|
||||||
|
const nixd = Bun.which("nixd")
|
||||||
|
if (!nixd) {
|
||||||
|
log.info("nixd not found, please install nixd first")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
process: spawn(nixd, [], {
|
||||||
|
cwd: root,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tinymist: Info = {
|
||||||
|
id: "tinymist",
|
||||||
|
extensions: [".typ", ".typc"],
|
||||||
|
root: NearestRoot(["typst.toml"]),
|
||||||
|
async spawn(root) {
|
||||||
|
let bin = Bun.which("tinymist", {
|
||||||
|
PATH: process.env["PATH"] + path.delimiter + Global.Path.bin,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!bin) {
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
|
log.info("downloading tinymist from GitHub releases")
|
||||||
|
|
||||||
|
const response = await fetch("https://api.github.com/repos/Myriad-Dreamin/tinymist/releases/latest")
|
||||||
|
if (!response.ok) {
|
||||||
|
log.error("Failed to fetch tinymist release info")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const release = (await response.json()) as {
|
||||||
|
tag_name?: string
|
||||||
|
assets?: { name?: string; browser_download_url?: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const platform = process.platform
|
||||||
|
const arch = process.arch
|
||||||
|
|
||||||
|
const tinymistArch = arch === "arm64" ? "aarch64" : "x86_64"
|
||||||
|
let tinymistPlatform: string
|
||||||
|
let ext: string
|
||||||
|
|
||||||
|
if (platform === "darwin") {
|
||||||
|
tinymistPlatform = "apple-darwin"
|
||||||
|
ext = "tar.gz"
|
||||||
|
} else if (platform === "win32") {
|
||||||
|
tinymistPlatform = "pc-windows-msvc"
|
||||||
|
ext = "zip"
|
||||||
|
} else {
|
||||||
|
tinymistPlatform = "unknown-linux-gnu"
|
||||||
|
ext = "tar.gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
const assetName = `tinymist-${tinymistArch}-${tinymistPlatform}.${ext}`
|
||||||
|
|
||||||
|
const assets = release.assets ?? []
|
||||||
|
const asset = assets.find((a) => a.name === assetName)
|
||||||
|
if (!asset?.browser_download_url) {
|
||||||
|
log.error(`Could not find asset ${assetName} in tinymist release`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadResponse = await fetch(asset.browser_download_url)
|
||||||
|
if (!downloadResponse.ok) {
|
||||||
|
log.error("Failed to download tinymist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempPath = path.join(Global.Path.bin, assetName)
|
||||||
|
await Bun.file(tempPath).write(downloadResponse)
|
||||||
|
|
||||||
|
if (ext === "zip") {
|
||||||
|
const ok = await Archive.extractZip(tempPath, Global.Path.bin)
|
||||||
|
.then(() => true)
|
||||||
|
.catch((error) => {
|
||||||
|
log.error("Failed to extract tinymist archive", { error })
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if (!ok) return
|
||||||
|
} else {
|
||||||
|
await $`tar -xzf ${tempPath} --strip-components=1`.cwd(Global.Path.bin).quiet().nothrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.rm(tempPath, { force: true })
|
||||||
|
|
||||||
|
bin = path.join(Global.Path.bin, "tinymist" + (platform === "win32" ? ".exe" : ""))
|
||||||
|
|
||||||
|
if (!(await Bun.file(bin).exists())) {
|
||||||
|
log.error("Failed to extract tinymist binary")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform !== "win32") {
|
||||||
|
await $`chmod +x ${bin}`.quiet().nothrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("installed tinymist", { bin })
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
process: spawn(bin, { cwd: root }),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { type Tool } from "ai"
|
import { dynamicTool, type Tool, jsonSchema, type JSONSchema7 } from "ai"
|
||||||
import { experimental_createMCPClient } from "@ai-sdk/mcp"
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
||||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
||||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
||||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
||||||
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
||||||
|
import type { Tool as MCPToolDef } from "@modelcontextprotocol/sdk/types.js"
|
||||||
import { Config } from "../config/config"
|
import { Config } from "../config/config"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { NamedError } from "@opencode-ai/util/error"
|
import { NamedError } from "@opencode-ai/util/error"
|
||||||
import z from "zod/v4"
|
import z from "zod/v4"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
|
import { Installation } from "../installation"
|
||||||
import { withTimeout } from "@/util/timeout"
|
import { withTimeout } from "@/util/timeout"
|
||||||
import { McpOAuthProvider } from "./oauth-provider"
|
import { McpOAuthProvider } from "./oauth-provider"
|
||||||
import { McpOAuthCallback } from "./oauth-callback"
|
import { McpOAuthCallback } from "./oauth-callback"
|
||||||
@@ -25,7 +27,7 @@ export namespace MCP {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client = Awaited<ReturnType<typeof experimental_createMCPClient>>
|
type MCPClient = Client
|
||||||
|
|
||||||
export const Status = z
|
export const Status = z
|
||||||
.discriminatedUnion("status", [
|
.discriminatedUnion("status", [
|
||||||
@@ -71,7 +73,30 @@ export namespace MCP {
|
|||||||
ref: "MCPStatus",
|
ref: "MCPStatus",
|
||||||
})
|
})
|
||||||
export type Status = z.infer<typeof Status>
|
export type Status = z.infer<typeof Status>
|
||||||
type MCPClient = Awaited<ReturnType<typeof experimental_createMCPClient>>
|
|
||||||
|
// Convert MCP tool definition to AI SDK Tool type
|
||||||
|
function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient): Tool {
|
||||||
|
const inputSchema = mcpTool.inputSchema
|
||||||
|
|
||||||
|
// Spread first, then override type to ensure it's always "object"
|
||||||
|
const schema: JSONSchema7 = {
|
||||||
|
...(inputSchema as JSONSchema7),
|
||||||
|
type: "object",
|
||||||
|
properties: (inputSchema.properties ?? {}) as JSONSchema7["properties"],
|
||||||
|
additionalProperties: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
return dynamicTool({
|
||||||
|
description: mcpTool.description ?? "",
|
||||||
|
inputSchema: jsonSchema(schema),
|
||||||
|
execute: async (args: unknown) => {
|
||||||
|
return client.callTool({
|
||||||
|
name: mcpTool.name,
|
||||||
|
arguments: args as Record<string, unknown>,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Store transports for OAuth servers to allow finishing auth
|
// Store transports for OAuth servers to allow finishing auth
|
||||||
type TransportWithAuth = StreamableHTTPClientTransport | SSEClientTransport
|
type TransportWithAuth = StreamableHTTPClientTransport | SSEClientTransport
|
||||||
@@ -81,7 +106,7 @@ export namespace MCP {
|
|||||||
async () => {
|
async () => {
|
||||||
const cfg = await Config.get()
|
const cfg = await Config.get()
|
||||||
const config = cfg.mcp ?? {}
|
const config = cfg.mcp ?? {}
|
||||||
const clients: Record<string, Client> = {}
|
const clients: Record<string, MCPClient> = {}
|
||||||
const status: Record<string, Status> = {}
|
const status: Record<string, Status> = {}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@@ -204,10 +229,12 @@ export namespace MCP {
|
|||||||
let lastError: Error | undefined
|
let lastError: Error | undefined
|
||||||
for (const { name, transport } of transports) {
|
for (const { name, transport } of transports) {
|
||||||
try {
|
try {
|
||||||
mcpClient = await experimental_createMCPClient({
|
const client = new Client({
|
||||||
name: "opencode",
|
name: "opencode",
|
||||||
transport,
|
version: Installation.VERSION,
|
||||||
})
|
})
|
||||||
|
await client.connect(transport)
|
||||||
|
mcpClient = client
|
||||||
log.info("connected", { key, transport: name })
|
log.info("connected", { key, transport: name })
|
||||||
status = { status: "connected" }
|
status = { status: "connected" }
|
||||||
break
|
break
|
||||||
@@ -248,36 +275,38 @@ export namespace MCP {
|
|||||||
|
|
||||||
if (mcp.type === "local") {
|
if (mcp.type === "local") {
|
||||||
const [cmd, ...args] = mcp.command
|
const [cmd, ...args] = mcp.command
|
||||||
await experimental_createMCPClient({
|
const transport = new StdioClientTransport({
|
||||||
name: "opencode",
|
stderr: "ignore",
|
||||||
transport: new StdioClientTransport({
|
command: cmd,
|
||||||
stderr: "ignore",
|
args,
|
||||||
command: cmd,
|
env: {
|
||||||
args,
|
...process.env,
|
||||||
env: {
|
...(cmd === "opencode" ? { BUN_BE_BUN: "1" } : {}),
|
||||||
...process.env,
|
...mcp.environment,
|
||||||
...(cmd === "opencode" ? { BUN_BE_BUN: "1" } : {}),
|
},
|
||||||
...mcp.environment,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.then((client) => {
|
|
||||||
mcpClient = client
|
try {
|
||||||
status = {
|
const client = new Client({
|
||||||
status: "connected",
|
name: "opencode",
|
||||||
}
|
version: Installation.VERSION,
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
await client.connect(transport)
|
||||||
log.error("local mcp startup failed", {
|
mcpClient = client
|
||||||
key,
|
status = {
|
||||||
command: mcp.command,
|
status: "connected",
|
||||||
error: error instanceof Error ? error.message : String(error),
|
}
|
||||||
})
|
} catch (error) {
|
||||||
status = {
|
log.error("local mcp startup failed", {
|
||||||
status: "failed" as const,
|
key,
|
||||||
error: error instanceof Error ? error.message : String(error),
|
command: mcp.command,
|
||||||
}
|
error: error instanceof Error ? error.message : String(error),
|
||||||
})
|
})
|
||||||
|
status = {
|
||||||
|
status: "failed" as const,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@@ -294,7 +323,7 @@ export namespace MCP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch((err) => {
|
const result = await withTimeout(mcpClient.listTools(), mcp.timeout ?? 5000).catch((err) => {
|
||||||
log.error("failed to get tools from client", { key, error: err })
|
log.error("failed to get tools from client", { key, error: err })
|
||||||
return undefined
|
return undefined
|
||||||
})
|
})
|
||||||
@@ -317,7 +346,7 @@ export namespace MCP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("create() successfully created client", { key, toolCount: Object.keys(result).length })
|
log.info("create() successfully created client", { key, toolCount: result.tools.length })
|
||||||
return {
|
return {
|
||||||
mcpClient,
|
mcpClient,
|
||||||
status,
|
status,
|
||||||
@@ -392,7 +421,7 @@ export namespace MCP {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const tools = await client.tools().catch((e) => {
|
const toolsResult = await client.listTools().catch((e) => {
|
||||||
log.error("failed to get tools", { clientName, error: e.message })
|
log.error("failed to get tools", { clientName, error: e.message })
|
||||||
const failedStatus = {
|
const failedStatus = {
|
||||||
status: "failed" as const,
|
status: "failed" as const,
|
||||||
@@ -400,14 +429,15 @@ export namespace MCP {
|
|||||||
}
|
}
|
||||||
s.status[clientName] = failedStatus
|
s.status[clientName] = failedStatus
|
||||||
delete s.clients[clientName]
|
delete s.clients[clientName]
|
||||||
|
return undefined
|
||||||
})
|
})
|
||||||
if (!tools) {
|
if (!toolsResult) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (const [toolName, tool] of Object.entries(tools)) {
|
for (const mcpTool of toolsResult.tools) {
|
||||||
const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
|
const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||||
const sanitizedToolName = toolName.replace(/[^a-zA-Z0-9_-]/g, "_")
|
const sanitizedToolName = mcpTool.name.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||||
result[sanitizedClientName + "_" + sanitizedToolName] = tool
|
result[sanitizedClientName + "_" + sanitizedToolName] = convertMcpTool(mcpTool, client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -469,10 +499,11 @@ export namespace MCP {
|
|||||||
|
|
||||||
// Try to connect - this will trigger the OAuth flow
|
// Try to connect - this will trigger the OAuth flow
|
||||||
try {
|
try {
|
||||||
await experimental_createMCPClient({
|
const client = new Client({
|
||||||
name: "opencode",
|
name: "opencode",
|
||||||
transport,
|
version: Installation.VERSION,
|
||||||
})
|
})
|
||||||
|
await client.connect(transport)
|
||||||
// If we get here, we're already authenticated
|
// If we get here, we're already authenticated
|
||||||
return { authorizationUrl: "" }
|
return { authorizationUrl: "" }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -25,6 +25,15 @@ import { createOpenAI } from "@ai-sdk/openai"
|
|||||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
|
||||||
import { createOpenRouter, type LanguageModelV2 } from "@openrouter/ai-sdk-provider"
|
import { createOpenRouter, type LanguageModelV2 } from "@openrouter/ai-sdk-provider"
|
||||||
import { createOpenaiCompatible as createGitHubCopilotOpenAICompatible } from "./sdk/openai-compatible/src"
|
import { createOpenaiCompatible as createGitHubCopilotOpenAICompatible } from "./sdk/openai-compatible/src"
|
||||||
|
import { createXai } from "@ai-sdk/xai"
|
||||||
|
import { createMistral } from "@ai-sdk/mistral"
|
||||||
|
import { createGroq } from "@ai-sdk/groq"
|
||||||
|
import { createDeepInfra } from "@ai-sdk/deepinfra"
|
||||||
|
import { createCerebras } from "@ai-sdk/cerebras"
|
||||||
|
import { createCohere } from "@ai-sdk/cohere"
|
||||||
|
import { createGateway } from "@ai-sdk/gateway"
|
||||||
|
import { createTogetherAI } from "@ai-sdk/togetherai"
|
||||||
|
import { createPerplexity } from "@ai-sdk/perplexity"
|
||||||
|
|
||||||
export namespace Provider {
|
export namespace Provider {
|
||||||
const log = Log.create({ service: "provider" })
|
const log = Log.create({ service: "provider" })
|
||||||
@@ -39,6 +48,15 @@ export namespace Provider {
|
|||||||
"@ai-sdk/openai": createOpenAI,
|
"@ai-sdk/openai": createOpenAI,
|
||||||
"@ai-sdk/openai-compatible": createOpenAICompatible,
|
"@ai-sdk/openai-compatible": createOpenAICompatible,
|
||||||
"@openrouter/ai-sdk-provider": createOpenRouter,
|
"@openrouter/ai-sdk-provider": createOpenRouter,
|
||||||
|
"@ai-sdk/xai": createXai,
|
||||||
|
"@ai-sdk/mistral": createMistral,
|
||||||
|
"@ai-sdk/groq": createGroq,
|
||||||
|
"@ai-sdk/deepinfra": createDeepInfra,
|
||||||
|
"@ai-sdk/cerebras": createCerebras,
|
||||||
|
"@ai-sdk/cohere": createCohere,
|
||||||
|
"@ai-sdk/gateway": createGateway,
|
||||||
|
"@ai-sdk/togetherai": createTogetherAI,
|
||||||
|
"@ai-sdk/perplexity": createPerplexity,
|
||||||
// @ts-ignore (TODO: kill this code so we dont have to maintain it)
|
// @ts-ignore (TODO: kill this code so we dont have to maintain it)
|
||||||
"@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible,
|
"@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible,
|
||||||
}
|
}
|
||||||
@@ -67,6 +85,8 @@ export namespace Provider {
|
|||||||
const env = Env.all()
|
const env = Env.all()
|
||||||
if (input.env.some((item) => env[item])) return true
|
if (input.env.some((item) => env[item])) return true
|
||||||
if (await Auth.get(input.id)) return true
|
if (await Auth.get(input.id)) return true
|
||||||
|
const config = await Config.get()
|
||||||
|
if (config.provider?.["opencode"]?.options?.apiKey) return true
|
||||||
return false
|
return false
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
|||||||
@@ -74,17 +74,10 @@ export namespace ProviderTransform {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rm later
|
|
||||||
const bugged =
|
|
||||||
(model.id === "kimi-k2-thinking" && model.providerID === "opencode") ||
|
|
||||||
(model.id === "moonshotai/Kimi-K2-Thinking" && model.providerID === "baseten")
|
|
||||||
if (
|
if (
|
||||||
model.providerID === "deepseek" ||
|
model.capabilities.interleaved &&
|
||||||
model.api.id.toLowerCase().includes("deepseek") ||
|
typeof model.capabilities.interleaved === "object" &&
|
||||||
(model.capabilities.interleaved &&
|
model.capabilities.interleaved.field === "reasoning_content"
|
||||||
typeof model.capabilities.interleaved === "object" &&
|
|
||||||
model.capabilities.interleaved.field === "reasoning_content" &&
|
|
||||||
!bugged)
|
|
||||||
) {
|
) {
|
||||||
return msgs.map((msg) => {
|
return msgs.map((msg) => {
|
||||||
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
||||||
@@ -224,23 +217,26 @@ export namespace ProviderTransform {
|
|||||||
if (id.includes("gemini-3-pro")) return 1.0
|
if (id.includes("gemini-3-pro")) return 1.0
|
||||||
if (id.includes("glm-4.6")) return 1.0
|
if (id.includes("glm-4.6")) return 1.0
|
||||||
if (id.includes("minimax-m2")) return 1.0
|
if (id.includes("minimax-m2")) return 1.0
|
||||||
// if (id.includes("kimi-k2")) {
|
if (id.includes("kimi-k2")) {
|
||||||
// if (id.includes("thinking")) return 1.0
|
if (id.includes("thinking")) return 1.0
|
||||||
// return 0.6
|
return 0.6
|
||||||
// }
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function topP(model: Provider.Model) {
|
export function topP(model: Provider.Model) {
|
||||||
const id = model.id.toLowerCase()
|
const id = model.id.toLowerCase()
|
||||||
if (id.includes("qwen")) return 1
|
if (id.includes("qwen")) return 1
|
||||||
if (id.includes("minimax-m2")) return 0.95
|
if (id.includes("minimax-m2")) {
|
||||||
|
if (id.includes("m2.1")) return 0.9
|
||||||
|
return 0.95
|
||||||
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function topK(model: Provider.Model) {
|
export function topK(model: Provider.Model) {
|
||||||
const id = model.id.toLowerCase()
|
const id = model.id.toLowerCase()
|
||||||
if (id.includes("minimax-m2")) return 40
|
if (id.includes("minimax-m2")) return 20
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,6 +427,10 @@ export namespace ProviderTransform {
|
|||||||
result.required = result.required.filter((field: any) => field in result.properties)
|
result.required = result.required.filter((field: any) => field in result.properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.type === "array" && result.items == null) {
|
||||||
|
result.items = {}
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1054,17 +1054,18 @@ export namespace Server {
|
|||||||
z.object({
|
z.object({
|
||||||
providerID: z.string(),
|
providerID: z.string(),
|
||||||
modelID: z.string(),
|
modelID: z.string(),
|
||||||
|
auto: z.boolean().optional().default(false),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
const body = c.req.valid("json")
|
const body = c.req.valid("json")
|
||||||
const msgs = await Session.messages({ sessionID })
|
const msgs = await Session.messages({ sessionID })
|
||||||
let currentAgent = "build"
|
let currentAgent = await Agent.defaultAgent()
|
||||||
for (let i = msgs.length - 1; i >= 0; i--) {
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
||||||
const info = msgs[i].info
|
const info = msgs[i].info
|
||||||
if (info.role === "user") {
|
if (info.role === "user") {
|
||||||
currentAgent = info.agent || "build"
|
currentAgent = info.agent || (await Agent.defaultAgent())
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1075,7 +1076,7 @@ export namespace Server {
|
|||||||
providerID: body.providerID,
|
providerID: body.providerID,
|
||||||
modelID: body.modelID,
|
modelID: body.modelID,
|
||||||
},
|
},
|
||||||
auto: false,
|
auto: body.auto,
|
||||||
})
|
})
|
||||||
await SessionPrompt.loop(sessionID)
|
await SessionPrompt.loop(sessionID)
|
||||||
return c.json(true)
|
return c.json(true)
|
||||||
@@ -1188,6 +1189,79 @@ export namespace Server {
|
|||||||
return c.json(message)
|
return c.json(message)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.delete(
|
||||||
|
"/session/:sessionID/message/:messageID/part/:partID",
|
||||||
|
describeRoute({
|
||||||
|
description: "Delete a part from a message",
|
||||||
|
operationId: "part.delete",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Successfully deleted part",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(z.boolean()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400, 404),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator(
|
||||||
|
"param",
|
||||||
|
z.object({
|
||||||
|
sessionID: z.string().meta({ description: "Session ID" }),
|
||||||
|
messageID: z.string().meta({ description: "Message ID" }),
|
||||||
|
partID: z.string().meta({ description: "Part ID" }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
async (c) => {
|
||||||
|
const params = c.req.valid("param")
|
||||||
|
await Session.removePart({
|
||||||
|
sessionID: params.sessionID,
|
||||||
|
messageID: params.messageID,
|
||||||
|
partID: params.partID,
|
||||||
|
})
|
||||||
|
return c.json(true)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.patch(
|
||||||
|
"/session/:sessionID/message/:messageID/part/:partID",
|
||||||
|
describeRoute({
|
||||||
|
description: "Update a part in a message",
|
||||||
|
operationId: "part.update",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Successfully updated part",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(MessageV2.Part),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400, 404),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator(
|
||||||
|
"param",
|
||||||
|
z.object({
|
||||||
|
sessionID: z.string().meta({ description: "Session ID" }),
|
||||||
|
messageID: z.string().meta({ description: "Message ID" }),
|
||||||
|
partID: z.string().meta({ description: "Part ID" }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
validator("json", MessageV2.Part),
|
||||||
|
async (c) => {
|
||||||
|
const params = c.req.valid("param")
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
if (body.id !== params.partID || body.messageID !== params.messageID || body.sessionID !== params.sessionID) {
|
||||||
|
throw new Error(
|
||||||
|
`Part mismatch: body.id='${body.id}' vs partID='${params.partID}', body.messageID='${body.messageID}' vs messageID='${params.messageID}', body.sessionID='${body.sessionID}' vs sessionID='${params.sessionID}'`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const part = await Session.updatePart(body)
|
||||||
|
return c.json(part)
|
||||||
|
},
|
||||||
|
)
|
||||||
.post(
|
.post(
|
||||||
"/session/:sessionID/message",
|
"/session/:sessionID/message",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
@@ -2528,13 +2602,19 @@ export namespace Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function listen(opts: { port: number; hostname: string }) {
|
export function listen(opts: { port: number; hostname: string }) {
|
||||||
const server = Bun.serve({
|
const args = {
|
||||||
port: opts.port,
|
|
||||||
hostname: opts.hostname,
|
hostname: opts.hostname,
|
||||||
idleTimeout: 0,
|
idleTimeout: 0,
|
||||||
fetch: App().fetch,
|
fetch: App().fetch,
|
||||||
websocket: websocket,
|
websocket: websocket,
|
||||||
})
|
} as const
|
||||||
return server
|
if (opts.port === 0) {
|
||||||
|
try {
|
||||||
|
return Bun.serve({ ...args, port: 4096 })
|
||||||
|
} catch {
|
||||||
|
// port 4096 not available, fall through to use port 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Bun.serve({ ...args, port: opts.port })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ export namespace SessionCompaction {
|
|||||||
export const PRUNE_MINIMUM = 20_000
|
export const PRUNE_MINIMUM = 20_000
|
||||||
export const PRUNE_PROTECT = 40_000
|
export const PRUNE_PROTECT = 40_000
|
||||||
|
|
||||||
|
const PRUNE_PROTECTED_TOOLS = ["skill"]
|
||||||
|
|
||||||
// goes backwards through parts until there are 40_000 tokens worth of tool
|
// goes backwards through parts until there are 40_000 tokens worth of tool
|
||||||
// calls. then erases output of previous tool calls. idea is to throw away old
|
// calls. then erases output of previous tool calls. idea is to throw away old
|
||||||
// tool calls that are no longer relevant.
|
// tool calls that are no longer relevant.
|
||||||
@@ -61,6 +63,8 @@ export namespace SessionCompaction {
|
|||||||
const part = msg.parts[partIndex]
|
const part = msg.parts[partIndex]
|
||||||
if (part.type === "tool")
|
if (part.type === "tool")
|
||||||
if (part.state.status === "completed") {
|
if (part.state.status === "completed") {
|
||||||
|
if (PRUNE_PROTECTED_TOOLS.includes(part.tool)) continue
|
||||||
|
|
||||||
if (part.state.time.compacted) break loop
|
if (part.state.time.compacted) break loop
|
||||||
const estimate = Token.estimate(part.state.output)
|
const estimate = Token.estimate(part.state.output)
|
||||||
total += estimate
|
total += estimate
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user