Compare commits
241
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6d646b6e6 | ||
|
|
44555cf4f4 | ||
|
|
ca14987c8e | ||
|
|
f9fcdead55 | ||
|
|
ff669d4414 | ||
|
|
9b2d595cfc | ||
|
|
3839d70a94 | ||
|
|
6fe265e7d8 | ||
|
|
2aed4d263b | ||
|
|
e2ac588c84 | ||
|
|
8917dfdf5e | ||
|
|
86900d71f5 | ||
|
|
adcc661798 | ||
|
|
f4a28b2659 | ||
|
|
a160a35d0c | ||
|
|
90eaf9b3fc | ||
|
|
16d516dbdb | ||
|
|
0026bc5815 | ||
|
|
bcdaf7e779 | ||
|
|
874e22a045 | ||
|
|
905226c01e | ||
|
|
73adf7e86f | ||
|
|
4c37e17ac2 | ||
|
|
cd6e07355b | ||
|
|
29703aee9a | ||
|
|
3997d3f2d7 | ||
|
|
1fccb3bda4 | ||
|
|
16b2bfa8ef | ||
|
|
4eb6b57503 | ||
|
|
7599396162 | ||
|
|
d99d1315ee | ||
|
|
d831432f93 | ||
|
|
0ddf8e6c6e | ||
|
|
a520c4ff98 | ||
|
|
a184714f67 | ||
|
|
9b76337236 | ||
|
|
4dc72669e5 | ||
|
|
dfa59dd21d | ||
|
|
f642a6c5b9 | ||
|
|
e37104cb10 | ||
|
|
dc654c93d2 | ||
|
|
c67b0a9ba4 | ||
|
|
5b699a0d9b | ||
|
|
bc557e828d | ||
|
|
fcaa041ef9 | ||
|
|
3c9d80d75f | ||
|
|
a761f66a16 | ||
|
|
15e80fca69 | ||
|
|
43680534df | ||
|
|
aa522aad62 | ||
|
|
82319bbd83 | ||
|
|
45fa4eda15 | ||
|
|
f242541ef3 | ||
|
|
562f067131 | ||
|
|
1ff46c75fa | ||
|
|
73d5cacc06 | ||
|
|
b8828f2609 | ||
|
|
2f7b2cf603 | ||
|
|
eaf18d9915 | ||
|
|
7aa7dd3690 | ||
|
|
bee4b6801e | ||
|
|
3565d8e44d | ||
|
|
0187b6bb72 | ||
|
|
0eb898abcf | ||
|
|
5a309c2dbf | ||
|
|
452f11ff77 | ||
|
|
774c24e76e | ||
|
|
ec4a44087b | ||
|
|
501347cda5 | ||
|
|
3f3816c0f2 | ||
|
|
0a3c72d678 | ||
|
|
66b7a4991e | ||
|
|
1550ae47c0 | ||
|
|
33ba064c40 | ||
|
|
96ae5925c3 | ||
|
|
3a750b0809 | ||
|
|
1258f7aeea | ||
|
|
797a56873d | ||
|
|
05867f9318 | ||
|
|
5947fe72e4 | ||
|
|
f3d4dd5099 | ||
|
|
b68a4a8838 | ||
|
|
b7a1d8f2f5 | ||
|
|
80e1173ef7 | ||
|
|
8ae10f1c94 | ||
|
|
f24251f89e | ||
|
|
3600bd27f4 | ||
|
|
92089bb295 | ||
|
|
a70932f742 | ||
|
|
217cf24c3c | ||
|
|
c87939ad12 | ||
|
|
a092f567b7 | ||
|
|
883a6577d5 | ||
|
|
20b52cad2a | ||
|
|
528291532b | ||
|
|
2b77a84c4f | ||
|
|
7d0b52dc29 | ||
|
|
c86c2acf4c | ||
|
|
29bf731d47 | ||
|
|
3c5a256f0f | ||
|
|
067338bc25 | ||
|
|
736cd10847 | ||
|
|
a03daa4252 | ||
|
|
b01eec38d1 | ||
|
|
35cb06e0e4 | ||
|
|
f3b7d2f786 | ||
|
|
1facf7d8e4 | ||
|
|
ddd9c71cca | ||
|
|
520a814fc2 | ||
|
|
2072c8681a | ||
|
|
21990621e2 | ||
|
|
68a0947292 | ||
|
|
f1f44644e2 | ||
|
|
afb8a0d28a | ||
|
|
f05f175842 | ||
|
|
c0b214232d | ||
|
|
f4f8f2d151 | ||
|
|
b4ad5c138e | ||
|
|
789e111a0f | ||
|
|
eaa76dad0c | ||
|
|
66f9bdab32 | ||
|
|
efaa9166fb | ||
|
|
eb2044989e | ||
|
|
5d37e58d34 | ||
|
|
20088a87b0 | ||
|
|
05019dae76 | ||
|
|
bf37a88f7f | ||
|
|
d839f70834 | ||
|
|
498a4ab408 | ||
|
|
835e48cd28 | ||
|
|
1a5a63843e | ||
|
|
d954e1e3b6 | ||
|
|
8f22a6b69d | ||
|
|
fd37d5b54e | ||
|
|
e146083b73 | ||
|
|
d7a1c268d9 | ||
|
|
5c4345da4f | ||
|
|
0be37cc2c6 | ||
|
|
64f0205f97 | ||
|
|
b3a1360ad9 | ||
|
|
08d4d6d4af | ||
|
|
9c69c1de9f | ||
|
|
735f3d17bc | ||
|
|
db7243c364 | ||
|
|
f0912ee838 | ||
|
|
983f8ffeca | ||
|
|
c474380684 | ||
|
|
7ca767de55 | ||
|
|
1954c1255e | ||
|
|
b4f33485a7 | ||
|
|
f2504d8eb2 | ||
|
|
e47f383137 | ||
|
|
cd56845dce | ||
|
|
1c24dd02a6 | ||
|
|
b7b09fdfc2 | ||
|
|
62702fbd11 | ||
|
|
22c68a6992 | ||
|
|
ca1b597b01 | ||
|
|
d527ceeb2b | ||
|
|
2e9c22d911 | ||
|
|
762c58b756 | ||
|
|
61d0b3e4dc | ||
|
|
71a7ad1a4e | ||
|
|
20399bbdfe | ||
|
|
547a975707 | ||
|
|
c009cab15b | ||
|
|
e6bc3b253b | ||
|
|
f1a13f25a4 | ||
|
|
4695e685cf | ||
|
|
c6092e4ad9 | ||
|
|
e6045ca925 | ||
|
|
ebbb4dd88a | ||
|
|
087473be6e | ||
|
|
e0eb460fc8 | ||
|
|
7c6b3f981e | ||
|
|
a5b6c57a76 | ||
|
|
65724b693b | ||
|
|
8b9a85b7e7 | ||
|
|
7cbec9a1a7 | ||
|
|
c4ba5961c8 | ||
|
|
82b432349e | ||
|
|
68ed664a3f | ||
|
|
3a30773874 | ||
|
|
0c0057a7de | ||
|
|
fa79736b87 | ||
|
|
2e0c2c9db7 | ||
|
|
3e9366487a | ||
|
|
025ed04da0 | ||
|
|
b81eca4ebc | ||
|
|
20c18689c0 | ||
|
|
a803cf8aee | ||
|
|
c526e2d908 | ||
|
|
bdbbcd8a0c | ||
|
|
43c2da24d0 | ||
|
|
3205db9c16 | ||
|
|
efbab087df | ||
|
|
9280db3297 | ||
|
|
0cc3c3bc78 | ||
|
|
ee8b38ab26 | ||
|
|
44fa3d5392 | ||
|
|
a457828a67 | ||
|
|
4752c83155 | ||
|
|
f94ee5ce90 | ||
|
|
76386f5cfc | ||
|
|
a9275def43 | ||
|
|
50ed4c6b5d | ||
|
|
f882cca98a | ||
|
|
2d2a044961 | ||
|
|
b41fbda68f | ||
|
|
794c5981a5 | ||
|
|
d5738f542c | ||
|
|
02b7eb59f8 | ||
|
|
a8f23fb548 | ||
|
|
1a642a79a6 | ||
|
|
b6b0097755 | ||
|
|
afb1cad26d | ||
|
|
58186004db | ||
|
|
a593ed4c9b | ||
|
|
449270aacc | ||
|
|
e20535655b | ||
|
|
1662e149b3 | ||
|
|
dfe3e79304 | ||
|
|
e92a2ec9db | ||
|
|
7cba1ff793 | ||
|
|
8c3cc0d447 | ||
|
|
e30562d5f4 | ||
|
|
7c06ef2477 | ||
|
|
58eccf7f53 | ||
|
|
6da60bd5d9 | ||
|
|
8a43c24934 | ||
|
|
b03172d723 | ||
|
|
e342795bd0 | ||
|
|
a44d4acb3f | ||
|
|
8b8a358de1 | ||
|
|
075fa2c0e8 | ||
|
|
8b287caaad | ||
|
|
b3e6b7a985 | ||
|
|
5c74bff8e1 | ||
|
|
172bbdaced | ||
|
|
563b4c33f2 | ||
|
|
982b71e861 |
@@ -92,7 +92,7 @@ jobs:
|
|||||||
|
|
||||||
publish-tauri:
|
publish-tauri:
|
||||||
needs: publish
|
needs: publish
|
||||||
continue-on-error: true
|
continue-on-error: false
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ on:
|
|||||||
- "packages/*/package.json"
|
- "packages/*/package.json"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update:
|
update-linux:
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
env:
|
env:
|
||||||
@@ -47,14 +47,14 @@ jobs:
|
|||||||
nix flake update
|
nix flake update
|
||||||
echo "✅ flake.lock updated successfully"
|
echo "✅ flake.lock updated successfully"
|
||||||
|
|
||||||
- name: Update node_modules hash
|
- name: Update node_modules hash for x86_64-linux
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
echo "🔄 Updating node_modules hash..."
|
echo "🔄 Updating node_modules hash for x86_64-linux..."
|
||||||
nix/scripts/update-hashes.sh
|
nix/scripts/update-hashes.sh
|
||||||
echo "✅ node_modules hash updated successfully"
|
echo "✅ node_modules hash for x86_64-linux updated successfully"
|
||||||
|
|
||||||
- name: Commit hash changes
|
- name: Commit Linux hash changes
|
||||||
env:
|
env:
|
||||||
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
summarize() {
|
summarize() {
|
||||||
local status="$1"
|
local status="$1"
|
||||||
{
|
{
|
||||||
echo "### Nix Hash Update"
|
echo "### Nix Hash Update (x86_64-linux)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "- ref: ${GITHUB_REF_NAME}"
|
echo "- ref: ${GITHUB_REF_NAME}"
|
||||||
echo "- status: ${status}"
|
echo "- status: ${status}"
|
||||||
@@ -89,7 +89,92 @@ jobs:
|
|||||||
echo "🔗 Staging files..."
|
echo "🔗 Staging files..."
|
||||||
git add "${FILES[@]}"
|
git add "${FILES[@]}"
|
||||||
echo "💾 Committing changes..."
|
echo "💾 Committing changes..."
|
||||||
git commit -m "Update Nix flake.lock and hashes"
|
git commit -m "Update Nix flake.lock and x86_64-linux hash"
|
||||||
|
echo "✅ Changes committed"
|
||||||
|
|
||||||
|
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||||
|
echo "🌳 Pulling latest from branch: $BRANCH"
|
||||||
|
git pull --rebase origin "$BRANCH"
|
||||||
|
echo "🚀 Pushing changes to branch: $BRANCH"
|
||||||
|
git push origin HEAD:"$BRANCH"
|
||||||
|
echo "✅ Changes pushed successfully"
|
||||||
|
|
||||||
|
summarize "committed $(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
update-macos:
|
||||||
|
needs: update-linux
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
runs-on: macos-latest
|
||||||
|
env:
|
||||||
|
SYSTEM: aarch64-darwin
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.head_ref || github.ref_name }}
|
||||||
|
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||||
|
|
||||||
|
- name: Setup Nix
|
||||||
|
uses: DeterminateSystems/nix-installer-action@v20
|
||||||
|
|
||||||
|
- name: Configure git
|
||||||
|
run: |
|
||||||
|
git config --global user.email "action@github.com"
|
||||||
|
git config --global user.name "Github Action"
|
||||||
|
|
||||||
|
- name: Pull latest changes
|
||||||
|
env:
|
||||||
|
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||||
|
git pull origin "$BRANCH"
|
||||||
|
|
||||||
|
- name: Update node_modules hash for aarch64-darwin
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
echo "🔄 Updating node_modules hash for aarch64-darwin..."
|
||||||
|
nix/scripts/update-hashes.sh
|
||||||
|
echo "✅ node_modules hash for aarch64-darwin updated successfully"
|
||||||
|
|
||||||
|
- name: Commit macOS hash changes
|
||||||
|
env:
|
||||||
|
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "🔍 Checking for changes in tracked Nix files..."
|
||||||
|
|
||||||
|
summarize() {
|
||||||
|
local status="$1"
|
||||||
|
{
|
||||||
|
echo "### Nix Hash Update (aarch64-darwin)"
|
||||||
|
echo ""
|
||||||
|
echo "- ref: ${GITHUB_REF_NAME}"
|
||||||
|
echo "- status: ${status}"
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
if [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ] && [ -n "${GITHUB_RUN_ID:-}" ]; then
|
||||||
|
echo "- run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
fi
|
||||||
|
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
}
|
||||||
|
|
||||||
|
FILES=(nix/hashes.json)
|
||||||
|
STATUS="$(git status --short -- "${FILES[@]}" || true)"
|
||||||
|
if [ -z "$STATUS" ]; then
|
||||||
|
echo "✅ No changes detected. Hash is already up to date."
|
||||||
|
summarize "no changes"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📝 Changes detected:"
|
||||||
|
echo "$STATUS"
|
||||||
|
echo "🔗 Staging files..."
|
||||||
|
git add "${FILES[@]}"
|
||||||
|
echo "💾 Committing changes..."
|
||||||
|
git commit -m "Update aarch64-darwin hash"
|
||||||
echo "✅ Changes committed"
|
echo "✅ Changes committed"
|
||||||
|
|
||||||
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||||
|
|||||||
@@ -24,3 +24,4 @@ target
|
|||||||
# Local dev files
|
# Local dev files
|
||||||
opencode-dev
|
opencode-dev
|
||||||
logs/
|
logs/
|
||||||
|
*.bun-build
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ Desktop app issues:
|
|||||||
|
|
||||||
#### zen
|
#### zen
|
||||||
|
|
||||||
**Only** add if the issue mentions "zen" or "opencode zen". Zen is our gateway for coding models. **Do not** add for other gateways or inference providers.
|
**Only** add if the issue mentions "zen" or "opencode zen" or "opencode black".
|
||||||
|
|
||||||
If the issue doesn't have "zen" in it then don't add zen label
|
If the issue doesn't have "zen" or "opencode black" in it then don't add zen label
|
||||||
|
|
||||||
#### docs
|
#### docs
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
- To test opencode in the `packages/opencode` directory you can run `bun dev`
|
- To test opencode in `packages/opencode`, run `bun dev`.
|
||||||
- To regenerate the javascript SDK, run ./packages/sdk/js/script/build.ts
|
- To regenerate the JavaScript SDK, run `./packages/sdk/js/script/build.ts`.
|
||||||
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
|
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
|
||||||
- the default branch in this repo is `dev`
|
- The default branch in this repo is `dev`.
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
# Reporting Security Issues
|
||||||
|
|
||||||
|
We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
||||||
|
|
||||||
|
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/anomalyco/opencode/security/advisories/new) tab.
|
||||||
|
|
||||||
|
The team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
||||||
|
|
||||||
|
## Escalation
|
||||||
|
|
||||||
|
If you do not receive an acknowledgement of your report within 6 business days, you may send an email to security@anoma.ly
|
||||||
@@ -196,3 +196,7 @@
|
|||||||
| 2026-01-07 | 2,123,239 (+162,251) | 1,398,648 (+21,271) | 3,521,887 (+183,522) |
|
| 2026-01-07 | 2,123,239 (+162,251) | 1,398,648 (+21,271) | 3,521,887 (+183,522) |
|
||||||
| 2026-01-08 | 2,272,630 (+149,391) | 1,432,480 (+33,832) | 3,705,110 (+183,223) |
|
| 2026-01-08 | 2,272,630 (+149,391) | 1,432,480 (+33,832) | 3,705,110 (+183,223) |
|
||||||
| 2026-01-09 | 2,443,565 (+170,935) | 1,469,451 (+36,971) | 3,913,016 (+207,906) |
|
| 2026-01-09 | 2,443,565 (+170,935) | 1,469,451 (+36,971) | 3,913,016 (+207,906) |
|
||||||
|
| 2026-01-10 | 2,632,023 (+188,458) | 1,503,670 (+34,219) | 4,135,693 (+222,677) |
|
||||||
|
| 2026-01-11 | 2,836,394 (+204,371) | 1,530,479 (+26,809) | 4,366,873 (+231,180) |
|
||||||
|
| 2026-01-12 | 3,053,594 (+217,200) | 1,553,671 (+23,192) | 4,607,265 (+240,392) |
|
||||||
|
| 2026-01-13 | 3,297,078 (+243,484) | 1,595,062 (+41,391) | 4,892,140 (+284,875) |
|
||||||
|
|||||||
+69
-9
@@ -1,11 +1,71 @@
|
|||||||
## Style Guide
|
## Style Guide
|
||||||
|
|
||||||
- Try to keep things in one function unless composable or reusable
|
- Keep things in one function unless composable or reusable
|
||||||
- AVOID unnecessary destructuring of variables. instead of doing `const { a, b }
|
- Avoid unnecessary destructuring. Instead of `const { a, b } = obj`, use `obj.a` and `obj.b` to preserve context
|
||||||
= obj` just reference it as obj.a and obj.b. this preserves context
|
- Avoid `try`/`catch` where possible
|
||||||
- AVOID `try`/`catch` where possible
|
- Avoid using the `any` type
|
||||||
- AVOID `else` statements
|
- Prefer single word variable names where possible
|
||||||
- AVOID using `any` type
|
- Use Bun APIs when possible, like `Bun.file()`
|
||||||
- AVOID `let` statements
|
|
||||||
- PREFER single word variable names where possible
|
# Avoid let statements
|
||||||
- Use as many bun apis as possible like Bun.file()
|
|
||||||
|
We don't like `let` statements, especially combined with if/else statements.
|
||||||
|
Prefer `const`.
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const foo = condition ? 1 : 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Bad:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
let foo
|
||||||
|
|
||||||
|
if (condition) foo = 1
|
||||||
|
else foo = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
# Avoid else statements
|
||||||
|
|
||||||
|
Prefer early returns or using an `iife` to avoid else statements.
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function foo() {
|
||||||
|
if (condition) return 1
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Bad:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function foo() {
|
||||||
|
if (condition) return 1
|
||||||
|
else return 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Prefer single word naming
|
||||||
|
|
||||||
|
Try your best to find a single word name for your variables, functions, etc.
|
||||||
|
Only use multiple words if you cannot.
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const foo = 1
|
||||||
|
const bar = 2
|
||||||
|
const baz = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Bad:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const fooBar = 1
|
||||||
|
const barBaz = 2
|
||||||
|
const bazFoo = 3
|
||||||
|
```
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"packages/app": {
|
"packages/app": {
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/app": {
|
"packages/console/app": {
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cloudflare/vite-plugin": "1.15.2",
|
"@cloudflare/vite-plugin": "1.15.2",
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
@@ -84,9 +84,12 @@
|
|||||||
"@solidjs/meta": "catalog:",
|
"@solidjs/meta": "catalog:",
|
||||||
"@solidjs/router": "catalog:",
|
"@solidjs/router": "catalog:",
|
||||||
"@solidjs/start": "catalog:",
|
"@solidjs/start": "catalog:",
|
||||||
|
"@stripe/stripe-js": "8.6.1",
|
||||||
"chart.js": "4.5.1",
|
"chart.js": "4.5.1",
|
||||||
"nitro": "3.0.1-alpha.1",
|
"nitro": "3.0.1-alpha.1",
|
||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
|
"solid-list": "0.3.0",
|
||||||
|
"solid-stripe": "0.8.1",
|
||||||
"vite": "catalog:",
|
"vite": "catalog:",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -98,7 +101,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/core": {
|
"packages/console/core": {
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"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",
|
||||||
@@ -125,7 +128,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/function": {
|
"packages/console/function": {
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"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",
|
||||||
@@ -149,7 +152,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/mail": {
|
"packages/console/mail": {
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"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",
|
||||||
@@ -173,7 +176,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/app": "workspace:*",
|
"@opencode-ai/app": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
@@ -202,7 +205,7 @@
|
|||||||
},
|
},
|
||||||
"packages/enterprise": {
|
"packages/enterprise": {
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
@@ -231,7 +234,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "catalog:",
|
"@octokit/rest": "catalog:",
|
||||||
@@ -247,7 +250,7 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -255,26 +258,27 @@
|
|||||||
"@actions/core": "1.11.1",
|
"@actions/core": "1.11.1",
|
||||||
"@actions/github": "6.0.1",
|
"@actions/github": "6.0.1",
|
||||||
"@agentclientprotocol/sdk": "0.5.1",
|
"@agentclientprotocol/sdk": "0.5.1",
|
||||||
"@ai-sdk/amazon-bedrock": "3.0.57",
|
"@ai-sdk/amazon-bedrock": "3.0.73",
|
||||||
"@ai-sdk/anthropic": "2.0.56",
|
"@ai-sdk/anthropic": "2.0.57",
|
||||||
"@ai-sdk/azure": "2.0.82",
|
"@ai-sdk/azure": "2.0.91",
|
||||||
"@ai-sdk/cerebras": "1.0.33",
|
"@ai-sdk/cerebras": "1.0.34",
|
||||||
"@ai-sdk/cohere": "2.0.21",
|
"@ai-sdk/cohere": "2.0.22",
|
||||||
"@ai-sdk/deepinfra": "1.0.30",
|
"@ai-sdk/deepinfra": "1.0.31",
|
||||||
"@ai-sdk/gateway": "2.0.23",
|
"@ai-sdk/gateway": "2.0.25",
|
||||||
"@ai-sdk/google": "2.0.49",
|
"@ai-sdk/google": "2.0.52",
|
||||||
"@ai-sdk/google-vertex": "3.0.81",
|
"@ai-sdk/google-vertex": "3.0.97",
|
||||||
"@ai-sdk/groq": "2.0.33",
|
"@ai-sdk/groq": "2.0.34",
|
||||||
"@ai-sdk/mistral": "2.0.26",
|
"@ai-sdk/mistral": "2.0.27",
|
||||||
"@ai-sdk/openai": "2.0.71",
|
"@ai-sdk/openai": "2.0.89",
|
||||||
"@ai-sdk/openai-compatible": "1.0.29",
|
"@ai-sdk/openai-compatible": "1.0.30",
|
||||||
"@ai-sdk/perplexity": "2.0.22",
|
"@ai-sdk/perplexity": "2.0.23",
|
||||||
"@ai-sdk/provider": "2.0.0",
|
"@ai-sdk/provider": "2.0.1",
|
||||||
"@ai-sdk/provider-utils": "3.0.19",
|
"@ai-sdk/provider-utils": "3.0.20",
|
||||||
"@ai-sdk/togetherai": "1.0.30",
|
"@ai-sdk/togetherai": "1.0.31",
|
||||||
"@ai-sdk/vercel": "1.0.31",
|
"@ai-sdk/vercel": "1.0.31",
|
||||||
"@ai-sdk/xai": "2.0.42",
|
"@ai-sdk/xai": "2.0.51",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
|
"@gitlab/gitlab-ai-provider": "3.1.0",
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
"@modelcontextprotocol/sdk": "1.25.2",
|
"@modelcontextprotocol/sdk": "1.25.2",
|
||||||
@@ -350,7 +354,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
@@ -370,7 +374,7 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hey-api/openapi-ts": "0.88.1",
|
"@hey-api/openapi-ts": "0.88.1",
|
||||||
"@tsconfig/node22": "catalog:",
|
"@tsconfig/node22": "catalog:",
|
||||||
@@ -381,7 +385,7 @@
|
|||||||
},
|
},
|
||||||
"packages/slack": {
|
"packages/slack": {
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@slack/bolt": "^3.17.1",
|
"@slack/bolt": "^3.17.1",
|
||||||
@@ -394,7 +398,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -406,6 +410,7 @@
|
|||||||
"@solid-primitives/resize-observer": "2.1.3",
|
"@solid-primitives/resize-observer": "2.1.3",
|
||||||
"@solidjs/meta": "catalog:",
|
"@solidjs/meta": "catalog:",
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
|
"dompurify": "catalog:",
|
||||||
"fuzzysort": "catalog:",
|
"fuzzysort": "catalog:",
|
||||||
"katex": "0.16.27",
|
"katex": "0.16.27",
|
||||||
"luxon": "catalog:",
|
"luxon": "catalog:",
|
||||||
@@ -433,7 +438,7 @@
|
|||||||
},
|
},
|
||||||
"packages/util": {
|
"packages/util": {
|
||||||
"name": "@opencode-ai/util",
|
"name": "@opencode-ai/util",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -444,7 +449,7 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"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,8 +509,9 @@
|
|||||||
"@types/luxon": "3.7.1",
|
"@types/luxon": "3.7.1",
|
||||||
"@types/node": "22.13.9",
|
"@types/node": "22.13.9",
|
||||||
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
||||||
"ai": "5.0.97",
|
"ai": "5.0.119",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
|
"dompurify": "3.3.1",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"hono": "4.10.7",
|
"hono": "4.10.7",
|
||||||
"hono-openapi": "1.1.2",
|
"hono-openapi": "1.1.2",
|
||||||
@@ -541,48 +547,52 @@
|
|||||||
|
|
||||||
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.5.1", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg=="],
|
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.5.1", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg=="],
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@3.0.57", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.45", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-mOUSLe+RgZzx0rtL1p9QXmSd/08z1EkBR+vQ1ydpd1t5P0Nx2kB8afiukEgM8nuDvmO9eYQlp7VTy1n5ffPs2g=="],
|
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@3.0.73", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.57", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-EAAGJ/dfbAZaqIhK3w52hq6cftSLZwXdC6uHKh8Cls1T0N4MxS6ykDf54UyFO3bZWkQxR+Mdw1B3qireGOxtJQ=="],
|
||||||
|
|
||||||
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-uyyaO4KhxoIKZztREqLPh+6/K3ZJx/rp72JKoUEL9/kC+vfQTThUfPnY/bUryUpcnawx8IY/tSoYNOi/8PCv7w=="],
|
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-uyyaO4KhxoIKZztREqLPh+6/K3ZJx/rp72JKoUEL9/kC+vfQTThUfPnY/bUryUpcnawx8IY/tSoYNOi/8PCv7w=="],
|
||||||
|
|
||||||
"@ai-sdk/azure": ["@ai-sdk/azure@2.0.82", "", { "dependencies": { "@ai-sdk/openai": "2.0.80", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Bpab51ETBB4adZC1xGMYsryL/CB8j1sA+t5aDqhRv3t3WRLTxhaBDcFKtQTIuxiEQTFosz9Q2xQqdfBvQm5jHw=="],
|
"@ai-sdk/azure": ["@ai-sdk/azure@2.0.91", "", { "dependencies": { "@ai-sdk/openai": "2.0.89", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9tznVSs6LGQNKKxb8pKd7CkBV9yk+a/ENpFicHCj2CmBUKefxzwJ9JbUqrlK3VF6dGZw3LXq0dWxt7/Yekaj1w=="],
|
||||||
|
|
||||||
"@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/cerebras": ["@ai-sdk/cerebras@1.0.34", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XOK0dJsAGoPYi/lfR4KFBi8xhvJ46oCpAxUD6FmJAuJ4eh0qlj5zDt+myvzM8gvN7S6K7zHD+mdWlOPKGQT8Vg=="],
|
||||||
|
|
||||||
"@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/cohere": ["@ai-sdk/cohere@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yJ9kP5cEDJwo8qpITq5TQFD8YNfNtW+HbyvWwrKMbFzmiMvIZuk95HIaFXE7PCTuZsqMA05yYu+qX/vQ3rNKjA=="],
|
||||||
|
|
||||||
"@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/deepinfra": ["@ai-sdk/deepinfra@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-87qFcYNvDF/89hB//MQjYTb3tlsAfmgeZrZ34RESeBTZpSgs0EzYOMqPMwFTHUNp4wteoifikDJbaS/9Da8cfw=="],
|
||||||
|
|
||||||
"@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/gateway": ["@ai-sdk/gateway@2.0.25", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Rq+FX55ne7lMiqai7NcvvDZj4HLsr+hg77WayqmySqc6zhw3tIOLxd4Ty6OpwNj0C0bVMi3iCl2zvJIEirh9XA=="],
|
||||||
|
|
||||||
"@ai-sdk/google": ["@ai-sdk/google@2.0.49", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-efwKk4mOV0SpumUaQskeYABk37FJPmEYwoDJQEjyLRmGSjtHRe9P5Cwof5ffLvaFav2IaJpBGEz98pyTs7oNWA=="],
|
"@ai-sdk/google": ["@ai-sdk/google@2.0.52", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2XUnGi3f7TV4ujoAhA+Fg3idUoG/+Y2xjCRg70a1/m0DH1KSQqYaCboJ1C19y6ZHGdf5KNT20eJdswP6TvrY2g=="],
|
||||||
|
|
||||||
"@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.97", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.57", "@ai-sdk/google": "2.0.52", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-s4tI7Z15i6FlbtCvS4SBRal8wRfkOXJzKxlS6cU4mJW/QfUfoVy4b22836NVNJwDvkG/HkDSfzwm/X8mn46MhA=="],
|
||||||
|
|
||||||
"@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/groq": ["@ai-sdk/groq@2.0.34", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wfCYkVgmVjxNA32T57KbLabVnv9aFUflJ4urJ7eWgTwbnmGQHElCTu+rJ3ydxkXSqxOkXPwMOttDm7XNrvPjmg=="],
|
||||||
|
|
||||||
"@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/mistral": ["@ai-sdk/mistral@2.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-gaptHgaXjMw3+eA0Q4FABcsj5nQNP6EpFaGUR+Pj5WJy7Kn6mApl975/x57224MfeJIShNpt8wFKK3tvh5ewKg=="],
|
||||||
|
|
||||||
"@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/perplexity": ["@ai-sdk/perplexity@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-aiaRvnc6mhQZKhTTSXPCjPH8Iqr5D/PfCN1hgVP/3RGTBbJtsd9HemIBSABeSdAKbsMH/PwJxgnqH75HEamcBA=="],
|
||||||
|
|
||||||
"@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.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="],
|
||||||
|
|
||||||
"@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/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
|
||||||
|
|
||||||
"@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/togetherai": ["@ai-sdk/togetherai@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RlYubjStoZQxna4Ng91Vvo8YskvL7lW9zj68IwZfCnaDBSAp1u6Nhc5BR4ZtKnY6PA3XEtu4bATIQl7yiiQ+Lw=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel": ["@ai-sdk/vercel@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ggvwAMt/KsbqcdR6ILQrjwrRONLV/8aG6rOLbjcOGvV0Ai+WdZRRKQj5nOeQ06PvwVQtKdkp7S4IinpXIhCiHg=="],
|
"@ai-sdk/vercel": ["@ai-sdk/vercel@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ggvwAMt/KsbqcdR6ILQrjwrRONLV/8aG6rOLbjcOGvV0Ai+WdZRRKQj5nOeQ06PvwVQtKdkp7S4IinpXIhCiHg=="],
|
||||||
|
|
||||||
"@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=="],
|
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.51", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-AI3le03qiegkZvn9hpnpDwez49lOvQLj4QUBT8H41SMbrdTYOxn3ktTwrsSu90cNDdzKGMvoH0u2GHju1EdnCg=="],
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
|
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.71.2", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ=="],
|
||||||
|
|
||||||
|
"@anycable/core": ["@anycable/core@0.9.2", "", { "dependencies": { "nanoevents": "^7.0.1" } }, "sha512-x5ZXDcW/N4cxWl93CnbHs/u7qq4793jS2kNPWm+duPrXlrva+ml2ZGT7X9tuOBKzyIHf60zWCdIK7TUgMPAwXA=="],
|
||||||
|
|
||||||
"@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="],
|
"@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="],
|
||||||
|
|
||||||
"@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="],
|
"@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="],
|
||||||
@@ -903,6 +913,10 @@
|
|||||||
|
|
||||||
"@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
|
"@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
|
||||||
|
|
||||||
|
"@gitlab/gitlab-ai-provider": ["@gitlab/gitlab-ai-provider@3.1.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-S0MVXsogrwbOboA/8L0CY5sBXg2HrrO8gdeUeHd9yLZDPsggFD0FzcSuzO5vBO6geUOpruRa8Hqrbb6WWu7Frw=="],
|
||||||
|
|
||||||
|
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
|
||||||
|
|
||||||
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
|
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
|
||||||
|
|
||||||
"@hey-api/codegen-core": ["@hey-api/codegen-core@0.3.3", "", { "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-vArVDtrvdzFewu1hnjUm4jX1NBITlSCeO81EdWq676MxQbyxsGcDPAgohaSA+Wvr4HjPSvsg2/1s2zYxUtXebg=="],
|
"@hey-api/codegen-core": ["@hey-api/codegen-core@0.3.3", "", { "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-vArVDtrvdzFewu1hnjUm4jX1NBITlSCeO81EdWq676MxQbyxsGcDPAgohaSA+Wvr4HjPSvsg2/1s2zYxUtXebg=="],
|
||||||
@@ -1597,6 +1611,8 @@
|
|||||||
|
|
||||||
"@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
|
"@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
|
||||||
|
|
||||||
|
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
|
||||||
|
|
||||||
"@solid-primitives/active-element": ["@solid-primitives/active-element@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9t5K4aR2naVDj950XU8OjnLgOg94a8k5wr6JNOPK+N5ESLsJDq42c1ZP8UKpewi1R+wplMMxiM6OPKRzbxJY7A=="],
|
"@solid-primitives/active-element": ["@solid-primitives/active-element@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9t5K4aR2naVDj950XU8OjnLgOg94a8k5wr6JNOPK+N5ESLsJDq42c1ZP8UKpewi1R+wplMMxiM6OPKRzbxJY7A=="],
|
||||||
|
|
||||||
"@solid-primitives/audio": ["@solid-primitives/audio@1.4.2", "", { "dependencies": { "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-UMD3ORQfI5Ky8yuKPxidDiEazsjv/dsoiKK5yZxLnsgaeNR1Aym3/77h/qT1jBYeXUgj4DX6t7NMpFUSVr14OQ=="],
|
"@solid-primitives/audio": ["@solid-primitives/audio@1.4.2", "", { "dependencies": { "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-UMD3ORQfI5Ky8yuKPxidDiEazsjv/dsoiKK5yZxLnsgaeNR1Aym3/77h/qT1jBYeXUgj4DX6t7NMpFUSVr14OQ=="],
|
||||||
@@ -1649,6 +1665,8 @@
|
|||||||
|
|
||||||
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
||||||
|
|
||||||
|
"@stripe/stripe-js": ["@stripe/stripe-js@8.6.1", "", {}, "sha512-UJ05U2062XDgydbUcETH1AoRQLNhigQ2KmDn1BG8sC3xfzu6JKg95Qt6YozdzFpxl1Npii/02m2LEWFt1RYjVA=="],
|
||||||
|
|
||||||
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
|
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
|
||||||
@@ -1827,6 +1845,8 @@
|
|||||||
|
|
||||||
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
||||||
|
|
||||||
|
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||||
|
|
||||||
"@types/tsscmp": ["@types/tsscmp@1.0.2", "", {}, "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g=="],
|
"@types/tsscmp": ["@types/tsscmp@1.0.2", "", {}, "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g=="],
|
||||||
|
|
||||||
"@types/tunnel": ["@types/tunnel@0.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA=="],
|
"@types/tunnel": ["@types/tunnel@0.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA=="],
|
||||||
@@ -1903,7 +1923,7 @@
|
|||||||
|
|
||||||
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
||||||
|
|
||||||
"ai": ["ai@5.0.97", "", { "dependencies": { "@ai-sdk/gateway": "2.0.12", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8zBx0b/owis4eJI2tAlV8a1Rv0BANmLxontcAelkLNwEHhgfgXeKpDkhNB6OgV+BJSwboIUDkgd9312DdJnCOQ=="],
|
"ai": ["ai@5.0.119", "", { "dependencies": { "@ai-sdk/gateway": "2.0.25", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HUOwhc17fl2SZTJGZyA/99aNu706qKfXaUBCy9vgZiXBwrxg2eTzn2BCz7kmYDsfx6Fg2ACBy2icm41bsDXCTw=="],
|
||||||
|
|
||||||
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||||
|
|
||||||
@@ -2205,6 +2225,8 @@
|
|||||||
|
|
||||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
|
|
||||||
|
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||||
|
|
||||||
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
||||||
|
|
||||||
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
||||||
@@ -2277,6 +2299,8 @@
|
|||||||
|
|
||||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||||
|
|
||||||
|
"dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="],
|
||||||
|
|
||||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||||
|
|
||||||
"dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="],
|
"dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="],
|
||||||
@@ -2309,6 +2333,10 @@
|
|||||||
|
|
||||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
|
"engine.io-client": ["engine.io-client@6.6.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="],
|
||||||
|
|
||||||
|
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
||||||
|
|
||||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||||
@@ -2419,6 +2447,8 @@
|
|||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
|
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||||
|
|
||||||
"file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
|
"file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
|
||||||
|
|
||||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
@@ -2449,6 +2479,8 @@
|
|||||||
|
|
||||||
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
|
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
|
||||||
|
|
||||||
|
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||||
|
|
||||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
||||||
@@ -2471,9 +2503,9 @@
|
|||||||
|
|
||||||
"fuzzysort": ["fuzzysort@3.1.0", "", {}, "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ=="],
|
"fuzzysort": ["fuzzysort@3.1.0", "", {}, "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ=="],
|
||||||
|
|
||||||
"gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
|
"gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
|
||||||
|
|
||||||
"gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
|
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
||||||
|
|
||||||
"gel": ["gel@2.2.0", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ=="],
|
"gel": ["gel@2.2.0", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ=="],
|
||||||
|
|
||||||
@@ -2519,17 +2551,21 @@
|
|||||||
|
|
||||||
"globby": ["globby@11.0.4", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" } }, "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg=="],
|
"globby": ["globby@11.0.4", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" } }, "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg=="],
|
||||||
|
|
||||||
"google-auth-library": ["google-auth-library@9.15.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng=="],
|
"google-auth-library": ["google-auth-library@10.5.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
|
||||||
|
|
||||||
"google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
|
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
||||||
|
|
||||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||||
|
|
||||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||||
|
|
||||||
|
"graphql": ["graphql@16.12.0", "", {}, "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ=="],
|
||||||
|
|
||||||
|
"graphql-request": ["graphql-request@6.1.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", "cross-fetch": "^3.1.5" }, "peerDependencies": { "graphql": "14 - 16" } }, "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw=="],
|
||||||
|
|
||||||
"gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
|
"gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
|
||||||
|
|
||||||
"gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="],
|
"gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
|
||||||
|
|
||||||
"h3": ["h3@2.0.1-rc.4", "", { "dependencies": { "rou3": "^0.7.8", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-vZq8pEUp6THsXKXrUXX44eOqfChic2wVQ1GlSzQCBr7DeFBkfIZAo2WyNND4GSv54TAa0E4LYIK73WSPdgKUgw=="],
|
"h3": ["h3@2.0.1-rc.4", "", { "dependencies": { "rou3": "^0.7.8", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-vZq8pEUp6THsXKXrUXX44eOqfChic2wVQ1GlSzQCBr7DeFBkfIZAo2WyNND4GSv54TAa0E4LYIK73WSPdgKUgw=="],
|
||||||
|
|
||||||
@@ -2755,6 +2791,8 @@
|
|||||||
|
|
||||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||||
|
|
||||||
|
"isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="],
|
||||||
|
|
||||||
"iterate-iterator": ["iterate-iterator@1.0.2", "", {}, "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw=="],
|
"iterate-iterator": ["iterate-iterator@1.0.2", "", {}, "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw=="],
|
||||||
|
|
||||||
"iterate-value": ["iterate-value@1.0.2", "", { "dependencies": { "es-get-iterator": "^1.0.2", "iterate-iterator": "^1.0.1" } }, "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ=="],
|
"iterate-value": ["iterate-value@1.0.2", "", { "dependencies": { "es-get-iterator": "^1.0.2", "iterate-iterator": "^1.0.1" } }, "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ=="],
|
||||||
@@ -2787,6 +2825,8 @@
|
|||||||
|
|
||||||
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
|
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
|
||||||
|
|
||||||
|
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
|
||||||
|
|
||||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||||
|
|
||||||
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
||||||
@@ -3063,6 +3103,8 @@
|
|||||||
|
|
||||||
"named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
|
"named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
|
||||||
|
|
||||||
|
"nanoevents": ["nanoevents@7.0.1", "", {}, "sha512-o6lpKiCxLeijK4hgsqfR6CNToPyRU3keKyyI6uwuHRvpRTbZ0wXw51WRgyldVugZqoJfkGFrjrIenYH3bfEO3Q=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
@@ -3417,6 +3459,8 @@
|
|||||||
|
|
||||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
|
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
|
"rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
|
||||||
|
|
||||||
"rou3": ["rou3@0.7.10", "", {}, "sha512-aoFj6f7MJZ5muJ+Of79nrhs9N3oLGqi2VEMe94Zbkjb6Wupha46EuoYgpWSOZlXww3bbd8ojgXTAA2mzimX5Ww=="],
|
"rou3": ["rou3@0.7.10", "", {}, "sha512-aoFj6f7MJZ5muJ+Of79nrhs9N3oLGqi2VEMe94Zbkjb6Wupha46EuoYgpWSOZlXww3bbd8ojgXTAA2mzimX5Ww=="],
|
||||||
@@ -3503,6 +3547,10 @@
|
|||||||
|
|
||||||
"smol-toml": ["smol-toml@1.5.2", "", {}, "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ=="],
|
"smol-toml": ["smol-toml@1.5.2", "", {}, "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ=="],
|
||||||
|
|
||||||
|
"socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="],
|
||||||
|
|
||||||
|
"socket.io-parser": ["socket.io-parser@4.2.5", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ=="],
|
||||||
|
|
||||||
"solid-js": ["solid-js@1.9.10", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew=="],
|
"solid-js": ["solid-js@1.9.10", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew=="],
|
||||||
|
|
||||||
"solid-list": ["solid-list@0.3.0", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-t4hx/F/l8Vmq+ib9HtZYl7Z9F1eKxq3eKJTXlvcm7P7yI4Z8O7QSOOEVHb/K6DD7M0RxzVRobK/BS5aSfLRwKg=="],
|
"solid-list": ["solid-list@0.3.0", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-t4hx/F/l8Vmq+ib9HtZYl7Z9F1eKxq3eKJTXlvcm7P7yI4Z8O7QSOOEVHb/K6DD7M0RxzVRobK/BS5aSfLRwKg=="],
|
||||||
@@ -3513,6 +3561,8 @@
|
|||||||
|
|
||||||
"solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="],
|
"solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="],
|
||||||
|
|
||||||
|
"solid-stripe": ["solid-stripe@0.8.1", "", { "peerDependencies": { "@stripe/stripe-js": ">=1.44.1 <8.0.0", "solid-js": "^1.6.0" } }, "sha512-l2SkWoe51rsvk9u1ILBRWyCHODZebChSGMR6zHYJTivTRC0XWrRnNNKs5x1PYXsaIU71KYI6ov5CZB5cOtGLWw=="],
|
||||||
|
|
||||||
"solid-use": ["solid-use@0.9.1", "", { "peerDependencies": { "solid-js": "^1.7" } }, "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw=="],
|
"solid-use": ["solid-use@0.9.1", "", { "peerDependencies": { "solid-js": "^1.7" } }, "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw=="],
|
||||||
|
|
||||||
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||||
@@ -3667,6 +3717,8 @@
|
|||||||
|
|
||||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||||
|
|
||||||
|
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
||||||
|
|
||||||
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
||||||
|
|
||||||
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
||||||
@@ -3859,6 +3911,8 @@
|
|||||||
|
|
||||||
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
||||||
|
|
||||||
|
"xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="],
|
||||||
|
|
||||||
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
||||||
|
|
||||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||||
@@ -3909,35 +3963,33 @@
|
|||||||
|
|
||||||
"@agentclientprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"@agentclientprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.45", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Ipv62vavDCmrV/oE/lXehL9FzwQuZOnnlhPEftWizx464Wb6lvnBTJx8uhmEYruFSzOWTI95Z33ncZ4tA8E6RQ=="],
|
"@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.57", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DREpYqW2pylgaj69gZ+K8u92bo9DaMgFdictYnY+IwYeY3bawQ4zI7l/o1VkDsBDljAx8iYz5lPURwVZNu+Xpg=="],
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock/@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/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
||||||
|
|
||||||
"@ai-sdk/anthropic/@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/anthropic/@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/azure/@ai-sdk/openai": ["@ai-sdk/openai@2.0.80", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tNHuraF11db+8xJEDBoU9E3vMcpnHFKRhnLQ3DQX2LnEzfPB9DksZ8rE+yVuDN1WRW9cm2OWAhgHFgVKs7ICuw=="],
|
"@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@2.0.89", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4+qWkBCbL9HPKbgrUO/F2uXZ8GqrYxHa8SWEYIzxEJ9zvWw3ISr3t1/27O1i8MGSym+PzEyHBT48EV4LAwWaEw=="],
|
||||||
|
|
||||||
"@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/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@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/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@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.57", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DREpYqW2pylgaj69gZ+K8u92bo9DaMgFdictYnY+IwYeY3bawQ4zI7l/o1VkDsBDljAx8iYz5lPURwVZNu+Xpg=="],
|
||||||
|
|
||||||
"@ai-sdk/google-vertex/@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/openai/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
||||||
|
|
||||||
"@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": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
||||||
|
|
||||||
"@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/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/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
"@ai-sdk/vercel/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="],
|
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
|
|
||||||
|
|
||||||
"@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=="],
|
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
@@ -4011,6 +4063,8 @@
|
|||||||
|
|
||||||
"@expressive-code/plugin-shiki/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="],
|
"@expressive-code/plugin-shiki/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="],
|
||||||
|
|
||||||
|
"@gitlab/gitlab-ai-provider/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"@hey-api/json-schema-ref-parser/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
"@hey-api/json-schema-ref-parser/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||||
|
|
||||||
"@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
|
"@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
|
||||||
@@ -4205,10 +4259,6 @@
|
|||||||
|
|
||||||
"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=="],
|
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
@@ -4257,6 +4307,8 @@
|
|||||||
|
|
||||||
"editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="],
|
"editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="],
|
||||||
|
|
||||||
|
"engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
@@ -4273,13 +4325,13 @@
|
|||||||
|
|
||||||
"express/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
"express/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
||||||
|
|
||||||
|
"fetch-blob/web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||||
|
|
||||||
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"gaxios/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
"gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||||
|
|
||||||
"gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
|
||||||
|
|
||||||
"glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
"glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
||||||
|
|
||||||
@@ -4321,11 +4373,11 @@
|
|||||||
|
|
||||||
"nypm/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
"nypm/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||||
|
|
||||||
"opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.56", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XHJKu0Yvfu9SPzRfsAFESa+9T7f2YJY6TxykKMfRsAwpeWAiX/Gbx5J5uM15AzYC3Rw8tVP3oH+j7jEivENirQ=="],
|
"opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.57", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DREpYqW2pylgaj69gZ+K8u92bo9DaMgFdictYnY+IwYeY3bawQ4zI7l/o1VkDsBDljAx8iYz5lPURwVZNu+Xpg=="],
|
||||||
|
|
||||||
"opencode/@ai-sdk/openai": ["@ai-sdk/openai@2.0.71", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tg+gj+R0z/On9P4V7hy7/7o04cQPjKGayMCL3gzWD/aNGjAKkhEnaocuNDidSnghizt8g2zJn16cAuAolnW+qQ=="],
|
"opencode/@ai-sdk/openai": ["@ai-sdk/openai@2.0.89", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4+qWkBCbL9HPKbgrUO/F2uXZ8GqrYxHa8SWEYIzxEJ9zvWw3ISr3t1/27O1i8MGSym+PzEyHBT48EV4LAwWaEw=="],
|
||||||
|
|
||||||
"opencode/@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=="],
|
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
@@ -4363,6 +4415,8 @@
|
|||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
|
"rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
@@ -4925,8 +4979,6 @@
|
|||||||
|
|
||||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||||
|
|
||||||
"opencode/@ai-sdk/openai/@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=="],
|
|
||||||
|
|
||||||
"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=="],
|
||||||
@@ -4943,6 +4995,12 @@
|
|||||||
|
|
||||||
"readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
|
"rimraf/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||||
|
|
||||||
|
"rimraf/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
|
"rimraf/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
@@ -5125,6 +5183,8 @@
|
|||||||
|
|
||||||
"pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
|
"pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
|
||||||
|
|
||||||
|
"rimraf/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
"tw-to-css/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"tw-to-css/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
"tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|||||||
Generated
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767364772,
|
"lastModified": 1768302833,
|
||||||
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
|
"narHash": "sha256-h5bRFy9bco+8QcK7rGoOiqMxMbmn21moTACofNLRMP4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
|
"rev": "61db79b0c6b838d9894923920b612048e1201926",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -27,11 +27,28 @@
|
|||||||
"aarch64-darwin" = "bun-darwin-arm64";
|
"aarch64-darwin" = "bun-darwin-arm64";
|
||||||
"x86_64-darwin" = "bun-darwin-x64";
|
"x86_64-darwin" = "bun-darwin-x64";
|
||||||
};
|
};
|
||||||
defaultNodeModules = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
|
||||||
|
# Parse "bun-{os}-{cpu}" to {os, cpu}
|
||||||
|
parseBunTarget =
|
||||||
|
target:
|
||||||
|
let
|
||||||
|
parts = lib.splitString "-" target;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
os = builtins.elemAt parts 1;
|
||||||
|
cpu = builtins.elemAt parts 2;
|
||||||
|
};
|
||||||
|
|
||||||
hashesFile = "${./nix}/hashes.json";
|
hashesFile = "${./nix}/hashes.json";
|
||||||
hashesData =
|
hashesData =
|
||||||
if builtins.pathExists hashesFile then builtins.fromJSON (builtins.readFile hashesFile) else { };
|
if builtins.pathExists hashesFile then builtins.fromJSON (builtins.readFile hashesFile) else { };
|
||||||
nodeModulesHash = hashesData.nodeModules or defaultNodeModules;
|
# Lookup hash: supports per-system ({system: hash}) or legacy single hash
|
||||||
|
nodeModulesHashFor =
|
||||||
|
system:
|
||||||
|
if builtins.isAttrs hashesData.nodeModules then
|
||||||
|
hashesData.nodeModules.${system}
|
||||||
|
else
|
||||||
|
hashesData.nodeModules;
|
||||||
modelsDev = forEachSystem (
|
modelsDev = forEachSystem (
|
||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
@@ -63,8 +80,11 @@
|
|||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
pkgs = pkgsFor system;
|
pkgs = pkgsFor system;
|
||||||
|
bunPlatform = parseBunTarget bunTarget.${system};
|
||||||
mkNodeModules = pkgs.callPackage ./nix/node-modules.nix {
|
mkNodeModules = pkgs.callPackage ./nix/node-modules.nix {
|
||||||
hash = nodeModulesHash;
|
hash = nodeModulesHashFor system;
|
||||||
|
bunCpu = bunPlatform.cpu;
|
||||||
|
bunOs = bunPlatform.os;
|
||||||
};
|
};
|
||||||
mkOpencode = pkgs.callPackage ./nix/opencode.nix { };
|
mkOpencode = pkgs.callPackage ./nix/opencode.nix { };
|
||||||
mkDesktop = pkgs.callPackage ./nix/desktop.nix { };
|
mkDesktop = pkgs.callPackage ./nix/desktop.nix { };
|
||||||
|
|||||||
+6
-5
@@ -81,12 +81,13 @@ This will walk you through installing the GitHub app, creating the workflow, and
|
|||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Run opencode
|
- name: Run opencode
|
||||||
uses: anomalyco/opencode/github@latest
|
uses: anomalyco/opencode/github@latest
|
||||||
env:
|
env:
|
||||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ const ZEN_MODELS = [
|
|||||||
]
|
]
|
||||||
const ZEN_BLACK = new sst.Secret("ZEN_BLACK")
|
const ZEN_BLACK = new sst.Secret("ZEN_BLACK")
|
||||||
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
|
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
|
||||||
|
const STRIPE_PUBLISHABLE_KEY = new sst.Secret("STRIPE_PUBLISHABLE_KEY")
|
||||||
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||||
properties: { value: auth.url.apply((url) => url!) },
|
properties: { value: auth.url.apply((url) => url!) },
|
||||||
})
|
})
|
||||||
@@ -163,6 +164,7 @@ new sst.cloudflare.x.SolidStart("Console", {
|
|||||||
AWS_SES_ACCESS_KEY_ID,
|
AWS_SES_ACCESS_KEY_ID,
|
||||||
AWS_SES_SECRET_ACCESS_KEY,
|
AWS_SES_SECRET_ACCESS_KEY,
|
||||||
ZEN_BLACK,
|
ZEN_BLACK,
|
||||||
|
new sst.Secret("ZEN_SESSION_SECRET"),
|
||||||
...ZEN_MODELS,
|
...ZEN_MODELS,
|
||||||
...($dev
|
...($dev
|
||||||
? [
|
? [
|
||||||
@@ -176,6 +178,7 @@ new sst.cloudflare.x.SolidStart("Console", {
|
|||||||
//VITE_DOCS_URL: web.url.apply((url) => url!),
|
//VITE_DOCS_URL: web.url.apply((url) => url!),
|
||||||
//VITE_API_URL: gateway.url.apply((url) => url!),
|
//VITE_API_URL: gateway.url.apply((url) => url!),
|
||||||
VITE_AUTH_URL: auth.url.apply((url) => url!),
|
VITE_AUTH_URL: auth.url.apply((url) => url!),
|
||||||
|
VITE_STRIPE_PUBLISHABLE_KEY: STRIPE_PUBLISHABLE_KEY.value,
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"nodeModules": "sha256-+QM5BDFxzrm1HY5ealjCm7jIO1t/rpW1q4GGLViPMmA="
|
"nodeModules": {
|
||||||
|
"x86_64-linux": "sha256-wENwhwRVfgoVyA9YNGcG+fAfu46JxK4xvNgiPbRt//s=",
|
||||||
|
"aarch64-darwin": "sha256-vm1DYl1erlbaqz5NHHlnZEMuFmidr/UkS84nIqLJ96Q="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
bun,
|
bun,
|
||||||
cacert,
|
cacert,
|
||||||
curl,
|
curl,
|
||||||
|
bunCpu,
|
||||||
|
bunOs,
|
||||||
}:
|
}:
|
||||||
args:
|
args:
|
||||||
stdenvNoCC.mkDerivation {
|
stdenvNoCC.mkDerivation {
|
||||||
@@ -29,8 +31,8 @@ stdenvNoCC.mkDerivation {
|
|||||||
export HOME=$(mktemp -d)
|
export HOME=$(mktemp -d)
|
||||||
export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
|
export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
|
||||||
bun install \
|
bun install \
|
||||||
--cpu="*" \
|
--cpu="${bunCpu}" \
|
||||||
--os="*" \
|
--os="${bunOs}" \
|
||||||
--frozen-lockfile \
|
--frozen-lockfile \
|
||||||
--ignore-scripts \
|
--ignore-scripts \
|
||||||
--no-progress \
|
--no-progress \
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ HASH_FILE=${HASH_FILE:-$DEFAULT_HASH_FILE}
|
|||||||
if [ ! -f "$HASH_FILE" ]; then
|
if [ ! -f "$HASH_FILE" ]; then
|
||||||
cat >"$HASH_FILE" <<EOF
|
cat >"$HASH_FILE" <<EOF
|
||||||
{
|
{
|
||||||
"nodeModules": "$DUMMY"
|
"nodeModules": {}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
@@ -33,9 +33,16 @@ trap cleanup EXIT
|
|||||||
|
|
||||||
write_node_modules_hash() {
|
write_node_modules_hash() {
|
||||||
local value="$1"
|
local value="$1"
|
||||||
|
local system="${2:-$SYSTEM}"
|
||||||
local temp
|
local temp
|
||||||
temp=$(mktemp)
|
temp=$(mktemp)
|
||||||
jq --arg value "$value" '.nodeModules = $value' "$HASH_FILE" >"$temp"
|
|
||||||
|
if jq -e '.nodeModules | type == "object"' "$HASH_FILE" >/dev/null 2>&1; then
|
||||||
|
jq --arg system "$system" --arg value "$value" '.nodeModules[$system] = $value' "$HASH_FILE" >"$temp"
|
||||||
|
else
|
||||||
|
jq --arg system "$system" --arg value "$value" '.nodeModules = {($system): $value}' "$HASH_FILE" >"$temp"
|
||||||
|
fi
|
||||||
|
|
||||||
mv "$temp" "$HASH_FILE"
|
mv "$temp" "$HASH_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +111,7 @@ fi
|
|||||||
|
|
||||||
write_node_modules_hash "$CORRECT_HASH"
|
write_node_modules_hash "$CORRECT_HASH"
|
||||||
|
|
||||||
jq -e --arg hash "$CORRECT_HASH" '.nodeModules == $hash' "$HASH_FILE" >/dev/null
|
jq -e --arg system "$SYSTEM" --arg hash "$CORRECT_HASH" '.nodeModules[$system] == $hash' "$HASH_FILE" >/dev/null
|
||||||
|
|
||||||
echo "node_modules hash updated for ${SYSTEM}: $CORRECT_HASH"
|
echo "node_modules hash updated for ${SYSTEM}: $CORRECT_HASH"
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -36,7 +36,8 @@
|
|||||||
"@solid-primitives/storage": "4.3.3",
|
"@solid-primitives/storage": "4.3.3",
|
||||||
"@tailwindcss/vite": "4.1.11",
|
"@tailwindcss/vite": "4.1.11",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
"ai": "5.0.97",
|
"dompurify": "3.3.1",
|
||||||
|
"ai": "5.0.119",
|
||||||
"hono": "4.10.7",
|
"hono": "4.10.7",
|
||||||
"hono-openapi": "1.1.2",
|
"hono-openapi": "1.1.2",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
+12
-12
@@ -33,19 +33,10 @@ const Loading = () => <div class="size-full flex items-center justify-center tex
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
__OPENCODE__?: { updaterEnabled?: boolean; port?: number; serverReady?: boolean }
|
__OPENCODE__?: { updaterEnabled?: boolean; serverPassword?: string }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultServerUrl = iife(() => {
|
|
||||||
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
|
|
||||||
if (window.__OPENCODE__) return `http://127.0.0.1:${window.__OPENCODE__.port}`
|
|
||||||
if (import.meta.env.DEV)
|
|
||||||
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
|
|
||||||
|
|
||||||
return window.location.origin
|
|
||||||
})
|
|
||||||
|
|
||||||
export function AppBaseProviders(props: ParentProps) {
|
export function AppBaseProviders(props: ParentProps) {
|
||||||
return (
|
return (
|
||||||
<MetaProvider>
|
<MetaProvider>
|
||||||
@@ -74,9 +65,18 @@ function ServerKey(props: ParentProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AppInterface() {
|
export function AppInterface(props: { defaultUrl?: string }) {
|
||||||
|
const defaultServerUrl = () => {
|
||||||
|
if (props.defaultUrl) return props.defaultUrl
|
||||||
|
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
|
||||||
|
if (import.meta.env.DEV)
|
||||||
|
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
|
||||||
|
|
||||||
|
return window.location.origin
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ServerProvider defaultUrl={defaultServerUrl}>
|
<ServerProvider defaultUrl={defaultServerUrl()}>
|
||||||
<ServerKey>
|
<ServerKey>
|
||||||
<GlobalSDKProvider>
|
<GlobalSDKProvider>
|
||||||
<GlobalSyncProvider>
|
<GlobalSyncProvider>
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { Component, createMemo } from "solid-js"
|
||||||
|
import { useNavigate, useParams } from "@solidjs/router"
|
||||||
|
import { useSync } from "@/context/sync"
|
||||||
|
import { useSDK } from "@/context/sdk"
|
||||||
|
import { usePrompt } from "@/context/prompt"
|
||||||
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
|
import { List } from "@opencode-ai/ui/list"
|
||||||
|
import { extractPromptFromParts } from "@/utils/prompt"
|
||||||
|
import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
|
||||||
|
import { base64Encode } from "@opencode-ai/util/encode"
|
||||||
|
|
||||||
|
interface ForkableMessage {
|
||||||
|
id: string
|
||||||
|
text: string
|
||||||
|
time: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(date: Date): string {
|
||||||
|
return date.toLocaleTimeString(undefined, { timeStyle: "short" })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DialogFork: Component = () => {
|
||||||
|
const params = useParams()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const sync = useSync()
|
||||||
|
const sdk = useSDK()
|
||||||
|
const prompt = usePrompt()
|
||||||
|
const dialog = useDialog()
|
||||||
|
|
||||||
|
const messages = createMemo((): ForkableMessage[] => {
|
||||||
|
const sessionID = params.id
|
||||||
|
if (!sessionID) return []
|
||||||
|
|
||||||
|
const msgs = sync.data.message[sessionID] ?? []
|
||||||
|
const result: ForkableMessage[] = []
|
||||||
|
|
||||||
|
for (const message of msgs) {
|
||||||
|
if (message.role !== "user") continue
|
||||||
|
|
||||||
|
const parts = sync.data.part[message.id] ?? []
|
||||||
|
const textPart = parts.find((x): x is SDKTextPart => x.type === "text" && !x.synthetic && !x.ignored)
|
||||||
|
if (!textPart) continue
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
id: message.id,
|
||||||
|
text: textPart.text.replace(/\n/g, " ").slice(0, 200),
|
||||||
|
time: formatTime(new Date(message.time.created)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.reverse()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSelect = (item: ForkableMessage | undefined) => {
|
||||||
|
if (!item) return
|
||||||
|
|
||||||
|
const sessionID = params.id
|
||||||
|
if (!sessionID) return
|
||||||
|
|
||||||
|
const parts = sync.data.part[item.id] ?? []
|
||||||
|
const restored = extractPromptFromParts(parts, { directory: sdk.directory })
|
||||||
|
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
sdk.client.session.fork({ sessionID, messageID: item.id }).then((forked) => {
|
||||||
|
if (!forked.data) return
|
||||||
|
navigate(`/${base64Encode(sdk.directory)}/session/${forked.data.id}`)
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
prompt.set(restored)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog title="Fork from message">
|
||||||
|
<List
|
||||||
|
class="flex-1 min-h-0 [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:min-h-0"
|
||||||
|
search={{ placeholder: "Search", autofocus: true }}
|
||||||
|
emptyMessage="No messages to fork from"
|
||||||
|
key={(x) => x.id}
|
||||||
|
items={messages}
|
||||||
|
filterKeys={["text"]}
|
||||||
|
onSelect={handleSelect}
|
||||||
|
>
|
||||||
|
{(item) => (
|
||||||
|
<div class="w-full flex items-center gap-2">
|
||||||
|
<span class="truncate flex-1 min-w-0 text-left" style={{ "font-weight": "400" }}>
|
||||||
|
{item.text}
|
||||||
|
</span>
|
||||||
|
<span class="text-text-weak shrink-0" style={{ "font-weight": "400" }}>
|
||||||
|
{item.time}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { createEffect, createMemo, onCleanup } from "solid-js"
|
import { createResource, createEffect, createMemo, onCleanup, Show } from "solid-js"
|
||||||
import { createStore, reconcile } from "solid-js/store"
|
import { createStore, reconcile } from "solid-js/store"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
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 { TextField } from "@opencode-ai/ui/text-field"
|
import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server"
|
import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
|
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
|
||||||
@@ -35,6 +36,8 @@ export function DialogSelectServer() {
|
|||||||
error: "",
|
error: "",
|
||||||
status: {} as Record<string, ServerStatus | undefined>,
|
status: {} as Record<string, ServerStatus | undefined>,
|
||||||
})
|
})
|
||||||
|
const [defaultUrl, defaultUrlActions] = createResource(() => platform.getDefaultServerUrl?.())
|
||||||
|
const isDesktop = platform.platform === "desktop"
|
||||||
|
|
||||||
const items = createMemo(() => {
|
const items = createMemo(() => {
|
||||||
const current = server.url
|
const current = server.url
|
||||||
@@ -114,6 +117,10 @@ export function DialogSelectServer() {
|
|||||||
select(value, true)
|
select(value, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRemove(url: string) {
|
||||||
|
server.remove(url)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog title="Servers" description="Switch which OpenCode server this app connects to.">
|
<Dialog title="Servers" description="Switch which OpenCode server this app connects to.">
|
||||||
<div class="flex flex-col gap-4 pb-4">
|
<div class="flex flex-col gap-4 pb-4">
|
||||||
@@ -128,20 +135,33 @@ export function DialogSelectServer() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div
|
<div class="flex items-center gap-2 min-w-0 flex-1 group/item">
|
||||||
class="flex items-center gap-2 min-w-0 flex-1"
|
|
||||||
classList={{ "opacity-50": store.status[i]?.healthy === false }}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
classList={{
|
class="flex items-center gap-2 min-w-0 flex-1"
|
||||||
"size-1.5 rounded-full shrink-0": true,
|
classList={{ "opacity-50": store.status[i]?.healthy === false }}
|
||||||
"bg-icon-success-base": store.status[i]?.healthy === true,
|
>
|
||||||
"bg-icon-critical-base": store.status[i]?.healthy === false,
|
<div
|
||||||
"bg-border-weak-base": store.status[i] === undefined,
|
classList={{
|
||||||
}}
|
"size-1.5 rounded-full shrink-0": true,
|
||||||
/>
|
"bg-icon-success-base": store.status[i]?.healthy === true,
|
||||||
<span class="truncate">{serverDisplayName(i)}</span>
|
"bg-icon-critical-base": store.status[i]?.healthy === false,
|
||||||
<span class="text-text-weak">{store.status[i]?.version}</span>
|
"bg-border-weak-base": store.status[i] === undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span class="truncate">{serverDisplayName(i)}</span>
|
||||||
|
<span class="text-text-weak">{store.status[i]?.version}</span>
|
||||||
|
</div>
|
||||||
|
<Show when={current() !== i && server.list.includes(i)}>
|
||||||
|
<IconButton
|
||||||
|
icon="circle-x"
|
||||||
|
variant="ghost"
|
||||||
|
class="bg-transparent transition-opacity shrink-0 hover:scale-110"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleRemove(i)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
@@ -173,6 +193,53 @@ export function DialogSelectServer() {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Show when={isDesktop}>
|
||||||
|
<div class="mt-6 px-3 flex flex-col gap-1.5">
|
||||||
|
<div class="px-3">
|
||||||
|
<h3 class="text-14-regular text-text-weak">Default server</h3>
|
||||||
|
<p class="text-12-regular text-text-weak mt-1">
|
||||||
|
Connect to this server on app launch instead of starting a local server. Requires restart.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 px-3 py-2">
|
||||||
|
<Show
|
||||||
|
when={defaultUrl()}
|
||||||
|
fallback={
|
||||||
|
<Show
|
||||||
|
when={server.url}
|
||||||
|
fallback={<span class="text-14-regular text-text-weak">No server selected</span>}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="small"
|
||||||
|
onClick={async () => {
|
||||||
|
await platform.setDefaultServerUrl?.(server.url)
|
||||||
|
defaultUrlActions.refetch(server.url)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Set current server as default
|
||||||
|
</Button>
|
||||||
|
</Show>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-2 flex-1 min-w-0">
|
||||||
|
<span class="truncate text-14-regular">{serverDisplayName(defaultUrl()!)}</span>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="small"
|
||||||
|
onClick={async () => {
|
||||||
|
await platform.setDefaultServerUrl?.(null)
|
||||||
|
defaultUrlActions.refetch()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,11 +33,14 @@ import { useSync } from "@/context/sync"
|
|||||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
|
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||||
|
import type { IconName } from "@opencode-ai/ui/icons/provider"
|
||||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { Select } from "@opencode-ai/ui/select"
|
import { Select } from "@opencode-ai/ui/select"
|
||||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
|
import { ImagePreview } from "@opencode-ai/ui/image-preview"
|
||||||
import { ModelSelectorPopover } from "@/components/dialog-select-model"
|
import { ModelSelectorPopover } 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"
|
||||||
@@ -361,6 +364,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
if (!isFocused()) setStore("popover", null)
|
if (!isFocused()) setStore("popover", null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Safety: reset composing state on focus change to prevent stuck state
|
||||||
|
// This handles edge cases where compositionend event may not fire
|
||||||
|
createEffect(() => {
|
||||||
|
if (!isFocused()) setComposing(false)
|
||||||
|
})
|
||||||
|
|
||||||
type AtOption = { type: "agent"; name: string; display: string } | { type: "file"; path: string; display: string }
|
type AtOption = { type: "agent"; name: string; display: string } | { type: "file"; path: string; display: string }
|
||||||
|
|
||||||
const agentList = createMemo(() =>
|
const agentList = createMemo(() =>
|
||||||
@@ -386,6 +395,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const {
|
const {
|
||||||
flat: atFlat,
|
flat: atFlat,
|
||||||
active: atActive,
|
active: atActive,
|
||||||
|
setActive: setAtActive,
|
||||||
onInput: atOnInput,
|
onInput: atOnInput,
|
||||||
onKeyDown: atOnKeyDown,
|
onKeyDown: atOnKeyDown,
|
||||||
} = useFilteredList<AtOption>({
|
} = useFilteredList<AtOption>({
|
||||||
@@ -452,6 +462,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const {
|
const {
|
||||||
flat: slashFlat,
|
flat: slashFlat,
|
||||||
active: slashActive,
|
active: slashActive,
|
||||||
|
setActive: setSlashActive,
|
||||||
onInput: slashOnInput,
|
onInput: slashOnInput,
|
||||||
onKeyDown: slashOnKeyDown,
|
onKeyDown: slashOnKeyDown,
|
||||||
refetch: slashRefetch,
|
refetch: slashRefetch,
|
||||||
@@ -876,6 +887,14 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Shift+Enter BEFORE IME check - Shift+Enter is never used for IME input
|
||||||
|
// and should always insert a newline regardless of composition state
|
||||||
|
if (event.key === "Enter" && event.shiftKey) {
|
||||||
|
addPart({ type: "text", content: "\n", start: 0, end: 0 })
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (event.key === "Enter" && isImeComposing(event)) {
|
if (event.key === "Enter" && isImeComposing(event)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -939,11 +958,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === "Enter" && event.shiftKey) {
|
// Note: Shift+Enter is handled earlier, before IME check
|
||||||
addPart({ type: "text", content: "\n", start: 0, end: 0 })
|
|
||||||
event.preventDefault()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (event.key === "Enter" && !event.shiftKey) {
|
if (event.key === "Enter" && !event.shiftKey) {
|
||||||
handleSubmit(event)
|
handleSubmit(event)
|
||||||
}
|
}
|
||||||
@@ -1299,6 +1314,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
class="absolute inset-x-0 -top-3 -translate-y-full origin-bottom-left max-h-80 min-h-10
|
class="absolute inset-x-0 -top-3 -translate-y-full origin-bottom-left max-h-80 min-h-10
|
||||||
overflow-auto no-scrollbar flex flex-col p-2 rounded-md
|
overflow-auto no-scrollbar flex flex-col p-2 rounded-md
|
||||||
border border-border-base bg-surface-raised-stronger-non-alpha shadow-md"
|
border border-border-base bg-surface-raised-stronger-non-alpha shadow-md"
|
||||||
|
onMouseDown={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={store.popover === "at"}>
|
<Match when={store.popover === "at"}>
|
||||||
@@ -1314,6 +1330,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
"bg-surface-raised-base-hover": atActive() === atKey(item),
|
"bg-surface-raised-base-hover": atActive() === atKey(item),
|
||||||
}}
|
}}
|
||||||
onClick={() => handleAtSelect(item)}
|
onClick={() => handleAtSelect(item)}
|
||||||
|
onMouseEnter={() => setAtActive(atKey(item))}
|
||||||
>
|
>
|
||||||
<Show
|
<Show
|
||||||
when={item.type === "agent"}
|
when={item.type === "agent"}
|
||||||
@@ -1360,6 +1377,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
"bg-surface-raised-base-hover": slashActive() === cmd.id,
|
"bg-surface-raised-base-hover": slashActive() === cmd.id,
|
||||||
}}
|
}}
|
||||||
onClick={() => handleSlashSelect(cmd)}
|
onClick={() => handleSlashSelect(cmd)}
|
||||||
|
onMouseEnter={() => setSlashActive(cmd.id)}
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
<span class="text-14-regular text-text-strong whitespace-nowrap">/{cmd.trigger}</span>
|
<span class="text-14-regular text-text-strong whitespace-nowrap">/{cmd.trigger}</span>
|
||||||
@@ -1479,7 +1497,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<img
|
<img
|
||||||
src={attachment.dataUrl}
|
src={attachment.dataUrl}
|
||||||
alt={attachment.filename}
|
alt={attachment.filename}
|
||||||
class="size-16 rounded-md object-cover border border-border-base"
|
class="size-16 rounded-md object-cover border border-border-base hover:border-border-strong-base transition-colors"
|
||||||
|
onClick={() =>
|
||||||
|
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<button
|
<button
|
||||||
@@ -1551,6 +1572,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
fallback={
|
fallback={
|
||||||
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
||||||
<Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
|
<Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
|
||||||
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
|
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
||||||
|
</Show>
|
||||||
{local.model.current()?.name ?? "Select model"}
|
{local.model.current()?.name ?? "Select model"}
|
||||||
<Icon name="chevron-down" size="small" />
|
<Icon name="chevron-down" size="small" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1560,6 +1584,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<ModelSelectorPopover>
|
<ModelSelectorPopover>
|
||||||
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
||||||
<Button as="div" variant="ghost">
|
<Button as="div" variant="ghost">
|
||||||
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
|
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
||||||
|
</Show>
|
||||||
{local.model.current()?.name ?? "Select model"}
|
{local.model.current()?.name ?? "Select model"}
|
||||||
<Icon name="chevron-down" size="small" />
|
<Icon name="chevron-down" size="small" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1574,10 +1601,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="text-text-base _hidden group-hover/prompt-input:inline-block"
|
class="text-text-base _hidden group-hover/prompt-input:inline-block capitalize text-12-regular"
|
||||||
onClick={() => local.model.variant.cycle()}
|
onClick={() => local.model.variant.cycle()}
|
||||||
>
|
>
|
||||||
<span class="capitalize text-12-regular">{local.model.variant.current() ?? "Default"}</span>
|
{local.model.variant.current() ?? "Default"}
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -100,9 +100,12 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
const mod = await import("ghostty-web")
|
const mod = await import("ghostty-web")
|
||||||
ghostty = await mod.Ghostty.load()
|
ghostty = await mod.Ghostty.load()
|
||||||
|
|
||||||
const socket = new WebSocket(
|
const url = new URL(sdk.url + `/pty/${local.pty.id}/connect?directory=${encodeURIComponent(sdk.directory)}`)
|
||||||
sdk.url + `/pty/${local.pty.id}/connect?directory=${encodeURIComponent(sdk.directory)}`,
|
if (window.__OPENCODE__?.serverPassword) {
|
||||||
)
|
url.username = "opencode"
|
||||||
|
url.password = window.__OPENCODE__?.serverPassword
|
||||||
|
}
|
||||||
|
const socket = new WebSocket(url)
|
||||||
ws = socket
|
ws = socket
|
||||||
|
|
||||||
const t = new mod.Terminal({
|
const t = new mod.Terminal({
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
|
|||||||
name: "GlobalSDK",
|
name: "GlobalSDK",
|
||||||
init: () => {
|
init: () => {
|
||||||
const server = useServer()
|
const server = useServer()
|
||||||
|
const platform = usePlatform()
|
||||||
const abort = new AbortController()
|
const abort = new AbortController()
|
||||||
|
|
||||||
const eventSdk = createOpencodeClient({
|
const eventSdk = createOpencodeClient({
|
||||||
baseUrl: server.url,
|
baseUrl: server.url,
|
||||||
signal: abort.signal,
|
signal: abort.signal,
|
||||||
|
fetch: platform.fetch,
|
||||||
})
|
})
|
||||||
const emitter = createGlobalEmitter<{
|
const emitter = createGlobalEmitter<{
|
||||||
[key: string]: Event
|
[key: string]: Event
|
||||||
@@ -93,7 +95,6 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
|
|||||||
stop()
|
stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
const platform = usePlatform()
|
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: server.url,
|
baseUrl: server.url,
|
||||||
fetch: platform.fetch,
|
fetch: platform.fetch,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
type LspStatus,
|
type LspStatus,
|
||||||
type VcsInfo,
|
type VcsInfo,
|
||||||
type PermissionRequest,
|
type PermissionRequest,
|
||||||
|
type QuestionRequest,
|
||||||
createOpencodeClient,
|
createOpencodeClient,
|
||||||
} 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"
|
||||||
@@ -26,6 +27,7 @@ import { ErrorPage, type InitError } from "../pages/error"
|
|||||||
import { batch, createContext, useContext, onCleanup, onMount, type ParentProps, Switch, Match } from "solid-js"
|
import { batch, createContext, useContext, onCleanup, onMount, type ParentProps, Switch, Match } from "solid-js"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
|
import { usePlatform } from "./platform"
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
status: "loading" | "partial" | "complete"
|
status: "loading" | "partial" | "complete"
|
||||||
@@ -48,6 +50,9 @@ type State = {
|
|||||||
permission: {
|
permission: {
|
||||||
[sessionID: string]: PermissionRequest[]
|
[sessionID: string]: PermissionRequest[]
|
||||||
}
|
}
|
||||||
|
question: {
|
||||||
|
[sessionID: string]: QuestionRequest[]
|
||||||
|
}
|
||||||
mcp: {
|
mcp: {
|
||||||
[name: string]: McpStatus
|
[name: string]: McpStatus
|
||||||
}
|
}
|
||||||
@@ -64,6 +69,7 @@ type State = {
|
|||||||
|
|
||||||
function createGlobalSync() {
|
function createGlobalSync() {
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
|
const platform = usePlatform()
|
||||||
const [globalStore, setGlobalStore] = createStore<{
|
const [globalStore, setGlobalStore] = createStore<{
|
||||||
ready: boolean
|
ready: boolean
|
||||||
error?: InitError
|
error?: InitError
|
||||||
@@ -96,6 +102,7 @@ function createGlobalSync() {
|
|||||||
session_diff: {},
|
session_diff: {},
|
||||||
todo: {},
|
todo: {},
|
||||||
permission: {},
|
permission: {},
|
||||||
|
question: {},
|
||||||
mcp: {},
|
mcp: {},
|
||||||
lsp: [],
|
lsp: [],
|
||||||
vcs: undefined,
|
vcs: undefined,
|
||||||
@@ -139,6 +146,7 @@ function createGlobalSync() {
|
|||||||
const [store, setStore] = child(directory)
|
const [store, setStore] = child(directory)
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: globalSDK.url,
|
baseUrl: globalSDK.url,
|
||||||
|
fetch: platform.fetch,
|
||||||
directory,
|
directory,
|
||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
@@ -205,6 +213,38 @@ function createGlobalSync() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
sdk.question.list().then((x) => {
|
||||||
|
const grouped: Record<string, QuestionRequest[]> = {}
|
||||||
|
for (const question of x.data ?? []) {
|
||||||
|
if (!question?.id || !question.sessionID) continue
|
||||||
|
const existing = grouped[question.sessionID]
|
||||||
|
if (existing) {
|
||||||
|
existing.push(question)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
grouped[question.sessionID] = [question]
|
||||||
|
}
|
||||||
|
|
||||||
|
batch(() => {
|
||||||
|
for (const sessionID of Object.keys(store.question)) {
|
||||||
|
if (grouped[sessionID]) continue
|
||||||
|
setStore("question", sessionID, [])
|
||||||
|
}
|
||||||
|
for (const [sessionID, questions] of Object.entries(grouped)) {
|
||||||
|
setStore(
|
||||||
|
"question",
|
||||||
|
sessionID,
|
||||||
|
reconcile(
|
||||||
|
questions
|
||||||
|
.filter((q) => !!q?.id)
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => a.id.localeCompare(b.id)),
|
||||||
|
{ key: "id" },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
setStore("status", "complete")
|
setStore("status", "complete")
|
||||||
})
|
})
|
||||||
@@ -393,9 +433,48 @@ function createGlobalSync() {
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case "question.asked": {
|
||||||
|
const sessionID = event.properties.sessionID
|
||||||
|
const questions = store.question[sessionID]
|
||||||
|
if (!questions) {
|
||||||
|
setStore("question", sessionID, [event.properties])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = Binary.search(questions, event.properties.id, (q) => q.id)
|
||||||
|
if (result.found) {
|
||||||
|
setStore("question", sessionID, result.index, reconcile(event.properties))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
setStore(
|
||||||
|
"question",
|
||||||
|
sessionID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 0, event.properties)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "question.replied":
|
||||||
|
case "question.rejected": {
|
||||||
|
const questions = store.question[event.properties.sessionID]
|
||||||
|
if (!questions) break
|
||||||
|
const result = Binary.search(questions, event.properties.requestID, (q) => q.id)
|
||||||
|
if (!result.found) break
|
||||||
|
setStore(
|
||||||
|
"question",
|
||||||
|
event.properties.sessionID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 1)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
case "lsp.updated": {
|
case "lsp.updated": {
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: globalSDK.url,
|
baseUrl: globalSDK.url,
|
||||||
|
fetch: platform.fetch,
|
||||||
directory,
|
directory,
|
||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ export type Platform = {
|
|||||||
|
|
||||||
/** Fetch override */
|
/** Fetch override */
|
||||||
fetch?: typeof fetch
|
fetch?: typeof fetch
|
||||||
|
|
||||||
|
/** Get the configured default server URL (desktop only) */
|
||||||
|
getDefaultServerUrl?(): Promise<string | null>
|
||||||
|
|
||||||
|
/** Set the default server URL to use on app startup (desktop only) */
|
||||||
|
setDefaultServerUrl?(url: string | null): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
|
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
|
||||||
|
|||||||
@@ -16,10 +16,7 @@ export function normalizeServerUrl(input: string) {
|
|||||||
|
|
||||||
export function serverDisplayName(url: string) {
|
export function serverDisplayName(url: string) {
|
||||||
if (!url) return ""
|
if (!url) return ""
|
||||||
return url
|
return url.replace(/^https?:\/\//, "").replace(/\/+$/, "")
|
||||||
.replace(/^https?:\/\//, "")
|
|
||||||
.replace(/\/+$/, "")
|
|
||||||
.split("/")[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectsKey(url: string) {
|
function projectsKey(url: string) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Persist, persisted } from "@/utils/persist"
|
|||||||
export type LocalPTY = {
|
export type LocalPTY = {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: string
|
||||||
|
titleNumber: number
|
||||||
rows?: number
|
rows?: number
|
||||||
cols?: number
|
cols?: number
|
||||||
buffer?: string
|
buffer?: string
|
||||||
@@ -42,8 +43,20 @@ function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, id:
|
|||||||
all: createMemo(() => Object.values(store.all)),
|
all: createMemo(() => Object.values(store.all)),
|
||||||
active: createMemo(() => store.active),
|
active: createMemo(() => store.active),
|
||||||
new() {
|
new() {
|
||||||
|
const existingTitleNumbers = new Set(
|
||||||
|
store.all.map((pty) => {
|
||||||
|
const match = pty.titleNumber
|
||||||
|
return match
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
let nextNumber = 1
|
||||||
|
while (existingTitleNumbers.has(nextNumber)) {
|
||||||
|
nextNumber++
|
||||||
|
}
|
||||||
|
|
||||||
sdk.client.pty
|
sdk.client.pty
|
||||||
.create({ title: `Terminal ${store.all.length + 1}` })
|
.create({ title: `Terminal ${nextNumber}` })
|
||||||
.then((pty) => {
|
.then((pty) => {
|
||||||
const id = pty.data?.id
|
const id = pty.data?.id
|
||||||
if (!id) return
|
if (!id) return
|
||||||
@@ -52,6 +65,7 @@ function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, id:
|
|||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
title: pty.data?.title ?? "Terminal",
|
title: pty.data?.title ?? "Terminal",
|
||||||
|
titleNumber: nextNumber,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
setStore("active", id)
|
setStore("active", id)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { LocalProvider } from "@/context/local"
|
|||||||
import { base64Decode } from "@opencode-ai/util/encode"
|
import { base64Decode } from "@opencode-ai/util/encode"
|
||||||
import { DataProvider } from "@opencode-ai/ui/context"
|
import { DataProvider } from "@opencode-ai/ui/context"
|
||||||
import { iife } from "@opencode-ai/util/iife"
|
import { iife } from "@opencode-ai/util/iife"
|
||||||
|
import type { QuestionAnswer } from "@opencode-ai/sdk/v2"
|
||||||
|
|
||||||
export default function Layout(props: ParentProps) {
|
export default function Layout(props: ParentProps) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
@@ -27,6 +28,11 @@ export default function Layout(props: ParentProps) {
|
|||||||
response: "once" | "always" | "reject"
|
response: "once" | "always" | "reject"
|
||||||
}) => sdk.client.permission.respond(input)
|
}) => sdk.client.permission.respond(input)
|
||||||
|
|
||||||
|
const replyToQuestion = (input: { requestID: string; answers: QuestionAnswer[] }) =>
|
||||||
|
sdk.client.question.reply(input)
|
||||||
|
|
||||||
|
const rejectQuestion = (input: { requestID: string }) => sdk.client.question.reject(input)
|
||||||
|
|
||||||
const navigateToSession = (sessionID: string) => {
|
const navigateToSession = (sessionID: string) => {
|
||||||
navigate(`/${params.dir}/session/${sessionID}`)
|
navigate(`/${params.dir}/session/${sessionID}`)
|
||||||
}
|
}
|
||||||
@@ -36,6 +42,8 @@ export default function Layout(props: ParentProps) {
|
|||||||
data={sync.data}
|
data={sync.data}
|
||||||
directory={directory()}
|
directory={directory()}
|
||||||
onPermissionRespond={respond}
|
onPermissionRespond={respond}
|
||||||
|
onQuestionReply={replyToQuestion}
|
||||||
|
onQuestionReject={rejectQuestion}
|
||||||
onNavigateToSession={navigateToSession}
|
onNavigateToSession={navigateToSession}
|
||||||
>
|
>
|
||||||
<LocalProvider>{props.children}</LocalProvider>
|
<LocalProvider>{props.children}</LocalProvider>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|||||||
import { DialogSelectFile } from "@/components/dialog-select-file"
|
import { DialogSelectFile } from "@/components/dialog-select-file"
|
||||||
import { DialogSelectModel } from "@/components/dialog-select-model"
|
import { DialogSelectModel } from "@/components/dialog-select-model"
|
||||||
import { DialogSelectMcp } from "@/components/dialog-select-mcp"
|
import { DialogSelectMcp } from "@/components/dialog-select-mcp"
|
||||||
|
import { DialogFork } from "@/components/dialog-fork"
|
||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { useNavigate, useParams } from "@solidjs/router"
|
import { useNavigate, useParams } from "@solidjs/router"
|
||||||
import { UserMessage } from "@opencode-ai/sdk/v2"
|
import { UserMessage } from "@opencode-ai/sdk/v2"
|
||||||
@@ -645,6 +646,15 @@ export default function Page() {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "session.fork",
|
||||||
|
title: "Fork from message",
|
||||||
|
description: "Create a new session from a previous message",
|
||||||
|
category: "Session",
|
||||||
|
slash: "fork",
|
||||||
|
disabled: !params.id || visibleUserMessages().length === 0,
|
||||||
|
onSelect: () => dialog.show(() => <DialogFork />),
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsgo --noEmit",
|
"typecheck": "tsgo --noEmit",
|
||||||
"dev": "vite dev --host 0.0.0.0",
|
"dev": "vite dev --host 0.0.0.0",
|
||||||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai VITE_STRIPE_PUBLISHABLE_KEY=pk_test_51RtuLNE7fOCwHSD4mewwzFejyytjdGoSDK7CAvhbffwaZnPbNb2rwJICw6LTOXCmWO320fSNXvb5NzI08RZVkAxd00syfqrW7t bun sst shell --stage=dev bun dev",
|
||||||
"build": "./script/generate-sitemap.ts && vite build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
"build": "./script/generate-sitemap.ts && vite build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||||
"start": "vite start"
|
"start": "vite start"
|
||||||
},
|
},
|
||||||
@@ -23,9 +23,12 @@
|
|||||||
"@solidjs/meta": "catalog:",
|
"@solidjs/meta": "catalog:",
|
||||||
"@solidjs/router": "catalog:",
|
"@solidjs/router": "catalog:",
|
||||||
"@solidjs/start": "catalog:",
|
"@solidjs/start": "catalog:",
|
||||||
|
"@stripe/stripe-js": "8.6.1",
|
||||||
"chart.js": "4.5.1",
|
"chart.js": "4.5.1",
|
||||||
"nitro": "3.0.1-alpha.1",
|
"nitro": "3.0.1-alpha.1",
|
||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
|
"solid-list": "0.3.0",
|
||||||
|
"solid-stripe": "0.8.1",
|
||||||
"vite": "catalog:",
|
"vite": "catalog:",
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -9,8 +9,8 @@ export const config = {
|
|||||||
github: {
|
github: {
|
||||||
repoUrl: "https://github.com/anomalyco/opencode",
|
repoUrl: "https://github.com/anomalyco/opencode",
|
||||||
starsFormatted: {
|
starsFormatted: {
|
||||||
compact: "50K",
|
compact: "60K",
|
||||||
full: "50,000",
|
full: "60,000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import { useSession } from "@solidjs/start/http"
|
|
||||||
|
|
||||||
export interface AuthSession {
|
|
||||||
account?: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
id: string
|
|
||||||
email: string
|
|
||||||
}
|
|
||||||
>
|
|
||||||
current?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useAuthSession() {
|
|
||||||
return useSession<AuthSession>({
|
|
||||||
password: "0".repeat(32),
|
|
||||||
name: "auth",
|
|
||||||
maxAge: 60 * 60 * 24 * 365,
|
|
||||||
cookie: {
|
|
||||||
secure: false,
|
|
||||||
httpOnly: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,13 +5,38 @@ import { redirect } from "@solidjs/router"
|
|||||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||||
|
|
||||||
import { createClient } from "@openauthjs/openauth/client"
|
import { createClient } from "@openauthjs/openauth/client"
|
||||||
import { useAuthSession } from "./auth.session"
|
|
||||||
|
|
||||||
export const AuthClient = createClient({
|
export const AuthClient = createClient({
|
||||||
clientID: "app",
|
clientID: "app",
|
||||||
issuer: import.meta.env.VITE_AUTH_URL,
|
issuer: import.meta.env.VITE_AUTH_URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
import { useSession } from "@solidjs/start/http"
|
||||||
|
import { Resource } from "@opencode-ai/console-resource"
|
||||||
|
|
||||||
|
export interface AuthSession {
|
||||||
|
account?: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
id: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
>
|
||||||
|
current?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAuthSession() {
|
||||||
|
return useSession<AuthSession>({
|
||||||
|
password: Resource.ZEN_SESSION_SECRET.value,
|
||||||
|
name: "auth",
|
||||||
|
maxAge: 60 * 60 * 24 * 365,
|
||||||
|
cookie: {
|
||||||
|
secure: false,
|
||||||
|
httpOnly: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const getActor = async (workspace?: string): Promise<Actor.Info> => {
|
export const getActor = async (workspace?: string): Promise<Actor.Info> => {
|
||||||
"use server"
|
"use server"
|
||||||
const evt = getRequestEvent()
|
const evt = getRequestEvent()
|
||||||
|
|||||||
+3
-2
@@ -1,10 +1,11 @@
|
|||||||
import { redirect } from "@solidjs/router"
|
import { redirect } from "@solidjs/router"
|
||||||
import type { APIEvent } from "@solidjs/start/server"
|
import type { APIEvent } from "@solidjs/start/server"
|
||||||
import { AuthClient } from "~/context/auth"
|
import { AuthClient } from "~/context/auth"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(input: APIEvent) {
|
export async function GET(input: APIEvent) {
|
||||||
const url = new URL(input.request.url)
|
const url = new URL(input.request.url)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const code = url.searchParams.get("code")
|
const code = url.searchParams.get("code")
|
||||||
if (!code) throw new Error("No code found")
|
if (!code) throw new Error("No code found")
|
||||||
@@ -27,7 +28,7 @@ export async function GET(input: APIEvent) {
|
|||||||
current: id,
|
current: id,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return redirect("/auth")
|
return redirect(url.pathname === "/auth/callback" ? "/auth" : url.pathname.replace("/auth/callback", ""))
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@@ -2,6 +2,9 @@ import type { APIEvent } from "@solidjs/start/server"
|
|||||||
import { AuthClient } from "~/context/auth"
|
import { AuthClient } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(input: APIEvent) {
|
export async function GET(input: APIEvent) {
|
||||||
const result = await AuthClient.authorize(new URL("./callback", input.request.url).toString(), "code")
|
const url = new URL(input.request.url)
|
||||||
|
const cont = url.searchParams.get("continue") ?? ""
|
||||||
|
const callbackUrl = new URL(`./callback${cont}`, input.request.url)
|
||||||
|
const result = await AuthClient.authorize(callbackUrl.toString(), "code")
|
||||||
return Response.redirect(result.url, 302)
|
return Response.redirect(result.url, 302)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { redirect } from "@solidjs/router"
|
import { redirect } from "@solidjs/router"
|
||||||
import { APIEvent } from "@solidjs/start"
|
import { APIEvent } from "@solidjs/start"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(event: APIEvent) {
|
export async function GET(event: APIEvent) {
|
||||||
const auth = await useAuthSession()
|
const auth = await useAuthSession()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { APIEvent } from "@solidjs/start"
|
import { APIEvent } from "@solidjs/start"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(input: APIEvent) {
|
export async function GET(input: APIEvent) {
|
||||||
const session = await useAuthSession()
|
const session = await useAuthSession()
|
||||||
|
|||||||
@@ -0,0 +1,742 @@
|
|||||||
|
[data-page="black"] {
|
||||||
|
background: #000;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: stretch;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
[data-component="header-gradient"] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 288px;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="header"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
/* [data-component="header-logo"] { */
|
||||||
|
/* } */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="content"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
[data-slot="hero"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="hero-black"] {
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 540px;
|
||||||
|
height: auto;
|
||||||
|
filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="cta"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: -40px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="heading"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 28.8px */
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="subheading"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 160%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="button"] {
|
||||||
|
display: inline-flex;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 12px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.92);
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000;
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="back-soon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 20.8px */
|
||||||
|
}
|
||||||
|
[data-slot="follow-us"] {
|
||||||
|
display: inline-flex;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 12px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="pricing"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 540px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="pricing-card"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
border-radius: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: border-color 0.15s ease;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="icon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="price"] {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="amount"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="period"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="multiplier"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "·";
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="selected-plan"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: calc(100% - 40px);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="selected-card"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
border-radius: 4px;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
[data-slot="icon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="price"] {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="amount"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="period"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="multiplier"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "·";
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="terms"] {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding-left: 16px;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "▪";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="actions"] {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
button,
|
||||||
|
a {
|
||||||
|
flex: 1;
|
||||||
|
display: inline-flex;
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 16px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="cancel"] {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="continue"] {
|
||||||
|
background: rgba(255, 255, 255, 0.17);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="fine-print"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 20.8px */
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subscribe page styles */
|
||||||
|
[data-slot="subscribe-form"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: -18px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 540px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="form-card"] {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="plan-header"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="title"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="icon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="price"] {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="amount"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="period"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="multiplier"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "·";
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="divider"] {
|
||||||
|
height: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.17);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="section-title"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="tax-id-section"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
[data-slot="label"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="input"] {
|
||||||
|
width: 100%;
|
||||||
|
height: 44px;
|
||||||
|
padding: 0 12px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.15s ease;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="checkout-form"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="error"] {
|
||||||
|
color: #ff6b6b;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="submit-button"] {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
background: rgba(255, 255, 255, 0.92);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #000;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="charge-notice"] {
|
||||||
|
color: #d4a500;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="success"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
[data-slot="title"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="details"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="charge-notice"] {
|
||||||
|
color: #d4a500;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="loading"] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="fine-print"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace-picker"] {
|
||||||
|
[data-slot="workspace-list"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
outline: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 240px;
|
||||||
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace-item"] {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 12px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "IBM Plex Mono", monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span:last-child {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&[data-active="true"] {
|
||||||
|
background: #161616;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="footer"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="footer-content"] {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span,
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="github-stars"] {
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="anomaly"] {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="anomaly-alt"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,43 @@
|
|||||||
|
import { Match, Switch } from "solid-js"
|
||||||
|
|
||||||
|
export const plans = [
|
||||||
|
{ id: "20", multiplier: null },
|
||||||
|
{ id: "100", multiplier: "6x more usage than Black 20" },
|
||||||
|
{ id: "200", multiplier: "21x more usage than Black 20" },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export type PlanID = (typeof plans)[number]["id"]
|
||||||
|
export type Plan = (typeof plans)[number]
|
||||||
|
|
||||||
|
export function PlanIcon(props: { plan: string }) {
|
||||||
|
return (
|
||||||
|
<Switch>
|
||||||
|
<Match when={props.plan === "20"}>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="4" y="4" width="16" height="16" rx="2" stroke="currentColor" stroke-width="1.5" />
|
||||||
|
</svg>
|
||||||
|
</Match>
|
||||||
|
<Match when={props.plan === "100"}>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.5" />
|
||||||
|
<rect x="14" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.5" />
|
||||||
|
<rect x="3" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.5" />
|
||||||
|
<rect x="14" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.5" />
|
||||||
|
</svg>
|
||||||
|
</Match>
|
||||||
|
<Match when={props.plan === "200"}>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="2" y="2" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="10" y="2" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="18" y="2" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="2" y="10" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="10" y="10" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="18" y="10" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="2" y="18" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="10" y="18" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
<rect x="18" y="18" width="4" height="4" rx="0.5" stroke="currentColor" stroke-width="1" />
|
||||||
|
</svg>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import { A, useSearchParams } from "@solidjs/router"
|
||||||
|
import { Title } from "@solidjs/meta"
|
||||||
|
import { createMemo, createSignal, For, Match, Show, Switch } from "solid-js"
|
||||||
|
import { PlanIcon, plans } from "./common"
|
||||||
|
|
||||||
|
export default function Black() {
|
||||||
|
const [params] = useSearchParams()
|
||||||
|
const [selected, setSelected] = createSignal<string | null>((params.plan as string) || null)
|
||||||
|
const selectedPlan = createMemo(() => plans.find((p) => p.id === selected()))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>opencode</Title>
|
||||||
|
<section data-slot="cta">
|
||||||
|
<Switch>
|
||||||
|
<Match when={!selected()}>
|
||||||
|
<div data-slot="pricing">
|
||||||
|
<For each={plans}>
|
||||||
|
{(plan) => (
|
||||||
|
<button type="button" onClick={() => setSelected(plan.id)} data-slot="pricing-card">
|
||||||
|
<div data-slot="icon">
|
||||||
|
<PlanIcon plan={plan.id} />
|
||||||
|
</div>
|
||||||
|
<p data-slot="price">
|
||||||
|
<span data-slot="amount">${plan.id}</span> <span data-slot="period">per month</span>
|
||||||
|
<Show when={plan.multiplier}>
|
||||||
|
<span data-slot="multiplier">{plan.multiplier}</span>
|
||||||
|
</Show>
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
<p data-slot="fine-print">
|
||||||
|
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||||
|
</p>
|
||||||
|
</Match>
|
||||||
|
<Match when={selectedPlan()}>
|
||||||
|
{(plan) => (
|
||||||
|
<div data-slot="selected-plan">
|
||||||
|
<div data-slot="selected-card">
|
||||||
|
<div data-slot="icon">
|
||||||
|
<PlanIcon plan={plan().id} />
|
||||||
|
</div>
|
||||||
|
<p data-slot="price">
|
||||||
|
<span data-slot="amount">${plan().id}</span>{" "}
|
||||||
|
<span data-slot="period">per person billed monthly</span>
|
||||||
|
<Show when={plan().multiplier}>
|
||||||
|
<span data-slot="multiplier">{plan().multiplier}</span>
|
||||||
|
</Show>
|
||||||
|
</p>
|
||||||
|
<ul data-slot="terms">
|
||||||
|
<li>Your subscription will not start immediately</li>
|
||||||
|
<li>You will be added to the waitlist and activated soon</li>
|
||||||
|
<li>Your card will be only charged when your subscription is activated</li>
|
||||||
|
<li>Usage limits apply, heavily automated use may reach limits sooner</li>
|
||||||
|
<li>Subscriptions for individuals, contact Enterprise for teams</li>
|
||||||
|
<li>Limits may be adjusted and plans may be discontinued in the future</li>
|
||||||
|
<li>Cancel your subscription at anytime</li>
|
||||||
|
</ul>
|
||||||
|
<div data-slot="actions">
|
||||||
|
<button type="button" onClick={() => setSelected(null)} data-slot="cancel">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<a href={`/black/subscribe/${plan().id}`} data-slot="continue">
|
||||||
|
Continue
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p data-slot="fine-print">
|
||||||
|
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,450 @@
|
|||||||
|
import { A, createAsync, query, redirect, useParams } from "@solidjs/router"
|
||||||
|
import { Title } from "@solidjs/meta"
|
||||||
|
import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js"
|
||||||
|
import { type Stripe, type PaymentMethod, loadStripe } from "@stripe/stripe-js"
|
||||||
|
import { Elements, PaymentElement, useStripe, useElements, AddressElement } from "solid-stripe"
|
||||||
|
import { PlanID, plans } from "../common"
|
||||||
|
import { getActor, useAuthSession } from "~/context/auth"
|
||||||
|
import { withActor } from "~/context/auth.withActor"
|
||||||
|
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||||
|
import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||||
|
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||||
|
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||||
|
import { createList } from "solid-list"
|
||||||
|
import { Modal } from "~/component/modal"
|
||||||
|
import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js"
|
||||||
|
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||||
|
|
||||||
|
const plansMap = Object.fromEntries(plans.map((p) => [p.id, p])) as Record<PlanID, (typeof plans)[number]>
|
||||||
|
const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY!)
|
||||||
|
|
||||||
|
const getWorkspaces = query(async () => {
|
||||||
|
"use server"
|
||||||
|
const actor = await getActor()
|
||||||
|
if (actor.type === "public") throw redirect("/auth/authorize?continue=/black/subscribe")
|
||||||
|
return withActor(async () => {
|
||||||
|
return Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
id: WorkspaceTable.id,
|
||||||
|
name: WorkspaceTable.name,
|
||||||
|
slug: WorkspaceTable.slug,
|
||||||
|
billing: {
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
paymentMethodID: BillingTable.paymentMethodID,
|
||||||
|
paymentMethodType: BillingTable.paymentMethodType,
|
||||||
|
paymentMethodLast4: BillingTable.paymentMethodLast4,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
timeSubscriptionBooked: BillingTable.timeSubscriptionBooked,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.from(UserTable)
|
||||||
|
.innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id))
|
||||||
|
.innerJoin(BillingTable, eq(WorkspaceTable.id, BillingTable.workspaceID))
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(UserTable.accountID, Actor.account()),
|
||||||
|
isNull(WorkspaceTable.timeDeleted),
|
||||||
|
isNull(UserTable.timeDeleted),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}, "black.subscribe.workspaces")
|
||||||
|
|
||||||
|
const createSetupIntent = async (input: { plan: string; workspaceID: string }) => {
|
||||||
|
"use server"
|
||||||
|
const { plan, workspaceID } = input
|
||||||
|
|
||||||
|
if (!plan || !["20", "100", "200"].includes(plan)) return { error: "Invalid plan" }
|
||||||
|
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||||
|
|
||||||
|
return withActor(async () => {
|
||||||
|
const session = await useAuthSession()
|
||||||
|
const account = session.data.account?.[session.data.current ?? ""]
|
||||||
|
const email = account?.email
|
||||||
|
|
||||||
|
const customer = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (customer?.subscriptionID) {
|
||||||
|
return { error: "This workspace already has a subscription" }
|
||||||
|
}
|
||||||
|
|
||||||
|
let customerID = customer?.customerID
|
||||||
|
if (!customerID) {
|
||||||
|
const customer = await Billing.stripe().customers.create({
|
||||||
|
email,
|
||||||
|
metadata: {
|
||||||
|
workspaceID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
customerID = customer.id
|
||||||
|
await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
customerID,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const intent = await Billing.stripe().setupIntents.create({
|
||||||
|
customer: customerID,
|
||||||
|
payment_method_types: ["card"],
|
||||||
|
metadata: {
|
||||||
|
workspaceID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return { clientSecret: intent.client_secret ?? undefined }
|
||||||
|
}, workspaceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bookSubscription = async (input: {
|
||||||
|
workspaceID: string
|
||||||
|
plan: PlanID
|
||||||
|
paymentMethodID: string
|
||||||
|
paymentMethodType: string
|
||||||
|
paymentMethodLast4?: string
|
||||||
|
}) => {
|
||||||
|
"use server"
|
||||||
|
return withActor(
|
||||||
|
() =>
|
||||||
|
Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
paymentMethodID: input.paymentMethodID,
|
||||||
|
paymentMethodType: input.paymentMethodType,
|
||||||
|
paymentMethodLast4: input.paymentMethodLast4,
|
||||||
|
subscriptionPlan: input.plan,
|
||||||
|
timeSubscriptionBooked: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, input.workspaceID)),
|
||||||
|
),
|
||||||
|
input.workspaceID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SuccessData {
|
||||||
|
plan: string
|
||||||
|
paymentMethodType: string
|
||||||
|
paymentMethodLast4?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Failure(props: { message: string }) {
|
||||||
|
return (
|
||||||
|
<div data-slot="failure">
|
||||||
|
<p data-slot="message">Uh oh! {props.message}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Success(props: SuccessData) {
|
||||||
|
return (
|
||||||
|
<div data-slot="success">
|
||||||
|
<p data-slot="title">You're on the OpenCode Black waitlist</p>
|
||||||
|
<dl data-slot="details">
|
||||||
|
<div>
|
||||||
|
<dt>Subscription plan</dt>
|
||||||
|
<dd>OpenCode Black {props.plan}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Amount</dt>
|
||||||
|
<dd>${props.plan} per month</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Payment method</dt>
|
||||||
|
<dd>
|
||||||
|
<Show when={props.paymentMethodLast4} fallback={<span>{props.paymentMethodType}</span>}>
|
||||||
|
<span>
|
||||||
|
{props.paymentMethodType} - {props.paymentMethodLast4}
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Date joined</dt>
|
||||||
|
<dd>{new Date().toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
<p data-slot="charge-notice">Your card will be charged when your subscription is activated</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function IntentForm(props: { plan: PlanID; workspaceID: string; onSuccess: (data: SuccessData) => void }) {
|
||||||
|
const stripe = useStripe()
|
||||||
|
const elements = useElements()
|
||||||
|
const [error, setError] = createSignal<string | undefined>(undefined)
|
||||||
|
const [loading, setLoading] = createSignal(false)
|
||||||
|
|
||||||
|
const handleSubmit = async (e: Event) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (!stripe() || !elements()) return
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
setError(undefined)
|
||||||
|
|
||||||
|
const result = await elements()!.submit()
|
||||||
|
if (result.error) {
|
||||||
|
setError(result.error.message ?? "An error occurred")
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error: confirmError, setupIntent } = await stripe()!.confirmSetup({
|
||||||
|
elements: elements()!,
|
||||||
|
confirmParams: {
|
||||||
|
expand: ["payment_method"],
|
||||||
|
payment_method_data: {
|
||||||
|
allow_redisplay: "always",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
redirect: "if_required",
|
||||||
|
})
|
||||||
|
|
||||||
|
if (confirmError) {
|
||||||
|
setError(confirmError.message ?? "An error occurred")
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
console.log(setupIntent)
|
||||||
|
if (setupIntent?.status === "succeeded") {
|
||||||
|
const pm = setupIntent.payment_method as PaymentMethod
|
||||||
|
|
||||||
|
await bookSubscription({
|
||||||
|
workspaceID: props.workspaceID,
|
||||||
|
plan: props.plan,
|
||||||
|
paymentMethodID: pm.id,
|
||||||
|
paymentMethodType: pm.type,
|
||||||
|
paymentMethodLast4: pm.card?.last4,
|
||||||
|
})
|
||||||
|
|
||||||
|
props.onSuccess({
|
||||||
|
plan: props.plan,
|
||||||
|
paymentMethodType: pm.type,
|
||||||
|
paymentMethodLast4: pm.card?.last4,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} data-slot="checkout-form">
|
||||||
|
<PaymentElement />
|
||||||
|
<AddressElement options={{ mode: "billing" }} />
|
||||||
|
<Show when={error()}>
|
||||||
|
<p data-slot="error">{error()}</p>
|
||||||
|
</Show>
|
||||||
|
<button type="submit" disabled={loading() || !stripe() || !elements()} data-slot="submit-button">
|
||||||
|
{loading() ? "Processing..." : `Subscribe $${props.plan}`}
|
||||||
|
</button>
|
||||||
|
<p data-slot="charge-notice">You will only be charged when your subscription is activated</p>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BlackSubscribe() {
|
||||||
|
const workspaces = createAsync(() => getWorkspaces())
|
||||||
|
const [selectedWorkspace, setSelectedWorkspace] = createSignal<string | undefined>(undefined)
|
||||||
|
const [success, setSuccess] = createSignal<SuccessData | undefined>(undefined)
|
||||||
|
const [failure, setFailure] = createSignal<string | undefined>(undefined)
|
||||||
|
const [clientSecret, setClientSecret] = createSignal<string | undefined>(undefined)
|
||||||
|
const [stripe, setStripe] = createSignal<Stripe | undefined>(undefined)
|
||||||
|
const params = useParams()
|
||||||
|
const planData = plansMap[(params.plan as PlanID) ?? "20"] ?? plansMap["20"]
|
||||||
|
const plan = planData.id
|
||||||
|
|
||||||
|
// Resolve stripe promise once
|
||||||
|
createEffect(() => {
|
||||||
|
stripePromise.then((s) => {
|
||||||
|
if (s) setStripe(s)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Auto-select if only one workspace
|
||||||
|
createEffect(() => {
|
||||||
|
const ws = workspaces()
|
||||||
|
if (ws?.length === 1 && !selectedWorkspace()) {
|
||||||
|
setSelectedWorkspace(ws[0].id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fetch setup intent when workspace is selected (unless workspace already has payment method)
|
||||||
|
createEffect(async () => {
|
||||||
|
const id = selectedWorkspace()
|
||||||
|
if (!id) return
|
||||||
|
|
||||||
|
const ws = workspaces()?.find((w) => w.id === id)
|
||||||
|
if (ws?.billing?.subscriptionID) {
|
||||||
|
setFailure("This workspace already has a subscription")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ws?.billing?.paymentMethodID) {
|
||||||
|
if (!ws?.billing?.timeSubscriptionBooked) {
|
||||||
|
await bookSubscription({
|
||||||
|
workspaceID: id,
|
||||||
|
plan: planData.id,
|
||||||
|
paymentMethodID: ws.billing.paymentMethodID!,
|
||||||
|
paymentMethodType: ws.billing.paymentMethodType!,
|
||||||
|
paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setSuccess({
|
||||||
|
plan: planData.id,
|
||||||
|
paymentMethodType: ws.billing.paymentMethodType!,
|
||||||
|
paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await createSetupIntent({ plan, workspaceID: id })
|
||||||
|
if (result.error) {
|
||||||
|
setFailure(result.error)
|
||||||
|
} else if ("clientSecret" in result) {
|
||||||
|
setClientSecret(result.clientSecret)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Keyboard navigation for workspace picker
|
||||||
|
const { active, setActive, onKeyDown } = createList({
|
||||||
|
items: () => workspaces()?.map((w) => w.id) ?? [],
|
||||||
|
initialActive: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSelectWorkspace = (id: string) => {
|
||||||
|
setSelectedWorkspace(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
let listRef: HTMLUListElement | undefined
|
||||||
|
|
||||||
|
// Show workspace picker if multiple workspaces and none selected
|
||||||
|
const showWorkspacePicker = () => {
|
||||||
|
const ws = workspaces()
|
||||||
|
return ws && ws.length > 1 && !selectedWorkspace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>Subscribe to OpenCode Black</Title>
|
||||||
|
<section data-slot="subscribe-form">
|
||||||
|
<div data-slot="form-card">
|
||||||
|
<Switch>
|
||||||
|
<Match when={success()}>{(data) => <Success {...data()} />}</Match>
|
||||||
|
<Match when={failure()}>{(data) => <Failure message={data()} />}</Match>
|
||||||
|
<Match when={true}>
|
||||||
|
<>
|
||||||
|
<div data-slot="plan-header">
|
||||||
|
<p data-slot="title">Subscribe to OpenCode Black</p>
|
||||||
|
<p data-slot="price">
|
||||||
|
<span data-slot="amount">${planData.id}</span> <span data-slot="period">per month</span>
|
||||||
|
<Show when={planData.multiplier}>
|
||||||
|
<span data-slot="multiplier">{planData.multiplier}</span>
|
||||||
|
</Show>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div data-slot="divider" />
|
||||||
|
<p data-slot="section-title">Payment method</p>
|
||||||
|
|
||||||
|
<Show
|
||||||
|
when={clientSecret() && selectedWorkspace() && stripe()}
|
||||||
|
fallback={
|
||||||
|
<div data-slot="loading">
|
||||||
|
<p>{selectedWorkspace() ? "Loading payment form..." : "Select a workspace to continue"}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Elements
|
||||||
|
stripe={stripe()!}
|
||||||
|
options={{
|
||||||
|
clientSecret: clientSecret()!,
|
||||||
|
appearance: {
|
||||||
|
theme: "night",
|
||||||
|
variables: {
|
||||||
|
colorPrimary: "#ffffff",
|
||||||
|
colorBackground: "#1a1a1a",
|
||||||
|
colorText: "#ffffff",
|
||||||
|
colorTextSecondary: "#999999",
|
||||||
|
colorDanger: "#ff6b6b",
|
||||||
|
fontFamily: "JetBrains Mono, monospace",
|
||||||
|
borderRadius: "4px",
|
||||||
|
spacingUnit: "4px",
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
".Input": {
|
||||||
|
backgroundColor: "#1a1a1a",
|
||||||
|
border: "1px solid rgba(255, 255, 255, 0.17)",
|
||||||
|
color: "#ffffff",
|
||||||
|
},
|
||||||
|
".Input:focus": {
|
||||||
|
borderColor: "rgba(255, 255, 255, 0.35)",
|
||||||
|
boxShadow: "none",
|
||||||
|
},
|
||||||
|
".Label": {
|
||||||
|
color: "rgba(255, 255, 255, 0.59)",
|
||||||
|
fontSize: "14px",
|
||||||
|
marginBottom: "8px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IntentForm plan={plan} workspaceID={selectedWorkspace()!} onSuccess={setSuccess} />
|
||||||
|
</Elements>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Workspace picker modal */}
|
||||||
|
<Modal open={showWorkspacePicker() ?? false} onClose={() => {}} title="Select a workspace for this plan">
|
||||||
|
<div data-slot="workspace-picker">
|
||||||
|
<ul
|
||||||
|
ref={listRef}
|
||||||
|
data-slot="workspace-list"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" && active()) {
|
||||||
|
handleSelectWorkspace(active()!)
|
||||||
|
} else {
|
||||||
|
onKeyDown(e)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<For each={workspaces()}>
|
||||||
|
{(workspace) => (
|
||||||
|
<li
|
||||||
|
data-slot="workspace-item"
|
||||||
|
data-active={active() === workspace.id}
|
||||||
|
onMouseEnter={() => setActive(workspace.id)}
|
||||||
|
onClick={() => handleSelectWorkspace(workspace.id)}
|
||||||
|
>
|
||||||
|
<span data-slot="selected-icon">[*]</span>
|
||||||
|
<span>{workspace.name || workspace.slug}</span>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<p data-slot="fine-print">
|
||||||
|
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
[data-page="black"] {
|
||||||
|
background: #000;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: stretch;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
[data-component="header-gradient"] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 288px;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="header"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
/* [data-component="header-logo"] { */
|
||||||
|
/* } */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="content"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
[data-slot="hero-black"] {
|
||||||
|
margin-top: 110px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="select-workspace"] {
|
||||||
|
display: flex;
|
||||||
|
margin-top: -24px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 480px;
|
||||||
|
height: 305px;
|
||||||
|
padding: 32px 20px 0 20px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
border: 1px solid #303030;
|
||||||
|
background: #0a0a0a;
|
||||||
|
box-shadow:
|
||||||
|
0 100px 80px 0 rgba(0, 0, 0, 0.04),
|
||||||
|
0 41.778px 33.422px 0 rgba(0, 0, 0, 0.05),
|
||||||
|
0 22.336px 17.869px 0 rgba(0, 0, 0, 0.06),
|
||||||
|
0 12.522px 10.017px 0 rgba(0, 0, 0, 0.08),
|
||||||
|
0 6.65px 5.32px 0 rgba(0, 0, 0, 0.09),
|
||||||
|
0 2.767px 2.214px 0 rgba(0, 0, 0, 0.13);
|
||||||
|
|
||||||
|
[data-slot="select-workspace-title"] {
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: stretch;
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 25.6px */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspaces"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
outline: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
scrollbar-width: none;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace"] {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 12px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "IBM Plex Mono";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 25.6px */
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 25.6px */
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace"]:hover,
|
||||||
|
[data-slot="workspace"][data-active="true"] {
|
||||||
|
background: #161616;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="footer"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="footer-content"] {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span,
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="github-stars"] {
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="anomaly"] {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="anomaly-alt"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -24,7 +24,7 @@ export async function GET({ params: { platform } }: APIEvent) {
|
|||||||
const resp = await fetch(`https://github.com/anomalyco/opencode/releases/latest/download/${assetName}`, {
|
const resp = await fetch(`https://github.com/anomalyco/opencode/releases/latest/download/${assetName}`, {
|
||||||
cf: {
|
cf: {
|
||||||
// in case gh releases has rate limits
|
// in case gh releases has rate limits
|
||||||
cacheTtl: 60 * 60 * 24,
|
cacheTtl: 60 * 5,
|
||||||
cacheEverything: true,
|
cacheEverything: true,
|
||||||
},
|
},
|
||||||
} as any)
|
} as any)
|
||||||
|
|||||||
@@ -244,7 +244,8 @@ export default function Download() {
|
|||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div data-component="download-row">
|
{/* Disabled temporarily as it doesn't work */}
|
||||||
|
{/*<div data-component="download-row">
|
||||||
<div data-component="download-info">
|
<div data-component="download-info">
|
||||||
<span data-slot="icon">
|
<span data-slot="icon">
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -259,7 +260,7 @@ export default function Download() {
|
|||||||
<a href={getDownloadHref("linux-x64-appimage")} data-component="action-button">
|
<a href={getDownloadHref("linux-x64-appimage")} data-component="action-button">
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>*/}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ body {
|
|||||||
[data-slot="content"] {
|
[data-slot="content"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 1ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="text"] {
|
[data-slot="text"] {
|
||||||
|
|||||||
@@ -195,6 +195,12 @@ export default function Home() {
|
|||||||
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>[*]</span>
|
||||||
|
<div>
|
||||||
|
<strong>ChatGPT Plus/Pro</strong> Log in with OpenAI to use your ChatGPT Plus or Pro account
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span>[*]</span>
|
<span>[*]</span>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ export default function Home() {
|
|||||||
<li>
|
<li>
|
||||||
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>ChatGPT Plus/Pro</strong> Log in with OpenAI to use your ChatGPT Plus or Pro account
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Use any model</strong> Supports 75+ LLM providers through{" "}
|
<strong>Use any model</strong> Supports 75+ LLM providers through{" "}
|
||||||
<a href="https://models.dev">Models.dev</a>, including local models
|
<a href="https://models.dev">Models.dev</a>, including local models
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { action } from "@solidjs/router"
|
import { action } from "@solidjs/router"
|
||||||
import { getRequestEvent } from "solid-js/web"
|
import { getRequestEvent } from "solid-js/web"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
import { Dropdown } from "~/component/dropdown"
|
import { Dropdown } from "~/component/dropdown"
|
||||||
import "./user-menu.css"
|
import "./user-menu.css"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `billing` ADD `time_subscription_booked` timestamp(3);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `billing` ADD `subscription_plan` enum('20','100','200');
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -358,6 +358,20 @@
|
|||||||
"when": 1767931290031,
|
"when": 1767931290031,
|
||||||
"tag": "0050_bumpy_mephistopheles",
|
"tag": "0050_bumpy_mephistopheles",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 51,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1768341152722,
|
||||||
|
"tag": "0051_jazzy_green_goblin",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 52,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1768343920467,
|
||||||
|
"tag": "0052_aromatic_agent_zero",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.1.8",
|
"version": "1.1.19",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -38,10 +38,21 @@ if (identifier.startsWith("wrk_")) {
|
|||||||
workspaceID: UserTable.workspaceID,
|
workspaceID: UserTable.workspaceID,
|
||||||
workspaceName: WorkspaceTable.name,
|
workspaceName: WorkspaceTable.name,
|
||||||
role: UserTable.role,
|
role: UserTable.role,
|
||||||
|
subscribed: SubscriptionTable.timeCreated,
|
||||||
})
|
})
|
||||||
.from(UserTable)
|
.from(UserTable)
|
||||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
|
.rightJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
|
||||||
.where(eq(UserTable.accountID, accountID)),
|
.leftJoin(SubscriptionTable, eq(SubscriptionTable.userID, UserTable.id))
|
||||||
|
.where(eq(UserTable.accountID, accountID))
|
||||||
|
.then((rows) =>
|
||||||
|
rows.map((row) => ({
|
||||||
|
userID: row.userID,
|
||||||
|
workspaceID: row.workspaceID,
|
||||||
|
workspaceName: row.workspaceName,
|
||||||
|
role: row.role,
|
||||||
|
subscribed: formatDate(row.subscribed),
|
||||||
|
})),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get all payments for these workspaces
|
// Get all payments for these workspaces
|
||||||
@@ -102,6 +113,8 @@ async function printWorkspace(workspaceID: string) {
|
|||||||
.select({
|
.select({
|
||||||
balance: BillingTable.balance,
|
balance: BillingTable.balance,
|
||||||
customerID: BillingTable.customerID,
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
subscriptionCouponID: BillingTable.subscriptionCouponID,
|
||||||
})
|
})
|
||||||
.from(BillingTable)
|
.from(BillingTable)
|
||||||
.where(eq(BillingTable.workspaceID, workspace.id))
|
.where(eq(BillingTable.workspaceID, workspace.id))
|
||||||
@@ -138,6 +151,7 @@ async function printWorkspace(workspaceID: string) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
await printTable("Usage", (tx) =>
|
await printTable("Usage", (tx) =>
|
||||||
tx
|
tx
|
||||||
.select({
|
.select({
|
||||||
@@ -163,6 +177,7 @@ async function printWorkspace(workspaceID: string) {
|
|||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMicroCents(value: number | null | undefined) {
|
function formatMicroCents(value: number | null | undefined) {
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { Billing } from "../src/billing.js"
|
||||||
|
import { and, Database, eq } from "../src/drizzle/index.js"
|
||||||
|
import { BillingTable, PaymentTable, SubscriptionTable } from "../src/schema/billing.sql.js"
|
||||||
|
|
||||||
|
const workspaceID = process.argv[2]
|
||||||
|
|
||||||
|
if (!workspaceID) {
|
||||||
|
console.error("Usage: bun remove-black.ts <workspaceID>")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Removing subscription from workspace ${workspaceID}`)
|
||||||
|
|
||||||
|
// Look up the workspace billing
|
||||||
|
const billing = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!billing) {
|
||||||
|
console.error(`Error: No billing record found for workspace ${workspaceID}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!billing.subscriptionID) {
|
||||||
|
console.error(`Error: Workspace ${workspaceID} does not have a subscription`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(` Customer ID: ${billing.customerID}`)
|
||||||
|
console.log(` Subscription ID: ${billing.subscriptionID}`)
|
||||||
|
|
||||||
|
// Clear workspaceID from Stripe customer metadata
|
||||||
|
if (billing.customerID) {
|
||||||
|
//await Billing.stripe().customers.update(billing.customerID, {
|
||||||
|
// metadata: {
|
||||||
|
// workspaceID: "",
|
||||||
|
// },
|
||||||
|
//})
|
||||||
|
//console.log(`Cleared workspaceID from Stripe customer metadata`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await Database.transaction(async (tx) => {
|
||||||
|
// Clear subscription-related fields from billing table
|
||||||
|
await tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
// customerID: null,
|
||||||
|
subscriptionID: null,
|
||||||
|
subscriptionCouponID: null,
|
||||||
|
// paymentMethodID: null,
|
||||||
|
// paymentMethodLast4: null,
|
||||||
|
// paymentMethodType: null,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
|
||||||
|
// Delete from subscription table
|
||||||
|
await tx.delete(SubscriptionTable).where(eq(SubscriptionTable.workspaceID, workspaceID))
|
||||||
|
|
||||||
|
// Delete from payments table
|
||||||
|
await tx
|
||||||
|
.delete(PaymentTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(PaymentTable.workspaceID, workspaceID),
|
||||||
|
eq(PaymentTable.enrichment, { type: "subscription" }),
|
||||||
|
eq(PaymentTable.amount, 20000000000),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`Successfully removed subscription from workspace ${workspaceID}`)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { bigint, boolean, index, int, json, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
import { bigint, boolean, index, int, json, mysqlEnum, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||||
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
|
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
|
||||||
import { workspaceIndexes } from "./workspace.sql"
|
import { workspaceIndexes } from "./workspace.sql"
|
||||||
|
|
||||||
@@ -23,6 +23,8 @@ export const BillingTable = mysqlTable(
|
|||||||
timeReloadLockedTill: utc("time_reload_locked_till"),
|
timeReloadLockedTill: utc("time_reload_locked_till"),
|
||||||
subscriptionID: varchar("subscription_id", { length: 28 }),
|
subscriptionID: varchar("subscription_id", { length: 28 }),
|
||||||
subscriptionCouponID: varchar("subscription_coupon_id", { length: 28 }),
|
subscriptionCouponID: varchar("subscription_coupon_id", { length: 28 }),
|
||||||
|
subscriptionPlan: mysqlEnum("subscription_plan", ["20", "100", "200"] as const),
|
||||||
|
timeSubscriptionBooked: utc("time_subscription_booked"),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
...workspaceIndexes(table),
|
...workspaceIndexes(table),
|
||||||
|
|||||||
Vendored
+8
@@ -78,6 +78,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
"STRIPE_SECRET_KEY": {
|
"STRIPE_SECRET_KEY": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
@@ -130,6 +134,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cloudflare
|
// cloudflare
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+8
@@ -78,6 +78,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
"STRIPE_SECRET_KEY": {
|
"STRIPE_SECRET_KEY": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
@@ -130,6 +134,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cloudflare
|
// cloudflare
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"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",
|
||||||
|
|||||||
+8
@@ -78,6 +78,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
"STRIPE_SECRET_KEY": {
|
"STRIPE_SECRET_KEY": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
@@ -130,6 +134,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cloudflare
|
// cloudflare
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
|
|
||||||
import { copyBinaryToSidecarFolder, getCurrentSidecar } from "./utils"
|
import { copyBinaryToSidecarFolder, getCurrentSidecar, windowsify } from "./utils"
|
||||||
|
|
||||||
const RUST_TARGET = Bun.env.TAURI_ENV_TARGET_TRIPLE
|
const RUST_TARGET = Bun.env.TAURI_ENV_TARGET_TRIPLE
|
||||||
|
|
||||||
const sidecarConfig = getCurrentSidecar(RUST_TARGET)
|
const sidecarConfig = getCurrentSidecar(RUST_TARGET)
|
||||||
|
|
||||||
const binaryPath = `../opencode/dist/${sidecarConfig.ocBinary}/bin/opencode${process.platform === "win32" ? ".exe" : ""}`
|
const binaryPath = windowsify(`../opencode/dist/${sidecarConfig.ocBinary}/bin/opencode`)
|
||||||
|
|
||||||
await $`cd ../opencode && bun run build --single`
|
await $`cd ../opencode && bun run build --single`
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
|
|
||||||
import { copyBinaryToSidecarFolder, getCurrentSidecar } from "./utils"
|
import { copyBinaryToSidecarFolder, getCurrentSidecar, windowsify } from "./utils"
|
||||||
|
|
||||||
const sidecarConfig = getCurrentSidecar()
|
const sidecarConfig = getCurrentSidecar()
|
||||||
|
|
||||||
@@ -10,6 +10,4 @@ const dir = "src-tauri/target/opencode-binaries"
|
|||||||
await $`mkdir -p ${dir}`
|
await $`mkdir -p ${dir}`
|
||||||
await $`gh run download ${Bun.env.GITHUB_RUN_ID} -n opencode-cli`.cwd(dir)
|
await $`gh run download ${Bun.env.GITHUB_RUN_ID} -n opencode-cli`.cwd(dir)
|
||||||
|
|
||||||
await copyBinaryToSidecarFolder(
|
await copyBinaryToSidecarFolder(windowsify(`${dir}/${sidecarConfig.ocBinary}/bin/opencode`))
|
||||||
`${dir}/${sidecarConfig.ocBinary}/bin/opencode${process.platform === "win32" ? ".exe" : ""}`,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -41,8 +41,13 @@ export function getCurrentSidecar(target = RUST_TARGET) {
|
|||||||
|
|
||||||
export async function copyBinaryToSidecarFolder(source: string, target = RUST_TARGET) {
|
export async function copyBinaryToSidecarFolder(source: string, target = RUST_TARGET) {
|
||||||
await $`mkdir -p src-tauri/sidecars`
|
await $`mkdir -p src-tauri/sidecars`
|
||||||
const dest = `src-tauri/sidecars/opencode-cli-${target}${process.platform === "win32" ? ".exe" : ""}`
|
const dest = windowsify(`src-tauri/sidecars/opencode-cli-${target}`)
|
||||||
await $`cp ${source} ${dest}`
|
await $`cp ${source} ${dest}`
|
||||||
|
|
||||||
console.log(`Copied ${source} to ${dest}`)
|
console.log(`Copied ${source} to ${dest}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function windowsify(path: string) {
|
||||||
|
if (path.endsWith(".exe")) return path
|
||||||
|
return `${path}${process.platform === "win32" ? ".exe" : ""}`
|
||||||
|
}
|
||||||
|
|||||||
Generated
+6
-3
@@ -2795,6 +2795,7 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"gtk",
|
"gtk",
|
||||||
"listeners",
|
"listeners",
|
||||||
|
"reqwest",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2813,7 +2814,9 @@ dependencies = [
|
|||||||
"tauri-plugin-updater",
|
"tauri-plugin-updater",
|
||||||
"tauri-plugin-window-state",
|
"tauri-plugin-window-state",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"uuid",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5363,13 +5366,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.18.1"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"serde",
|
"serde_core",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name = "opencode-desktop"
|
|||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
description = "The open source AI coding agent"
|
description = "The open source AI coding agent"
|
||||||
authors = ["Anomaly Innovations"]
|
authors = ["Anomaly Innovations"]
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -38,7 +38,17 @@ listeners = "0.3"
|
|||||||
tauri-plugin-os = "2"
|
tauri-plugin-os = "2"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
semver = "1.0.27"
|
semver = "1.0.27"
|
||||||
|
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
|
||||||
|
uuid = { version = "1.19.0", features = ["v4"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
gtk = "0.18.2"
|
gtk = "0.18.2"
|
||||||
webkit2gtk = "=2.0.1"
|
webkit2gtk = "=2.0.1"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
windows = { version = "0.61", features = [
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_System_JobObjects",
|
||||||
|
"Win32_System_Threading",
|
||||||
|
"Win32_Security"
|
||||||
|
] }
|
||||||
|
|||||||
@@ -1,8 +1,30 @@
|
|||||||
use tauri::Manager;
|
use tauri::{path::BaseDirectory, AppHandle, Manager};
|
||||||
|
use tauri_plugin_shell::{process::Command, ShellExt};
|
||||||
|
|
||||||
const CLI_INSTALL_DIR: &str = ".opencode/bin";
|
const CLI_INSTALL_DIR: &str = ".opencode/bin";
|
||||||
const CLI_BINARY_NAME: &str = "opencode";
|
const CLI_BINARY_NAME: &str = "opencode";
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct ServerConfig {
|
||||||
|
pub hostname: Option<String>,
|
||||||
|
pub port: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub server: Option<ServerConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_config(app: &AppHandle) -> Option<Config> {
|
||||||
|
create_command(app, "debug config")
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| eprintln!("Failed to read OC config: {e}"))
|
||||||
|
.ok()
|
||||||
|
.and_then(|out| String::from_utf8(out.stdout.to_vec()).ok())
|
||||||
|
.and_then(|s| serde_json::from_str::<Config>(&s).ok())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_cli_install_path() -> Option<std::path::PathBuf> {
|
fn get_cli_install_path() -> Option<std::path::PathBuf> {
|
||||||
std::env::var("HOME").ok().map(|home| {
|
std::env::var("HOME").ok().map(|home| {
|
||||||
std::path::PathBuf::from(home)
|
std::path::PathBuf::from(home)
|
||||||
@@ -117,3 +139,36 @@ pub fn sync_cli(app: tauri::AppHandle) -> Result<(), String> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_user_shell() -> String {
|
||||||
|
std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_command(app: &tauri::AppHandle, args: &str) -> Command {
|
||||||
|
let state_dir = app
|
||||||
|
.path()
|
||||||
|
.resolve("", BaseDirectory::AppLocalData)
|
||||||
|
.expect("Failed to resolve app local data dir");
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
return app
|
||||||
|
.shell()
|
||||||
|
.sidecar("opencode-cli")
|
||||||
|
.unwrap()
|
||||||
|
.args(args.split_whitespace())
|
||||||
|
.env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true")
|
||||||
|
.env("OPENCODE_CLIENT", "desktop")
|
||||||
|
.env("XDG_STATE_HOME", &state_dir);
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
return {
|
||||||
|
let sidecar = get_sidecar_path(app);
|
||||||
|
let shell = get_user_shell();
|
||||||
|
app.shell()
|
||||||
|
.command(&shell)
|
||||||
|
.env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true")
|
||||||
|
.env("OPENCODE_CLIENT", "desktop")
|
||||||
|
.env("XDG_STATE_HOME", &state_dir)
|
||||||
|
.args(["-il", "-c", &format!("\"{}\" {}", sidecar.display(), args)])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
//! Windows Job Object for reliable child process cleanup.
|
||||||
|
//!
|
||||||
|
//! This module provides a wrapper around Windows Job Objects with the
|
||||||
|
//! `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` flag set. When the job object handle
|
||||||
|
//! is closed (including when the parent process exits or crashes), Windows
|
||||||
|
//! automatically terminates all processes assigned to the job.
|
||||||
|
//!
|
||||||
|
//! This is more reliable than manual cleanup because it works even if:
|
||||||
|
//! - The parent process crashes
|
||||||
|
//! - The parent is killed via Task Manager
|
||||||
|
//! - The RunEvent::Exit handler fails to run
|
||||||
|
|
||||||
|
use std::io::{Error, Result};
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
||||||
|
use windows::Win32::System::JobObjects::{
|
||||||
|
AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
|
||||||
|
JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation,
|
||||||
|
SetInformationJobObject,
|
||||||
|
};
|
||||||
|
use windows::Win32::System::Threading::{OpenProcess, PROCESS_SET_QUOTA, PROCESS_TERMINATE};
|
||||||
|
|
||||||
|
/// A Windows Job Object configured to kill all assigned processes when closed.
|
||||||
|
///
|
||||||
|
/// When this struct is dropped or when the owning process exits (even abnormally),
|
||||||
|
/// Windows will automatically terminate all processes that have been assigned to it.
|
||||||
|
pub struct JobObject(HANDLE);
|
||||||
|
|
||||||
|
// SAFETY: HANDLE is just a pointer-sized value, and Windows job objects
|
||||||
|
// can be safely accessed from multiple threads.
|
||||||
|
unsafe impl Send for JobObject {}
|
||||||
|
unsafe impl Sync for JobObject {}
|
||||||
|
|
||||||
|
impl JobObject {
|
||||||
|
/// Creates a new anonymous job object with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` set.
|
||||||
|
///
|
||||||
|
/// When the last handle to this job is closed (including on process exit),
|
||||||
|
/// Windows will terminate all processes assigned to the job.
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
unsafe {
|
||||||
|
// Create an anonymous job object
|
||||||
|
let job = CreateJobObjectW(None, None).map_err(|e| Error::other(e.message()))?;
|
||||||
|
|
||||||
|
// Configure the job to kill all processes when the handle is closed
|
||||||
|
let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
|
||||||
|
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||||
|
|
||||||
|
SetInformationJobObject(
|
||||||
|
job,
|
||||||
|
JobObjectExtendedLimitInformation,
|
||||||
|
&info as *const _ as *const std::ffi::c_void,
|
||||||
|
std::mem::size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
|
||||||
|
)
|
||||||
|
.map_err(|e| Error::other(e.message()))?;
|
||||||
|
|
||||||
|
Ok(Self(job))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assigns a process to this job object by its process ID.
|
||||||
|
///
|
||||||
|
/// Once assigned, the process will be terminated when this job object is dropped
|
||||||
|
/// or when the owning process exits.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `pid` - The process ID of the process to assign
|
||||||
|
pub fn assign_pid(&self, pid: u32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
// Open a handle to the process with the minimum required permissions
|
||||||
|
// PROCESS_SET_QUOTA and PROCESS_TERMINATE are required by AssignProcessToJobObject
|
||||||
|
let process = OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, false, pid)
|
||||||
|
.map_err(|e| Error::other(e.message()))?;
|
||||||
|
|
||||||
|
// Assign the process to the job
|
||||||
|
let result = AssignProcessToJobObject(self.0, process);
|
||||||
|
|
||||||
|
// Close our handle to the process - the job object maintains its own reference
|
||||||
|
let _ = CloseHandle(process);
|
||||||
|
|
||||||
|
result.map_err(|e| Error::other(e.message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for JobObject {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// When this handle is closed and it's the last handle to the job,
|
||||||
|
// Windows will terminate all processes in the job due to KILL_ON_JOB_CLOSE
|
||||||
|
let _ = CloseHandle(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Holds the Windows Job Object that ensures child processes are killed when the app exits.
|
||||||
|
/// On Windows, when the job object handle is closed (including on crash), all assigned
|
||||||
|
/// processes are automatically terminated by the OS.
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub struct JobObjectState {
|
||||||
|
job: Mutex<Option<JobObject>>,
|
||||||
|
error: Mutex<Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl JobObjectState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
match JobObject::new() {
|
||||||
|
Ok(job) => Self {
|
||||||
|
job: Mutex::new(Some(job)),
|
||||||
|
error: Mutex::new(None),
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to create job object: {e}");
|
||||||
|
Self {
|
||||||
|
job: Mutex::new(None),
|
||||||
|
error: Mutex::new(Some(format!("Failed to create job object: {e}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_pid(&self, pid: u32) {
|
||||||
|
if let Some(job) = self.job.lock().unwrap().as_ref() {
|
||||||
|
if let Err(e) = job.assign_pid(pid) {
|
||||||
|
eprintln!("Failed to assign process {pid} to job object: {e}");
|
||||||
|
*self.error.lock().unwrap() =
|
||||||
|
Some(format!("Failed to assign process to job object: {e}"));
|
||||||
|
} else {
|
||||||
|
println!("Assigned process {pid} to job object for automatic cleanup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_job_object_creation() {
|
||||||
|
let job = JobObject::new();
|
||||||
|
assert!(job.is_ok(), "Failed to create job object: {:?}", job.err());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +1,46 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod job_object;
|
||||||
mod window_customizer;
|
mod window_customizer;
|
||||||
|
|
||||||
use cli::{get_sidecar_path, install_cli, sync_cli};
|
use cli::{install_cli, sync_cli};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
use futures::future;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use job_object::*;
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
net::{SocketAddr, TcpListener},
|
net::TcpListener,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use tauri::{
|
use tauri::{AppHandle, LogicalSize, Manager, RunEvent, State, WebviewUrl, WebviewWindow};
|
||||||
path::BaseDirectory, AppHandle, LogicalSize, Manager, RunEvent, State, WebviewUrl,
|
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogResult};
|
||||||
WebviewWindow,
|
|
||||||
};
|
|
||||||
use tauri_plugin_shell::process::{CommandChild, CommandEvent};
|
use tauri_plugin_shell::process::{CommandChild, CommandEvent};
|
||||||
use tauri_plugin_shell::ShellExt;
|
use tauri_plugin_store::StoreExt;
|
||||||
use tokio::net::TcpSocket;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::window_customizer::PinchZoomDisablePlugin;
|
use crate::window_customizer::PinchZoomDisablePlugin;
|
||||||
|
|
||||||
|
const SETTINGS_STORE: &str = "opencode.settings.dat";
|
||||||
|
const DEFAULT_SERVER_URL_KEY: &str = "defaultServerUrl";
|
||||||
|
|
||||||
|
#[derive(Clone, serde::Serialize)]
|
||||||
|
struct ServerReadyData {
|
||||||
|
url: String,
|
||||||
|
password: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct ServerState {
|
struct ServerState {
|
||||||
child: Arc<Mutex<Option<CommandChild>>>,
|
child: Arc<Mutex<Option<CommandChild>>>,
|
||||||
status: futures::future::Shared<tokio::sync::oneshot::Receiver<Result<(), String>>>,
|
status: future::Shared<oneshot::Receiver<Result<ServerReadyData, String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerState {
|
impl ServerState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
child: Option<CommandChild>,
|
child: Option<CommandChild>,
|
||||||
status: tokio::sync::oneshot::Receiver<Result<(), String>>,
|
status: oneshot::Receiver<Result<ServerReadyData, String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
child: Arc::new(Mutex::new(child)),
|
child: Arc::new(Mutex::new(child)),
|
||||||
@@ -80,7 +92,7 @@ async fn get_logs(app: AppHandle) -> Result<String, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn ensure_server_started(state: State<'_, ServerState>) -> Result<(), String> {
|
async fn ensure_server_ready(state: State<'_, ServerState>) -> Result<ServerReadyData, String> {
|
||||||
state
|
state
|
||||||
.status
|
.status
|
||||||
.clone()
|
.clone()
|
||||||
@@ -88,6 +100,41 @@ async fn ensure_server_started(state: State<'_, ServerState>) -> Result<(), Stri
|
|||||||
.map_err(|_| "Failed to get server status".to_string())?
|
.map_err(|_| "Failed to get server status".to_string())?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_default_server_url(app: AppHandle) -> Result<Option<String>, String> {
|
||||||
|
let store = app
|
||||||
|
.store(SETTINGS_STORE)
|
||||||
|
.map_err(|e| format!("Failed to open settings store: {}", e))?;
|
||||||
|
|
||||||
|
let value = store.get(DEFAULT_SERVER_URL_KEY);
|
||||||
|
match value {
|
||||||
|
Some(v) => Ok(v.as_str().map(String::from)),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn set_default_server_url(app: AppHandle, url: Option<String>) -> Result<(), String> {
|
||||||
|
let store = app
|
||||||
|
.store(SETTINGS_STORE)
|
||||||
|
.map_err(|e| format!("Failed to open settings store: {}", e))?;
|
||||||
|
|
||||||
|
match url {
|
||||||
|
Some(u) => {
|
||||||
|
store.set(DEFAULT_SERVER_URL_KEY, serde_json::Value::String(u));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
store.delete(DEFAULT_SERVER_URL_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store
|
||||||
|
.save()
|
||||||
|
.map_err(|e| format!("Failed to save settings: {}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_sidecar_port() -> u32 {
|
fn get_sidecar_port() -> u32 {
|
||||||
option_env!("OPENCODE_PORT")
|
option_env!("OPENCODE_PORT")
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
@@ -102,49 +149,17 @@ fn get_sidecar_port() -> u32 {
|
|||||||
}) as u32
|
}) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_user_shell() -> String {
|
fn spawn_sidecar(app: &AppHandle, port: u32, password: &str) -> CommandChild {
|
||||||
std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild {
|
|
||||||
let log_state = app.state::<LogState>();
|
let log_state = app.state::<LogState>();
|
||||||
let log_state_clone = log_state.inner().clone();
|
let log_state_clone = log_state.inner().clone();
|
||||||
|
|
||||||
let state_dir = app
|
println!("spawning sidecar on port {port}");
|
||||||
.path()
|
|
||||||
.resolve("", BaseDirectory::AppLocalData)
|
|
||||||
.expect("Failed to resolve app local data dir");
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
let (mut rx, child) = cli::create_command(app, format!("serve --port {port}").as_str())
|
||||||
let (mut rx, child) = app
|
.env("OPENCODE_SERVER_PASSWORD", password)
|
||||||
.shell()
|
|
||||||
.sidecar("opencode-cli")
|
|
||||||
.unwrap()
|
|
||||||
.env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true")
|
|
||||||
.env("OPENCODE_CLIENT", "desktop")
|
|
||||||
.env("XDG_STATE_HOME", &state_dir)
|
|
||||||
.args(["serve", &format!("--port={port}")])
|
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to spawn opencode");
|
.expect("Failed to spawn opencode");
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
let (mut rx, child) = {
|
|
||||||
let sidecar = get_sidecar_path(app);
|
|
||||||
let shell = get_user_shell();
|
|
||||||
app.shell()
|
|
||||||
.command(&shell)
|
|
||||||
.env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true")
|
|
||||||
.env("OPENCODE_CLIENT", "desktop")
|
|
||||||
.env("XDG_STATE_HOME", &state_dir)
|
|
||||||
.args([
|
|
||||||
"-il",
|
|
||||||
"-c",
|
|
||||||
&format!("\"{}\" serve --port={}", sidecar.display(), port),
|
|
||||||
])
|
|
||||||
.spawn()
|
|
||||||
.expect("Failed to spawn opencode")
|
|
||||||
};
|
|
||||||
|
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
while let Some(event) = rx.recv().await {
|
while let Some(event) = rx.recv().await {
|
||||||
match event {
|
match event {
|
||||||
@@ -182,21 +197,37 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild {
|
|||||||
child
|
child
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_server_running(port: u32) -> bool {
|
async fn check_server_health(url: &str, password: Option<&str>) -> bool {
|
||||||
TcpSocket::new_v4()
|
let health_url = format!("{}/global/health", url.trim_end_matches('/'));
|
||||||
.unwrap()
|
let client = reqwest::Client::builder()
|
||||||
.connect(SocketAddr::new(
|
.timeout(Duration::from_secs(3))
|
||||||
"127.0.0.1".parse().expect("Failed to parse IP"),
|
.build();
|
||||||
port as u16,
|
|
||||||
))
|
let Ok(client) = client else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut req = client.get(&health_url);
|
||||||
|
|
||||||
|
if let Some(password) = password {
|
||||||
|
req = req.basic_auth("opencode", Some(password));
|
||||||
|
}
|
||||||
|
|
||||||
|
req.send()
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.map(|r| r.status().is_success())
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
let updater_enabled = option_env!("TAURI_SIGNING_PRIVATE_KEY").is_some();
|
let updater_enabled = option_env!("TAURI_SIGNING_PRIVATE_KEY").is_some();
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let _ = std::process::Command::new("killall")
|
||||||
|
.arg("opencode-cli")
|
||||||
|
.output();
|
||||||
|
|
||||||
let mut builder = tauri::Builder::default()
|
let mut builder = tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||||
// Focus existing window when another instance is launched
|
// Focus existing window when another instance is launched
|
||||||
@@ -219,7 +250,9 @@ pub fn run() {
|
|||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
kill_sidecar,
|
kill_sidecar,
|
||||||
install_cli,
|
install_cli,
|
||||||
ensure_server_started
|
ensure_server_ready,
|
||||||
|
get_default_server_url,
|
||||||
|
set_default_server_url
|
||||||
])
|
])
|
||||||
.setup(move |app| {
|
.setup(move |app| {
|
||||||
let app = app.handle().clone();
|
let app = app.handle().clone();
|
||||||
@@ -227,15 +260,15 @@ pub fn run() {
|
|||||||
// Initialize log state
|
// Initialize log state
|
||||||
app.manage(LogState(Arc::new(Mutex::new(VecDeque::new()))));
|
app.manage(LogState(Arc::new(Mutex::new(VecDeque::new()))));
|
||||||
|
|
||||||
// Get port and create window immediately for faster perceived startup
|
#[cfg(windows)]
|
||||||
let port = get_sidecar_port();
|
app.manage(JobObjectState::new());
|
||||||
|
|
||||||
let primary_monitor = app.primary_monitor().ok().flatten();
|
let primary_monitor = app.primary_monitor().ok().flatten();
|
||||||
let size = primary_monitor
|
let size = primary_monitor
|
||||||
.map(|m| m.size().to_logical(m.scale_factor()))
|
.map(|m| m.size().to_logical(m.scale_factor()))
|
||||||
.unwrap_or(LogicalSize::new(1920, 1080));
|
.unwrap_or(LogicalSize::new(1920, 1080));
|
||||||
|
|
||||||
// Create window immediately with serverReady = false
|
#[allow(unused_mut)]
|
||||||
let mut window_builder =
|
let mut window_builder =
|
||||||
WebviewWindow::builder(&app, "main", WebviewUrl::App("/".into()))
|
WebviewWindow::builder(&app, "main", WebviewUrl::App("/".into()))
|
||||||
.title("OpenCode")
|
.title("OpenCode")
|
||||||
@@ -247,7 +280,6 @@ pub fn run() {
|
|||||||
r#"
|
r#"
|
||||||
window.__OPENCODE__ ??= {{}};
|
window.__OPENCODE__ ??= {{}};
|
||||||
window.__OPENCODE__.updaterEnabled = {updater_enabled};
|
window.__OPENCODE__.updaterEnabled = {updater_enabled};
|
||||||
window.__OPENCODE__.port = {port};
|
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -258,51 +290,44 @@ pub fn run() {
|
|||||||
.hidden_title(true);
|
.hidden_title(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let window = window_builder.build().expect("Failed to create window");
|
window_builder.build().expect("Failed to create window");
|
||||||
|
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
app.manage(ServerState::new(None, rx));
|
app.manage(ServerState::new(None, rx));
|
||||||
|
|
||||||
{
|
{
|
||||||
let app = app.clone();
|
let app = app.clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
let should_spawn_sidecar = !is_server_running(port).await;
|
let mut custom_url = None;
|
||||||
|
|
||||||
let (child, res) = if should_spawn_sidecar {
|
if let Some(url) = get_default_server_url(app.clone()).ok().flatten() {
|
||||||
let child = spawn_sidecar(&app, port);
|
println!("Using desktop-specific custom URL: {url}");
|
||||||
|
custom_url = Some(url);
|
||||||
let timestamp = Instant::now();
|
|
||||||
let res = loop {
|
|
||||||
if timestamp.elapsed() > Duration::from_secs(7) {
|
|
||||||
break Err(format!(
|
|
||||||
"Failed to spawn OpenCode Server. Logs:\n{}",
|
|
||||||
get_logs(app.clone()).await.unwrap()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
||||||
|
|
||||||
if is_server_running(port).await {
|
|
||||||
// give the server a little bit more time to warm up
|
|
||||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
||||||
|
|
||||||
break Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("Server ready after {:?}", timestamp.elapsed());
|
|
||||||
|
|
||||||
(Some(child), res)
|
|
||||||
} else {
|
|
||||||
(None, Ok(()))
|
|
||||||
};
|
|
||||||
|
|
||||||
app.state::<ServerState>().set_child(child);
|
|
||||||
|
|
||||||
if res.is_ok() {
|
|
||||||
let _ = window.eval("window.__OPENCODE__.serverReady = true;");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if custom_url.is_none()
|
||||||
|
&& let Some(cli_config) = cli::get_config(&app).await
|
||||||
|
&& let Some(url) = get_server_url_from_config(&cli_config)
|
||||||
|
{
|
||||||
|
println!("Using custom server URL from config: {url}");
|
||||||
|
custom_url = Some(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = match setup_server_connection(&app, custom_url).await {
|
||||||
|
Ok((child, url)) => {
|
||||||
|
#[cfg(windows)]
|
||||||
|
if let Some(child) = &child {
|
||||||
|
let job_state = app.state::<JobObjectState>();
|
||||||
|
job_state.assign_pid(child.pid());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.state::<ServerState>().set_child(child);
|
||||||
|
|
||||||
|
Ok(url)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
let _ = tx.send(res);
|
let _ = tx.send(res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -334,3 +359,105 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_server_url_from_config(config: &cli::Config) -> Option<String> {
|
||||||
|
let server = config.server.as_ref()?;
|
||||||
|
let port = server.port?;
|
||||||
|
println!("server.port found in OC config: {port}");
|
||||||
|
let hostname = server.hostname.as_ref();
|
||||||
|
|
||||||
|
Some(format!(
|
||||||
|
"http://{}:{}",
|
||||||
|
hostname.map(|v| v.as_str()).unwrap_or("127.0.0.1"),
|
||||||
|
port
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_server_connection(
|
||||||
|
app: &AppHandle,
|
||||||
|
custom_url: Option<String>,
|
||||||
|
) -> Result<(Option<CommandChild>, ServerReadyData), String> {
|
||||||
|
if let Some(url) = custom_url {
|
||||||
|
loop {
|
||||||
|
if check_server_health(&url, None).await {
|
||||||
|
println!("Connected to custom server: {}", url);
|
||||||
|
return Ok((
|
||||||
|
None,
|
||||||
|
ServerReadyData {
|
||||||
|
url: url.clone(),
|
||||||
|
password: None,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
const RETRY: &str = "Retry";
|
||||||
|
|
||||||
|
let res = app.dialog()
|
||||||
|
.message(format!("Could not connect to configured server:\n{}\n\nWould you like to retry or start a local server instead?", url))
|
||||||
|
.title("Connection Failed")
|
||||||
|
.buttons(MessageDialogButtons::OkCancelCustom(RETRY.to_string(), "Start Local".to_string()))
|
||||||
|
.blocking_show_with_result();
|
||||||
|
|
||||||
|
match res {
|
||||||
|
MessageDialogResult::Custom(name) if name == RETRY => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let local_port = get_sidecar_port();
|
||||||
|
let local_url = format!("http://127.0.0.1:{local_port}");
|
||||||
|
|
||||||
|
if !check_server_health(&local_url, None).await {
|
||||||
|
let password = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
|
match spawn_local_server(app, local_port, &password).await {
|
||||||
|
Ok(child) => Ok((
|
||||||
|
Some(child),
|
||||||
|
ServerReadyData {
|
||||||
|
url: local_url,
|
||||||
|
password: Some(password),
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok((
|
||||||
|
None,
|
||||||
|
ServerReadyData {
|
||||||
|
url: local_url,
|
||||||
|
password: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn spawn_local_server(
|
||||||
|
app: &AppHandle,
|
||||||
|
port: u32,
|
||||||
|
password: &str,
|
||||||
|
) -> Result<CommandChild, String> {
|
||||||
|
let child = spawn_sidecar(app, port, password);
|
||||||
|
let url = format!("http://127.0.0.1:{port}");
|
||||||
|
|
||||||
|
let timestamp = Instant::now();
|
||||||
|
loop {
|
||||||
|
if timestamp.elapsed() > Duration::from_secs(30) {
|
||||||
|
break Err(format!(
|
||||||
|
"Failed to spawn OpenCode Server. Logs:\n{}",
|
||||||
|
get_logs(app.clone()).await.unwrap()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||||
|
|
||||||
|
if check_server_health(&url, Some(password)).await {
|
||||||
|
println!("Server ready after {:?}", timestamp.elapsed());
|
||||||
|
break Ok(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"icons/dev/icon.ico"
|
"icons/dev/icon.ico"
|
||||||
],
|
],
|
||||||
"active": true,
|
"active": true,
|
||||||
"targets": ["deb", "rpm", "dmg", "nsis", "app", "appimage"],
|
"targets": ["deb", "rpm", "dmg", "nsis", "app"],
|
||||||
"externalBin": ["sidecars/opencode-cli"],
|
"externalBin": ["sidecars/opencode-cli"],
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"entitlements": "./entitlements.plist"
|
"entitlements": "./entitlements.plist"
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ import { AsyncStorage } from "@solid-primitives/storage"
|
|||||||
import { fetch as tauriFetch } from "@tauri-apps/plugin-http"
|
import { fetch as tauriFetch } from "@tauri-apps/plugin-http"
|
||||||
import { Store } from "@tauri-apps/plugin-store"
|
import { Store } from "@tauri-apps/plugin-store"
|
||||||
import { Logo } from "@opencode-ai/ui/logo"
|
import { Logo } from "@opencode-ai/ui/logo"
|
||||||
import { Suspense, createResource, ParentProps } from "solid-js"
|
import { createSignal, Show, Accessor, JSX, createResource } from "solid-js"
|
||||||
|
|
||||||
import { UPDATER_ENABLED } from "./updater"
|
import { UPDATER_ENABLED } from "./updater"
|
||||||
import { createMenu } from "./menu"
|
import { createMenu } from "./menu"
|
||||||
import pkg from "../package.json"
|
import pkg from "../package.json"
|
||||||
import { Show } from "solid-js"
|
|
||||||
|
|
||||||
const root = document.getElementById("root")
|
const root = document.getElementById("root")
|
||||||
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
||||||
@@ -29,7 +28,7 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
|||||||
|
|
||||||
let update: Update | null = null
|
let update: Update | null = null
|
||||||
|
|
||||||
const platform: Platform = {
|
const createPlatform = (password: Accessor<string | null>): Platform => ({
|
||||||
platform: "desktop",
|
platform: "desktop",
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
|
|
||||||
@@ -256,8 +255,35 @@ const platform: Platform = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
fetch: tauriFetch,
|
fetch: (input, init) => {
|
||||||
}
|
const pw = password()
|
||||||
|
|
||||||
|
const addHeader = (headers: Headers, password: string) => {
|
||||||
|
headers.append("Authorization", `Basic ${btoa(`opencode:${password}`)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input instanceof Request) {
|
||||||
|
if (pw) addHeader(input.headers, pw)
|
||||||
|
return tauriFetch(input)
|
||||||
|
} else {
|
||||||
|
const headers = new Headers(init?.headers)
|
||||||
|
if (pw) addHeader(headers, pw)
|
||||||
|
return tauriFetch(input, {
|
||||||
|
...(init as any),
|
||||||
|
headers: headers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultServerUrl: async () => {
|
||||||
|
const result = await invoke<string | null>("get_default_server_url").catch(() => null)
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
|
||||||
|
setDefaultServerUrl: async (url: string | null) => {
|
||||||
|
await invoke("set_default_server_url", { url })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
createMenu()
|
createMenu()
|
||||||
|
|
||||||
@@ -267,41 +293,47 @@ root?.addEventListener("mousewheel", (e) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
render(() => {
|
render(() => {
|
||||||
|
const [serverPassword, setServerPassword] = createSignal<string | null>(null)
|
||||||
|
const platform = createPlatform(() => serverPassword())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PlatformProvider value={platform}>
|
<PlatformProvider value={platform}>
|
||||||
{ostype() === "macos" && (
|
|
||||||
<div class="mx-px bg-background-base border-b border-border-weak-base h-8" data-tauri-drag-region />
|
|
||||||
)}
|
|
||||||
<AppBaseProviders>
|
<AppBaseProviders>
|
||||||
|
{ostype() === "macos" && (
|
||||||
|
<div class="mx-px bg-background-base border-b border-border-weak-base h-8" data-tauri-drag-region />
|
||||||
|
)}
|
||||||
<ServerGate>
|
<ServerGate>
|
||||||
<AppInterface />
|
{(data) => {
|
||||||
|
setServerPassword(data().password)
|
||||||
|
window.__OPENCODE__ ??= {}
|
||||||
|
window.__OPENCODE__.serverPassword = data().password ?? undefined
|
||||||
|
|
||||||
|
return <AppInterface defaultUrl={data().url} />
|
||||||
|
}}
|
||||||
</ServerGate>
|
</ServerGate>
|
||||||
</AppBaseProviders>
|
</AppBaseProviders>
|
||||||
</PlatformProvider>
|
</PlatformProvider>
|
||||||
)
|
)
|
||||||
}, root!)
|
}, root!)
|
||||||
|
|
||||||
|
type ServerReadyData = { url: string; password: string | null }
|
||||||
|
|
||||||
// Gate component that waits for the server to be ready
|
// Gate component that waits for the server to be ready
|
||||||
function ServerGate(props: ParentProps) {
|
function ServerGate(props: { children: (data: Accessor<ServerReadyData>) => JSX.Element }) {
|
||||||
const [status] = createResource(async () => {
|
const [serverData] = createResource<ServerReadyData>(() => invoke("ensure_server_ready"))
|
||||||
if (window.__OPENCODE__?.serverReady) return
|
|
||||||
return await invoke("ensure_server_started")
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Not using suspense as not all components are compatible with it (undefined refs)
|
// Not using suspense as not all components are compatible with it (undefined refs)
|
||||||
<Show
|
<Show
|
||||||
when={status.state !== "pending"}
|
when={serverData.state !== "pending" && serverData()}
|
||||||
fallback={
|
fallback={
|
||||||
<div class="h-screen w-screen flex flex-col items-center justify-center bg-background-base">
|
<div class="h-screen w-screen flex flex-col items-center justify-center bg-background-base">
|
||||||
<Logo class="w-xl opacity-12 animate-pulse" />
|
<Logo class="w-xl opacity-12 animate-pulse" />
|
||||||
<div class="mt-8 text-14-regular text-text-weak">Starting server...</div>
|
<div class="mt-8 text-14-regular text-text-weak">Initializing...</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{/* Trigger error boundary without rendering the returned value */}
|
{(data) => props.children(data)}
|
||||||
{(status(), null)}
|
|
||||||
{props.children}
|
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,11 +57,10 @@ Example: `mintlify`
|
|||||||
Hex color codes for your global theme
|
Hex color codes for your global theme
|
||||||
<Expandable title="Colors">
|
<Expandable title="Colors">
|
||||||
<ResponseField name="primary" type="string" required>
|
<ResponseField name="primary" type="string" required>
|
||||||
The primary color. Used for most often for highlighted content, section headers, accents, in light mode
|
The primary color. Used most often for highlighted content, section headers, accents, in light mode
|
||||||
</ResponseField>
|
</ResponseField>
|
||||||
<ResponseField name="light" type="string">
|
<ResponseField name="light" type="string">
|
||||||
The primary color for dark mode. Used for most often for highlighted content, section headers, accents, in dark
|
The primary color for dark mode. Used most often for highlighted content, section headers, accents, in dark mode
|
||||||
mode
|
|
||||||
</ResponseField>
|
</ResponseField>
|
||||||
<ResponseField name="dark" type="string">
|
<ResponseField name="dark" type="string">
|
||||||
The primary color for important buttons
|
The primary color for important buttons
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:cloudflare": "OPENCODE_DEPLOYMENT_TARGET=cloudflare vite build",
|
"build:cloudflare": "OPENCODE_DEPLOYMENT_TARGET=cloudflare vite build",
|
||||||
"start": "vite start"
|
"start": "vite start",
|
||||||
|
"shell-prod": "sst shell --target Teams --stage production"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Storage } from "../src/core/storage"
|
||||||
|
|
||||||
|
// read share id from args
|
||||||
|
const args = process.argv.slice(2)
|
||||||
|
if (args.length !== 1) {
|
||||||
|
console.error("Usage: bun script/scrap.ts <shareID>")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const shareID = args[0]
|
||||||
|
|
||||||
|
await Storage.remove(["share", shareID])
|
||||||
|
const list = await Storage.list({ prefix: ["share_data", shareID] })
|
||||||
|
for (const item of list) {
|
||||||
|
await Storage.remove(item)
|
||||||
|
}
|
||||||
Vendored
+8
@@ -78,6 +78,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
"STRIPE_SECRET_KEY": {
|
"STRIPE_SECRET_KEY": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
@@ -130,6 +134,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cloudflare
|
// cloudflare
|
||||||
|
|||||||
@@ -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.1.8"
|
version = "1.1.19"
|
||||||
schema_version = 1
|
schema_version = 1
|
||||||
authors = ["Anomaly"]
|
authors = ["Anomaly"]
|
||||||
repository = "https://github.com/anomalyco/opencode"
|
repository = "https://github.com/anomalyco/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/anomalyco/opencode/releases/download/v1.1.8/opencode-darwin-arm64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.19/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/anomalyco/opencode/releases/download/v1.1.8/opencode-darwin-x64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.19/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/anomalyco/opencode/releases/download/v1.1.8/opencode-linux-arm64.tar.gz"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.19/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/anomalyco/opencode/releases/download/v1.1.8/opencode-linux-x64.tar.gz"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.19/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/anomalyco/opencode/releases/download/v1.1.8/opencode-windows-x64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.19/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.1.8",
|
"version": "1.1.19",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
Vendored
+8
@@ -78,6 +78,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
"STRIPE_SECRET_KEY": {
|
"STRIPE_SECRET_KEY": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
@@ -130,6 +134,10 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cloudflare
|
// cloudflare
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.1.8",
|
"version": "1.1.19",
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -50,26 +50,27 @@
|
|||||||
"@actions/core": "1.11.1",
|
"@actions/core": "1.11.1",
|
||||||
"@actions/github": "6.0.1",
|
"@actions/github": "6.0.1",
|
||||||
"@agentclientprotocol/sdk": "0.5.1",
|
"@agentclientprotocol/sdk": "0.5.1",
|
||||||
"@ai-sdk/amazon-bedrock": "3.0.57",
|
"@ai-sdk/amazon-bedrock": "3.0.73",
|
||||||
"@ai-sdk/anthropic": "2.0.56",
|
"@ai-sdk/anthropic": "2.0.57",
|
||||||
"@ai-sdk/azure": "2.0.82",
|
"@ai-sdk/azure": "2.0.91",
|
||||||
"@ai-sdk/cerebras": "1.0.33",
|
"@ai-sdk/cerebras": "1.0.34",
|
||||||
"@ai-sdk/cohere": "2.0.21",
|
"@ai-sdk/cohere": "2.0.22",
|
||||||
"@ai-sdk/deepinfra": "1.0.30",
|
"@ai-sdk/deepinfra": "1.0.31",
|
||||||
"@ai-sdk/gateway": "2.0.23",
|
"@ai-sdk/gateway": "2.0.25",
|
||||||
"@ai-sdk/google": "2.0.49",
|
"@ai-sdk/google": "2.0.52",
|
||||||
"@ai-sdk/google-vertex": "3.0.81",
|
"@ai-sdk/google-vertex": "3.0.97",
|
||||||
"@ai-sdk/groq": "2.0.33",
|
"@ai-sdk/groq": "2.0.34",
|
||||||
"@ai-sdk/mistral": "2.0.26",
|
"@ai-sdk/mistral": "2.0.27",
|
||||||
"@ai-sdk/openai": "2.0.71",
|
"@ai-sdk/openai": "2.0.89",
|
||||||
"@ai-sdk/openai-compatible": "1.0.29",
|
"@ai-sdk/openai-compatible": "1.0.30",
|
||||||
"@ai-sdk/perplexity": "2.0.22",
|
"@ai-sdk/perplexity": "2.0.23",
|
||||||
"@ai-sdk/provider": "2.0.0",
|
"@ai-sdk/provider": "2.0.1",
|
||||||
"@ai-sdk/provider-utils": "3.0.19",
|
"@ai-sdk/provider-utils": "3.0.20",
|
||||||
"@ai-sdk/togetherai": "1.0.30",
|
"@ai-sdk/togetherai": "1.0.31",
|
||||||
"@ai-sdk/vercel": "1.0.31",
|
"@ai-sdk/vercel": "1.0.31",
|
||||||
"@ai-sdk/xai": "2.0.42",
|
"@ai-sdk/xai": "2.0.51",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
|
"@gitlab/gitlab-ai-provider": "3.1.0",
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
"@modelcontextprotocol/sdk": "1.25.2",
|
"@modelcontextprotocol/sdk": "1.25.2",
|
||||||
|
|||||||
@@ -106,8 +106,11 @@ async function main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { binaryPath, binaryName } = findBinary()
|
// On non-Windows platforms, just verify the binary package exists
|
||||||
symlinkBinary(binaryPath, binaryName)
|
// Don't replace the wrapper script - it handles binary execution
|
||||||
|
const { binaryPath } = findBinary()
|
||||||
|
console.log(`Platform binary verified at: ${binaryPath}`)
|
||||||
|
console.log("Wrapper script will handle binary execution")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to setup opencode binary:", error.message)
|
console.error("Failed to setup opencode binary:", error.message)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { Todo } from "@/session/todo"
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { LoadAPIKeyError } from "ai"
|
import { LoadAPIKeyError } from "ai"
|
||||||
import type { OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
import type { OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
||||||
|
import { applyPatch } from "diff"
|
||||||
|
|
||||||
export namespace ACP {
|
export namespace ACP {
|
||||||
const log = Log.create({ service: "acp-agent" })
|
const log = Log.create({ service: "acp-agent" })
|
||||||
@@ -105,6 +106,22 @@ export namespace ACP {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (res.outcome.optionId !== "reject" && permission.permission == "edit") {
|
||||||
|
const metadata = permission.metadata || {}
|
||||||
|
const filepath = typeof metadata["filepath"] === "string" ? metadata["filepath"] : ""
|
||||||
|
const diff = typeof metadata["diff"] === "string" ? metadata["diff"] : ""
|
||||||
|
|
||||||
|
const content = await Bun.file(filepath).text()
|
||||||
|
const newContent = getNewContent(content, diff)
|
||||||
|
|
||||||
|
if (newContent) {
|
||||||
|
this.connection.writeTextFile({
|
||||||
|
sessionId: sessionId,
|
||||||
|
path: filepath,
|
||||||
|
content: newContent,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
await this.config.sdk.permission.reply({
|
await this.config.sdk.permission.reply({
|
||||||
requestID: permission.id,
|
requestID: permission.id,
|
||||||
reply: res.outcome.optionId as "once" | "always" | "reject",
|
reply: res.outcome.optionId as "once" | "always" | "reject",
|
||||||
@@ -1095,4 +1112,13 @@ export namespace ACP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNewContent(fileOriginal: string, unifiedDiff: string): string | undefined {
|
||||||
|
const result = applyPatch(fileOriginal, unifiedDiff)
|
||||||
|
if (result === false) {
|
||||||
|
log.error("Failed to apply unified diff (context mismatch)")
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,16 @@ export namespace Agent {
|
|||||||
external_directory: {
|
external_directory: {
|
||||||
"*": "ask",
|
"*": "ask",
|
||||||
[Truncate.DIR]: "allow",
|
[Truncate.DIR]: "allow",
|
||||||
|
[Truncate.GLOB]: "allow",
|
||||||
},
|
},
|
||||||
question: "deny",
|
question: "deny",
|
||||||
|
plan_enter: "deny",
|
||||||
|
plan_exit: "deny",
|
||||||
// mirrors github.com/github/gitignore Node.gitignore pattern for .env files
|
// mirrors github.com/github/gitignore Node.gitignore pattern for .env files
|
||||||
read: {
|
read: {
|
||||||
"*": "allow",
|
"*": "allow",
|
||||||
"*.env": "deny",
|
"*.env": "ask",
|
||||||
"*.env.*": "deny",
|
"*.env.*": "ask",
|
||||||
"*.env.example": "allow",
|
"*.env.example": "allow",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -70,6 +73,7 @@ export namespace Agent {
|
|||||||
defaults,
|
defaults,
|
||||||
PermissionNext.fromConfig({
|
PermissionNext.fromConfig({
|
||||||
question: "allow",
|
question: "allow",
|
||||||
|
plan_enter: "allow",
|
||||||
}),
|
}),
|
||||||
user,
|
user,
|
||||||
),
|
),
|
||||||
@@ -83,9 +87,10 @@ export namespace Agent {
|
|||||||
defaults,
|
defaults,
|
||||||
PermissionNext.fromConfig({
|
PermissionNext.fromConfig({
|
||||||
question: "allow",
|
question: "allow",
|
||||||
|
plan_exit: "allow",
|
||||||
edit: {
|
edit: {
|
||||||
"*": "deny",
|
"*": "deny",
|
||||||
".opencode/plan/*.md": "allow",
|
".opencode/plans/*.md": "allow",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
user,
|
user,
|
||||||
@@ -124,6 +129,7 @@ export namespace Agent {
|
|||||||
read: "allow",
|
read: "allow",
|
||||||
external_directory: {
|
external_directory: {
|
||||||
[Truncate.DIR]: "allow",
|
[Truncate.DIR]: "allow",
|
||||||
|
[Truncate.GLOB]: "allow",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
user,
|
user,
|
||||||
@@ -213,14 +219,16 @@ export namespace Agent {
|
|||||||
// Ensure Truncate.DIR is allowed unless explicitly configured
|
// Ensure Truncate.DIR is allowed unless explicitly configured
|
||||||
for (const name in result) {
|
for (const name in result) {
|
||||||
const agent = result[name]
|
const agent = result[name]
|
||||||
const explicit = agent.permission.some(
|
const explicit = agent.permission.some((r) => {
|
||||||
(r) => r.permission === "external_directory" && r.pattern === Truncate.DIR && r.action === "deny",
|
if (r.permission !== "external_directory") return false
|
||||||
)
|
if (r.action !== "deny") return false
|
||||||
|
return r.pattern === Truncate.DIR || r.pattern === Truncate.GLOB
|
||||||
|
})
|
||||||
if (explicit) continue
|
if (explicit) continue
|
||||||
|
|
||||||
result[name].permission = PermissionNext.merge(
|
result[name].permission = PermissionNext.merge(
|
||||||
result[name].permission,
|
result[name].permission,
|
||||||
PermissionNext.fromConfig({ external_directory: { [Truncate.DIR]: "allow" } }),
|
PermissionNext.fromConfig({ external_directory: { [Truncate.DIR]: "allow", [Truncate.GLOB]: "allow" } }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export namespace Auth {
|
|||||||
refresh: z.string(),
|
refresh: z.string(),
|
||||||
access: z.string(),
|
access: z.string(),
|
||||||
expires: z.number(),
|
expires: z.number(),
|
||||||
|
accountId: z.string().optional(),
|
||||||
enterpriseUrl: z.string().optional(),
|
enterpriseUrl: z.string().optional(),
|
||||||
})
|
})
|
||||||
.meta({ ref: "OAuth" })
|
.meta({ ref: "OAuth" })
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ export const AuthLoginCommand = cmd({
|
|||||||
hint: {
|
hint: {
|
||||||
opencode: "recommended",
|
opencode: "recommended",
|
||||||
anthropic: "Claude Max or API key",
|
anthropic: "Claude Max or API key",
|
||||||
|
openai: "ChatGPT Plus/Pro or API key",
|
||||||
}[x.id],
|
}[x.id],
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { basename } from "path"
|
import { basename } from "path"
|
||||||
import { Agent } from "../../../agent/agent"
|
import { Agent } from "../../../agent/agent"
|
||||||
|
import { Provider } from "../../../provider/provider"
|
||||||
|
import { Session } from "../../../session"
|
||||||
|
import type { MessageV2 } from "../../../session/message-v2"
|
||||||
|
import { Identifier } from "../../../id/id"
|
||||||
|
import { ToolRegistry } from "../../../tool/registry"
|
||||||
|
import { Instance } from "../../../project/instance"
|
||||||
|
import { PermissionNext } from "../../../permission/next"
|
||||||
|
import { iife } from "../../../util/iife"
|
||||||
import { bootstrap } from "../../bootstrap"
|
import { bootstrap } from "../../bootstrap"
|
||||||
import { cmd } from "../cmd"
|
import { cmd } from "../cmd"
|
||||||
|
|
||||||
@@ -8,11 +16,20 @@ export const AgentCommand = cmd({
|
|||||||
command: "agent <name>",
|
command: "agent <name>",
|
||||||
describe: "show agent configuration details",
|
describe: "show agent configuration details",
|
||||||
builder: (yargs) =>
|
builder: (yargs) =>
|
||||||
yargs.positional("name", {
|
yargs
|
||||||
type: "string",
|
.positional("name", {
|
||||||
demandOption: true,
|
type: "string",
|
||||||
description: "Agent name",
|
demandOption: true,
|
||||||
}),
|
description: "Agent name",
|
||||||
|
})
|
||||||
|
.option("tool", {
|
||||||
|
type: "string",
|
||||||
|
description: "Tool id to execute",
|
||||||
|
})
|
||||||
|
.option("params", {
|
||||||
|
type: "string",
|
||||||
|
description: "Tool params as JSON or a JS object literal",
|
||||||
|
}),
|
||||||
async handler(args) {
|
async handler(args) {
|
||||||
await bootstrap(process.cwd(), async () => {
|
await bootstrap(process.cwd(), async () => {
|
||||||
const agentName = args.name as string
|
const agentName = args.name as string
|
||||||
@@ -23,7 +40,127 @@ export const AgentCommand = cmd({
|
|||||||
)
|
)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
process.stdout.write(JSON.stringify(agent, null, 2) + EOL)
|
const availableTools = await getAvailableTools(agent)
|
||||||
|
const resolvedTools = await resolveTools(agent, availableTools)
|
||||||
|
const toolID = args.tool as string | undefined
|
||||||
|
if (toolID) {
|
||||||
|
const tool = availableTools.find((item) => item.id === toolID)
|
||||||
|
if (!tool) {
|
||||||
|
process.stderr.write(`Tool ${toolID} not found for agent ${agentName}` + EOL)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
if (resolvedTools[toolID] === false) {
|
||||||
|
process.stderr.write(`Tool ${toolID} is disabled for agent ${agentName}` + EOL)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const params = parseToolParams(args.params as string | undefined)
|
||||||
|
const ctx = await createToolContext(agent)
|
||||||
|
const result = await tool.execute(params, ctx)
|
||||||
|
process.stdout.write(JSON.stringify({ tool: toolID, input: params, result }, null, 2) + EOL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = {
|
||||||
|
...agent,
|
||||||
|
tools: resolvedTools,
|
||||||
|
}
|
||||||
|
process.stdout.write(JSON.stringify(output, null, 2) + EOL)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function getAvailableTools(agent: Agent.Info) {
|
||||||
|
const providerID = agent.model?.providerID ?? (await Provider.defaultModel()).providerID
|
||||||
|
return ToolRegistry.tools(providerID, agent)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveTools(agent: Agent.Info, availableTools: Awaited<ReturnType<typeof getAvailableTools>>) {
|
||||||
|
const disabled = PermissionNext.disabled(
|
||||||
|
availableTools.map((tool) => tool.id),
|
||||||
|
agent.permission,
|
||||||
|
)
|
||||||
|
const resolved: Record<string, boolean> = {}
|
||||||
|
for (const tool of availableTools) {
|
||||||
|
resolved[tool.id] = !disabled.has(tool.id)
|
||||||
|
}
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseToolParams(input?: string) {
|
||||||
|
if (!input) return {}
|
||||||
|
const trimmed = input.trim()
|
||||||
|
if (trimmed.length === 0) return {}
|
||||||
|
|
||||||
|
const parsed = iife(() => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(trimmed)
|
||||||
|
} catch (jsonError) {
|
||||||
|
try {
|
||||||
|
return new Function(`return (${trimmed})`)()
|
||||||
|
} catch (evalError) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse --params. Use JSON or a JS object literal. JSON error: ${jsonError}. Eval error: ${evalError}.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||||
|
throw new Error("Tool params must be an object.")
|
||||||
|
}
|
||||||
|
return parsed as Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createToolContext(agent: Agent.Info) {
|
||||||
|
const session = await Session.create({ title: `Debug tool run (${agent.name})` })
|
||||||
|
const messageID = Identifier.ascending("message")
|
||||||
|
const model = agent.model ?? (await Provider.defaultModel())
|
||||||
|
const now = Date.now()
|
||||||
|
const message: MessageV2.Assistant = {
|
||||||
|
id: messageID,
|
||||||
|
sessionID: session.id,
|
||||||
|
role: "assistant",
|
||||||
|
time: {
|
||||||
|
created: now,
|
||||||
|
},
|
||||||
|
parentID: messageID,
|
||||||
|
modelID: model.modelID,
|
||||||
|
providerID: model.providerID,
|
||||||
|
mode: "debug",
|
||||||
|
agent: agent.name,
|
||||||
|
path: {
|
||||||
|
cwd: Instance.directory,
|
||||||
|
root: Instance.worktree,
|
||||||
|
},
|
||||||
|
cost: 0,
|
||||||
|
tokens: {
|
||||||
|
input: 0,
|
||||||
|
output: 0,
|
||||||
|
reasoning: 0,
|
||||||
|
cache: {
|
||||||
|
read: 0,
|
||||||
|
write: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await Session.updateMessage(message)
|
||||||
|
|
||||||
|
const ruleset = PermissionNext.merge(agent.permission, session.permission ?? [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
sessionID: session.id,
|
||||||
|
messageID,
|
||||||
|
callID: Identifier.ascending("part"),
|
||||||
|
agent: agent.name,
|
||||||
|
abort: new AbortController().signal,
|
||||||
|
metadata: () => {},
|
||||||
|
async ask(req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) {
|
||||||
|
for (const pattern of req.patterns) {
|
||||||
|
const rule = PermissionNext.evaluate(req.permission, pattern, ruleset)
|
||||||
|
if (rule.action === "deny") {
|
||||||
|
throw new PermissionNext.DeniedError(ruleset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const FileTreeCommand = cmd({
|
|||||||
}),
|
}),
|
||||||
async handler(args) {
|
async handler(args) {
|
||||||
const files = await Ripgrep.tree({ cwd: args.dir, limit: 200 })
|
const files = await Ripgrep.tree({ cwd: args.dir, limit: 200 })
|
||||||
console.log(files)
|
console.log(JSON.stringify(files, null, 2))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -394,6 +394,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Run opencode
|
- name: Run opencode
|
||||||
uses: anomalyco/opencode/github@latest${envStr}
|
uses: anomalyco/opencode/github@latest${envStr}
|
||||||
@@ -624,7 +626,7 @@ export const GithubRunCommand = cmd({
|
|||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
console.error(e)
|
console.error(e instanceof Error ? e.message : String(e))
|
||||||
let msg = e
|
let msg = e
|
||||||
if (e instanceof $.ShellError) {
|
if (e instanceof $.ShellError) {
|
||||||
msg = e.stderr.toString()
|
msg = e.stderr.toString()
|
||||||
@@ -915,7 +917,7 @@ export const GithubRunCommand = cmd({
|
|||||||
|
|
||||||
// result should always be assistant just satisfying type checker
|
// result should always be assistant just satisfying type checker
|
||||||
if (result.info.role === "assistant" && result.info.error) {
|
if (result.info.role === "assistant" && result.info.error) {
|
||||||
console.error(result.info)
|
console.error("Agent error:", result.info.error)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${result.info.error.name}: ${"message" in result.info.error ? result.info.error.message : ""}`,
|
`${result.info.error.name}: ${"message" in result.info.error ? result.info.error.message : ""}`,
|
||||||
)
|
)
|
||||||
@@ -944,7 +946,7 @@ export const GithubRunCommand = cmd({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (summary.info.role === "assistant" && summary.info.error) {
|
if (summary.info.role === "assistant" && summary.info.error) {
|
||||||
console.error(summary.info)
|
console.error("Summary agent error:", summary.info.error)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${summary.info.error.name}: ${"message" in summary.info.error ? summary.info.error.message : ""}`,
|
`${summary.info.error.name}: ${"message" in summary.info.error ? summary.info.error.message : ""}`,
|
||||||
)
|
)
|
||||||
@@ -962,7 +964,7 @@ export const GithubRunCommand = cmd({
|
|||||||
try {
|
try {
|
||||||
return await core.getIDToken("opencode-github-action")
|
return await core.getIDToken("opencode-github-action")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to get OIDC token:", error)
|
console.error("Failed to get OIDC token:", error instanceof Error ? error.message : error)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Could not fetch an OIDC token. Make sure to add `id-token: write` to your workflow permissions.",
|
"Could not fetch an OIDC token. Make sure to add `id-token: write` to your workflow permissions.",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
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 { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
||||||
import * as prompts from "@clack/prompts"
|
import * as prompts from "@clack/prompts"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
@@ -13,6 +12,7 @@ import { Instance } from "../../project/instance"
|
|||||||
import { Installation } from "../../installation"
|
import { Installation } from "../../installation"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Global } from "../../global"
|
import { Global } from "../../global"
|
||||||
|
import { modify, applyEdits } from "jsonc-parser"
|
||||||
|
|
||||||
function getAuthStatusIcon(status: MCP.AuthStatus): string {
|
function getAuthStatusIcon(status: MCP.AuthStatus): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -21,7 +21,7 @@ function getAuthStatusIcon(status: MCP.AuthStatus): string {
|
|||||||
case "expired":
|
case "expired":
|
||||||
return "⚠"
|
return "⚠"
|
||||||
case "not_authenticated":
|
case "not_authenticated":
|
||||||
return "○"
|
return "✗"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,133 +366,204 @@ export const McpLogoutCommand = cmd({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function resolveConfigPath(baseDir: string, global = false) {
|
||||||
|
// Check for existing config files (prefer .jsonc over .json, check .opencode/ subdirectory too)
|
||||||
|
const candidates = [path.join(baseDir, "opencode.json"), path.join(baseDir, "opencode.jsonc")]
|
||||||
|
|
||||||
|
if (!global) {
|
||||||
|
candidates.push(path.join(baseDir, ".opencode", "opencode.json"), path.join(baseDir, ".opencode", "opencode.jsonc"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (await Bun.file(candidate).exists()) {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to opencode.json if none exist
|
||||||
|
return candidates[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addMcpToConfig(name: string, mcpConfig: Config.Mcp, configPath: string) {
|
||||||
|
const file = Bun.file(configPath)
|
||||||
|
|
||||||
|
let text = "{}"
|
||||||
|
if (await file.exists()) {
|
||||||
|
text = await file.text()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use jsonc-parser to modify while preserving comments
|
||||||
|
const edits = modify(text, ["mcp", name], mcpConfig, {
|
||||||
|
formattingOptions: { tabSize: 2, insertSpaces: true },
|
||||||
|
})
|
||||||
|
const result = applyEdits(text, edits)
|
||||||
|
|
||||||
|
await Bun.write(configPath, result)
|
||||||
|
|
||||||
|
return configPath
|
||||||
|
}
|
||||||
|
|
||||||
export const McpAddCommand = cmd({
|
export const McpAddCommand = cmd({
|
||||||
command: "add",
|
command: "add",
|
||||||
describe: "add an MCP server",
|
describe: "add an MCP server",
|
||||||
async handler() {
|
async handler() {
|
||||||
UI.empty()
|
await Instance.provide({
|
||||||
prompts.intro("Add MCP server")
|
directory: process.cwd(),
|
||||||
|
async fn() {
|
||||||
|
UI.empty()
|
||||||
|
prompts.intro("Add MCP server")
|
||||||
|
|
||||||
const name = await prompts.text({
|
const project = Instance.project
|
||||||
message: "Enter MCP server name",
|
|
||||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
|
||||||
})
|
|
||||||
if (prompts.isCancel(name)) throw new UI.CancelledError()
|
|
||||||
|
|
||||||
const type = await prompts.select({
|
// Resolve config paths eagerly for hints
|
||||||
message: "Select MCP server type",
|
const [projectConfigPath, globalConfigPath] = await Promise.all([
|
||||||
options: [
|
resolveConfigPath(Instance.worktree),
|
||||||
{
|
resolveConfigPath(Global.Path.config, true),
|
||||||
label: "Local",
|
])
|
||||||
value: "local",
|
|
||||||
hint: "Run a local command",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Remote",
|
|
||||||
value: "remote",
|
|
||||||
hint: "Connect to a remote URL",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
if (prompts.isCancel(type)) throw new UI.CancelledError()
|
|
||||||
|
|
||||||
if (type === "local") {
|
// Determine scope
|
||||||
const command = await prompts.text({
|
let configPath = globalConfigPath
|
||||||
message: "Enter command to run",
|
if (project.vcs === "git") {
|
||||||
placeholder: "e.g., opencode x @modelcontextprotocol/server-filesystem",
|
const scopeResult = await prompts.select({
|
||||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
message: "Location",
|
||||||
})
|
options: [
|
||||||
if (prompts.isCancel(command)) throw new UI.CancelledError()
|
{
|
||||||
|
label: "Current project",
|
||||||
|
value: projectConfigPath,
|
||||||
|
hint: projectConfigPath,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Global",
|
||||||
|
value: globalConfigPath,
|
||||||
|
hint: globalConfigPath,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(scopeResult)) throw new UI.CancelledError()
|
||||||
|
configPath = scopeResult
|
||||||
|
}
|
||||||
|
|
||||||
prompts.log.info(`Local MCP server "${name}" configured with command: ${command}`)
|
const name = await prompts.text({
|
||||||
prompts.outro("MCP server added successfully")
|
message: "Enter MCP server name",
|
||||||
return
|
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "remote") {
|
|
||||||
const url = await prompts.text({
|
|
||||||
message: "Enter MCP server URL",
|
|
||||||
placeholder: "e.g., https://example.com/mcp",
|
|
||||||
validate: (x) => {
|
|
||||||
if (!x) return "Required"
|
|
||||||
if (x.length === 0) return "Required"
|
|
||||||
const isValid = URL.canParse(x)
|
|
||||||
return isValid ? undefined : "Invalid URL"
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (prompts.isCancel(url)) throw new UI.CancelledError()
|
|
||||||
|
|
||||||
const useOAuth = await prompts.confirm({
|
|
||||||
message: "Does this server require OAuth authentication?",
|
|
||||||
initialValue: false,
|
|
||||||
})
|
|
||||||
if (prompts.isCancel(useOAuth)) throw new UI.CancelledError()
|
|
||||||
|
|
||||||
if (useOAuth) {
|
|
||||||
const hasClientId = await prompts.confirm({
|
|
||||||
message: "Do you have a pre-registered client ID?",
|
|
||||||
initialValue: false,
|
|
||||||
})
|
})
|
||||||
if (prompts.isCancel(hasClientId)) throw new UI.CancelledError()
|
if (prompts.isCancel(name)) throw new UI.CancelledError()
|
||||||
|
|
||||||
if (hasClientId) {
|
const type = await prompts.select({
|
||||||
const clientId = await prompts.text({
|
message: "Select MCP server type",
|
||||||
message: "Enter client ID",
|
options: [
|
||||||
|
{
|
||||||
|
label: "Local",
|
||||||
|
value: "local",
|
||||||
|
hint: "Run a local command",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Remote",
|
||||||
|
value: "remote",
|
||||||
|
hint: "Connect to a remote URL",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(type)) throw new UI.CancelledError()
|
||||||
|
|
||||||
|
if (type === "local") {
|
||||||
|
const command = await prompts.text({
|
||||||
|
message: "Enter command to run",
|
||||||
|
placeholder: "e.g., opencode x @modelcontextprotocol/server-filesystem",
|
||||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||||
})
|
})
|
||||||
if (prompts.isCancel(clientId)) throw new UI.CancelledError()
|
if (prompts.isCancel(command)) throw new UI.CancelledError()
|
||||||
|
|
||||||
const hasSecret = await prompts.confirm({
|
const mcpConfig: Config.Mcp = {
|
||||||
message: "Do you have a client secret?",
|
type: "local",
|
||||||
initialValue: false,
|
command: command.split(" "),
|
||||||
})
|
|
||||||
if (prompts.isCancel(hasSecret)) throw new UI.CancelledError()
|
|
||||||
|
|
||||||
let clientSecret: string | undefined
|
|
||||||
if (hasSecret) {
|
|
||||||
const secret = await prompts.password({
|
|
||||||
message: "Enter client secret",
|
|
||||||
})
|
|
||||||
if (prompts.isCancel(secret)) throw new UI.CancelledError()
|
|
||||||
clientSecret = secret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prompts.log.info(`Remote MCP server "${name}" configured with OAuth (client ID: ${clientId})`)
|
await addMcpToConfig(name, mcpConfig, configPath)
|
||||||
prompts.log.info("Add this to your opencode.json:")
|
prompts.log.success(`MCP server "${name}" added to ${configPath}`)
|
||||||
prompts.log.info(`
|
prompts.outro("MCP server added successfully")
|
||||||
"mcp": {
|
return
|
||||||
"${name}": {
|
|
||||||
"type": "remote",
|
|
||||||
"url": "${url}",
|
|
||||||
"oauth": {
|
|
||||||
"clientId": "${clientId}"${clientSecret ? `,\n "clientSecret": "${clientSecret}"` : ""}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
} else {
|
|
||||||
prompts.log.info(`Remote MCP server "${name}" configured with OAuth (dynamic registration)`)
|
|
||||||
prompts.log.info("Add this to your opencode.json:")
|
|
||||||
prompts.log.info(`
|
|
||||||
"mcp": {
|
|
||||||
"${name}": {
|
|
||||||
"type": "remote",
|
|
||||||
"url": "${url}",
|
|
||||||
"oauth": {}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const client = new Client({
|
|
||||||
name: "opencode",
|
|
||||||
version: "1.0.0",
|
|
||||||
})
|
|
||||||
const transport = new StreamableHTTPClientTransport(new URL(url))
|
|
||||||
await client.connect(transport)
|
|
||||||
prompts.log.info(`Remote MCP server "${name}" configured with URL: ${url}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prompts.outro("MCP server added successfully")
|
if (type === "remote") {
|
||||||
|
const url = await prompts.text({
|
||||||
|
message: "Enter MCP server URL",
|
||||||
|
placeholder: "e.g., https://example.com/mcp",
|
||||||
|
validate: (x) => {
|
||||||
|
if (!x) return "Required"
|
||||||
|
if (x.length === 0) return "Required"
|
||||||
|
const isValid = URL.canParse(x)
|
||||||
|
return isValid ? undefined : "Invalid URL"
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(url)) throw new UI.CancelledError()
|
||||||
|
|
||||||
|
const useOAuth = await prompts.confirm({
|
||||||
|
message: "Does this server require OAuth authentication?",
|
||||||
|
initialValue: false,
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(useOAuth)) throw new UI.CancelledError()
|
||||||
|
|
||||||
|
let mcpConfig: Config.Mcp
|
||||||
|
|
||||||
|
if (useOAuth) {
|
||||||
|
const hasClientId = await prompts.confirm({
|
||||||
|
message: "Do you have a pre-registered client ID?",
|
||||||
|
initialValue: false,
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(hasClientId)) throw new UI.CancelledError()
|
||||||
|
|
||||||
|
if (hasClientId) {
|
||||||
|
const clientId = await prompts.text({
|
||||||
|
message: "Enter client ID",
|
||||||
|
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(clientId)) throw new UI.CancelledError()
|
||||||
|
|
||||||
|
const hasSecret = await prompts.confirm({
|
||||||
|
message: "Do you have a client secret?",
|
||||||
|
initialValue: false,
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(hasSecret)) throw new UI.CancelledError()
|
||||||
|
|
||||||
|
let clientSecret: string | undefined
|
||||||
|
if (hasSecret) {
|
||||||
|
const secret = await prompts.password({
|
||||||
|
message: "Enter client secret",
|
||||||
|
})
|
||||||
|
if (prompts.isCancel(secret)) throw new UI.CancelledError()
|
||||||
|
clientSecret = secret
|
||||||
|
}
|
||||||
|
|
||||||
|
mcpConfig = {
|
||||||
|
type: "remote",
|
||||||
|
url,
|
||||||
|
oauth: {
|
||||||
|
clientId,
|
||||||
|
...(clientSecret && { clientSecret }),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mcpConfig = {
|
||||||
|
type: "remote",
|
||||||
|
url,
|
||||||
|
oauth: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mcpConfig = {
|
||||||
|
type: "remote",
|
||||||
|
url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await addMcpToConfig(name, mcpConfig, configPath)
|
||||||
|
prompts.log.success(`MCP server "${name}" added to ${configPath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
prompts.outro("MCP server added successfully")
|
||||||
|
},
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -339,13 +339,15 @@ export const RunCommand = cmd({
|
|||||||
}
|
}
|
||||||
|
|
||||||
await bootstrap(process.cwd(), async () => {
|
await bootstrap(process.cwd(), async () => {
|
||||||
const server = Server.listen({ port: args.port ?? 0, hostname: "127.0.0.1" })
|
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
const sdk = createOpencodeClient({ baseUrl: `http://${server.hostname}:${server.port}` })
|
const request = new Request(input, init)
|
||||||
|
return Server.App().fetch(request)
|
||||||
|
}) as typeof globalThis.fetch
|
||||||
|
const sdk = createOpencodeClient({ baseUrl: "http://opencode.internal", fetch: fetchFn })
|
||||||
|
|
||||||
if (args.command) {
|
if (args.command) {
|
||||||
const exists = await Command.get(args.command)
|
const exists = await Command.get(args.command)
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
server.stop()
|
|
||||||
UI.error(`Command "${args.command}" not found`)
|
UI.error(`Command "${args.command}" not found`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
@@ -370,7 +372,6 @@ export const RunCommand = cmd({
|
|||||||
})()
|
})()
|
||||||
|
|
||||||
if (!sessionID) {
|
if (!sessionID) {
|
||||||
server.stop()
|
|
||||||
UI.error("Session not found")
|
UI.error("Session not found")
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
@@ -389,7 +390,6 @@ export const RunCommand = cmd({
|
|||||||
}
|
}
|
||||||
|
|
||||||
await execute(sdk, sessionID)
|
await execute(sdk, sessionID)
|
||||||
server.stop()
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { Server } from "../../server/server"
|
import { Server } from "../../server/server"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
||||||
|
import { Flag } from "../../flag/flag"
|
||||||
|
|
||||||
export const ServeCommand = cmd({
|
export const ServeCommand = cmd({
|
||||||
command: "serve",
|
command: "serve",
|
||||||
builder: (yargs) => withNetworkOptions(yargs),
|
builder: (yargs) => withNetworkOptions(yargs),
|
||||||
describe: "starts a headless opencode server",
|
describe: "starts a headless opencode server",
|
||||||
handler: async (args) => {
|
handler: async (args) => {
|
||||||
|
if (!Flag.OPENCODE_SERVER_PASSWORD) {
|
||||||
|
console.log("Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
|
||||||
|
}
|
||||||
const opts = await resolveNetworkOptions(args)
|
const opts = await resolveNetworkOptions(args)
|
||||||
const server = Server.listen(opts)
|
const server = Server.listen(opts)
|
||||||
console.log(`opencode server listening on http://${server.hostname}:${server.port}`)
|
console.log(`opencode server listening on http://${server.hostname}:${server.port}`)
|
||||||
|
|||||||
@@ -97,7 +97,16 @@ async function getTerminalBackgroundColor(): Promise<"dark" | "light"> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tui(input: { url: string; args: Args; directory?: string; onExit?: () => Promise<void> }) {
|
import type { EventSource } from "./context/sdk"
|
||||||
|
|
||||||
|
export function tui(input: {
|
||||||
|
url: string
|
||||||
|
args: Args
|
||||||
|
directory?: string
|
||||||
|
fetch?: typeof fetch
|
||||||
|
events?: EventSource
|
||||||
|
onExit?: () => Promise<void>
|
||||||
|
}) {
|
||||||
// promise to prevent immediate exit
|
// promise to prevent immediate exit
|
||||||
return new Promise<void>(async (resolve) => {
|
return new Promise<void>(async (resolve) => {
|
||||||
const mode = await getTerminalBackgroundColor()
|
const mode = await getTerminalBackgroundColor()
|
||||||
@@ -117,7 +126,12 @@ export function tui(input: { url: string; args: Args; directory?: string; onExit
|
|||||||
<KVProvider>
|
<KVProvider>
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<RouteProvider>
|
<RouteProvider>
|
||||||
<SDKProvider url={input.url} directory={input.directory}>
|
<SDKProvider
|
||||||
|
url={input.url}
|
||||||
|
directory={input.directory}
|
||||||
|
fetch={input.fetch}
|
||||||
|
events={input.events}
|
||||||
|
>
|
||||||
<SyncProvider>
|
<SyncProvider>
|
||||||
<ThemeProvider mode={mode}>
|
<ThemeProvider mode={mode}>
|
||||||
<LocalProvider>
|
<LocalProvider>
|
||||||
@@ -592,15 +606,6 @@ function App() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
sdk.event.on(Installation.Event.Updated.type, (evt) => {
|
|
||||||
toast.show({
|
|
||||||
variant: "success",
|
|
||||||
title: "Update Complete",
|
|
||||||
message: `OpenCode updated to v${evt.properties.version}`,
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
sdk.event.on(Installation.Event.UpdateAvailable.type, (evt) => {
|
sdk.event.on(Installation.Event.UpdateAvailable.type, (evt) => {
|
||||||
toast.show({
|
toast.show({
|
||||||
variant: "info",
|
variant: "info",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export function createDialogProviderOptions() {
|
|||||||
description: {
|
description: {
|
||||||
opencode: "(Recommended)",
|
opencode: "(Recommended)",
|
||||||
anthropic: "(Claude Max or API key)",
|
anthropic: "(Claude Max or API key)",
|
||||||
|
openai: "(ChatGPT Plus/Pro or API key)",
|
||||||
}[provider.id],
|
}[provider.id],
|
||||||
category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other",
|
category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other",
|
||||||
async onSelect() {
|
async onSelect() {
|
||||||
@@ -111,8 +112,7 @@ function AutoMethod(props: AutoMethodProps) {
|
|||||||
|
|
||||||
useKeyboard((evt) => {
|
useKeyboard((evt) => {
|
||||||
if (evt.name === "c" && !evt.ctrl && !evt.meta) {
|
if (evt.name === "c" && !evt.ctrl && !evt.meta) {
|
||||||
const code =
|
const code = props.authorization.instructions.match(/[A-Z0-9]{4}-[A-Z0-9]{4}/)?.[0] ?? props.authorization.url
|
||||||
props.authorization.instructions.match(/[A-Z0-9]{4}-[A-Z0-9]{4}/)?.[0] ?? props.authorization.instructions
|
|
||||||
Clipboard.copy(code)
|
Clipboard.copy(code)
|
||||||
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
|
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
|
||||||
.catch(toast.error)
|
.catch(toast.error)
|
||||||
@@ -225,7 +225,7 @@ function ApiMethod(props: ApiMethodProps) {
|
|||||||
}
|
}
|
||||||
onConfirm={async (value) => {
|
onConfirm={async (value) => {
|
||||||
if (!value) return
|
if (!value) return
|
||||||
sdk.client.auth.set({
|
await sdk.client.auth.set({
|
||||||
providerID: props.providerID,
|
providerID: props.providerID,
|
||||||
auth: {
|
auth: {
|
||||||
type: "api",
|
type: "api",
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
import { createMemo, createSignal, For } from "solid-js"
|
|
||||||
import { useTheme } from "@tui/context/theme"
|
|
||||||
import { useKeybind } from "@tui/context/keybind"
|
|
||||||
import { TIPS } from "./tips"
|
|
||||||
import { EmptyBorder } from "./border"
|
|
||||||
|
|
||||||
type TipPart = { text: string; highlight: boolean }
|
|
||||||
|
|
||||||
function parseTip(tip: string): TipPart[] {
|
|
||||||
const parts: TipPart[] = []
|
|
||||||
const regex = /\{highlight\}(.*?)\{\/highlight\}/g
|
|
||||||
let lastIndex = 0
|
|
||||||
let match
|
|
||||||
|
|
||||||
while ((match = regex.exec(tip)) !== null) {
|
|
||||||
if (match.index > lastIndex) {
|
|
||||||
parts.push({ text: tip.slice(lastIndex, match.index), highlight: false })
|
|
||||||
}
|
|
||||||
parts.push({ text: match[1], highlight: true })
|
|
||||||
lastIndex = regex.lastIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastIndex < tip.length) {
|
|
||||||
parts.push({ text: tip.slice(lastIndex), highlight: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts
|
|
||||||
}
|
|
||||||
|
|
||||||
const [tipIndex, setTipIndex] = createSignal(Math.floor(Math.random() * TIPS.length))
|
|
||||||
|
|
||||||
export function randomizeTip() {
|
|
||||||
setTipIndex(Math.floor(Math.random() * TIPS.length))
|
|
||||||
}
|
|
||||||
|
|
||||||
const BOX_WIDTH = 42
|
|
||||||
const TITLE = " 🅘 Did you know? "
|
|
||||||
|
|
||||||
export function DidYouKnow() {
|
|
||||||
const { theme } = useTheme()
|
|
||||||
const keybind = useKeybind()
|
|
||||||
|
|
||||||
const tipParts = createMemo(() => parseTip(TIPS[tipIndex()]))
|
|
||||||
|
|
||||||
const dashes = createMemo(() => {
|
|
||||||
// ╭─ + title + ─...─ + ╮ = BOX_WIDTH
|
|
||||||
// 1 + 1 + title.length + dashes + 1 = BOX_WIDTH
|
|
||||||
return Math.max(0, BOX_WIDTH - 2 - TITLE.length - 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<box position="absolute" bottom={3} right={2} width={BOX_WIDTH}>
|
|
||||||
<text>
|
|
||||||
<span style={{ fg: theme.border }}>╭─</span>
|
|
||||||
<span style={{ fg: theme.text }}>{TITLE}</span>
|
|
||||||
<span style={{ fg: theme.border }}>{"─".repeat(dashes())}╮</span>
|
|
||||||
</text>
|
|
||||||
<box
|
|
||||||
border={["left", "right", "bottom"]}
|
|
||||||
borderColor={theme.border}
|
|
||||||
customBorderChars={{
|
|
||||||
...EmptyBorder,
|
|
||||||
bottomLeft: "╰",
|
|
||||||
bottomRight: "╯",
|
|
||||||
horizontal: "─",
|
|
||||||
vertical: "│",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<box paddingLeft={2} paddingRight={2} paddingTop={1} paddingBottom={1}>
|
|
||||||
<text>
|
|
||||||
<For each={tipParts()}>
|
|
||||||
{(part) => <span style={{ fg: part.highlight ? theme.text : theme.textMuted }}>{part.text}</span>}
|
|
||||||
</For>
|
|
||||||
</text>
|
|
||||||
</box>
|
|
||||||
</box>
|
|
||||||
<box flexDirection="row" justifyContent="flex-end">
|
|
||||||
<text>
|
|
||||||
<span style={{ fg: theme.text }}>{keybind.print("tips_toggle")}</span>
|
|
||||||
<span style={{ fg: theme.textMuted }}> hide tips</span>
|
|
||||||
</text>
|
|
||||||
</box>
|
|
||||||
</box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { BoxRenderable, TextareaRenderable, KeyEvent, ScrollBoxRenderable } 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, Index, Show, createSignal } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { useSDK } from "@tui/context/sdk"
|
import { useSDK } from "@tui/context/sdk"
|
||||||
import { useSync } from "@tui/context/sync"
|
import { useSync } from "@tui/context/sync"
|
||||||
@@ -159,6 +159,26 @@ export function Autocomplete(props: {
|
|||||||
})
|
})
|
||||||
|
|
||||||
props.setPrompt((draft) => {
|
props.setPrompt((draft) => {
|
||||||
|
if (part.type === "file") {
|
||||||
|
const existingIndex = draft.parts.findIndex((p) => p.type === "file" && "url" in p && p.url === part.url)
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
const existing = draft.parts[existingIndex]
|
||||||
|
if (
|
||||||
|
part.source?.text &&
|
||||||
|
existing &&
|
||||||
|
"source" in existing &&
|
||||||
|
existing.source &&
|
||||||
|
"text" in existing.source &&
|
||||||
|
existing.source.text
|
||||||
|
) {
|
||||||
|
existing.source.text.start = extmarkStart
|
||||||
|
existing.source.text.end = extmarkEnd
|
||||||
|
existing.source.text.value = virtualText
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (part.type === "file" && part.source?.text) {
|
if (part.type === "file" && part.source?.text) {
|
||||||
part.source.text.start = extmarkStart
|
part.source.text.start = extmarkStart
|
||||||
part.source.text.end = extmarkEnd
|
part.source.text.end = extmarkEnd
|
||||||
@@ -601,8 +621,31 @@ export function Autocomplete(props: {
|
|||||||
(store.visible === "/" && value.match(/^\S+\s+\S+\s*$/))
|
(store.visible === "/" && value.match(/^\S+\s+\S+\s*$/))
|
||||||
) {
|
) {
|
||||||
hide()
|
hide()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if autocomplete should reopen (e.g., after backspace deleted a space)
|
||||||
|
const offset = props.input().cursorOffset
|
||||||
|
if (offset === 0) return
|
||||||
|
|
||||||
|
// Check for "/" at position 0 - reopen slash commands
|
||||||
|
if (value.startsWith("/") && !value.slice(0, offset).match(/\s/)) {
|
||||||
|
show("/")
|
||||||
|
setStore("index", 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for "@" trigger - find the nearest "@" before cursor with no whitespace between
|
||||||
|
const text = value.slice(0, offset)
|
||||||
|
const idx = text.lastIndexOf("@")
|
||||||
|
if (idx === -1) return
|
||||||
|
|
||||||
|
const between = text.slice(idx)
|
||||||
|
const before = idx === 0 ? undefined : value[idx - 1]
|
||||||
|
if ((before === undefined || /\s/.test(before)) && !between.match(/\s/)) {
|
||||||
|
show("@")
|
||||||
|
setStore("index", idx)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onKeyDown(e: KeyEvent) {
|
onKeyDown(e: KeyEvent) {
|
||||||
@@ -686,7 +729,7 @@ export function Autocomplete(props: {
|
|||||||
height={height()}
|
height={height()}
|
||||||
scrollbarOptions={{ visible: false }}
|
scrollbarOptions={{ visible: false }}
|
||||||
>
|
>
|
||||||
<For
|
<Index
|
||||||
each={options()}
|
each={options()}
|
||||||
fallback={
|
fallback={
|
||||||
<box paddingLeft={1} paddingRight={1}>
|
<box paddingLeft={1} paddingRight={1}>
|
||||||
@@ -698,20 +741,22 @@ export function Autocomplete(props: {
|
|||||||
<box
|
<box
|
||||||
paddingLeft={1}
|
paddingLeft={1}
|
||||||
paddingRight={1}
|
paddingRight={1}
|
||||||
backgroundColor={index() === store.selected ? theme.primary : undefined}
|
backgroundColor={index === store.selected ? theme.primary : undefined}
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
|
onMouseOver={() => moveTo(index)}
|
||||||
|
onMouseUp={() => select()}
|
||||||
>
|
>
|
||||||
<text fg={index() === store.selected ? selectedForeground(theme) : theme.text} flexShrink={0}>
|
<text fg={index === store.selected ? selectedForeground(theme) : theme.text} flexShrink={0}>
|
||||||
{option.display}
|
{option().display}
|
||||||
</text>
|
</text>
|
||||||
<Show when={option.description}>
|
<Show when={option().description}>
|
||||||
<text fg={index() === store.selected ? selectedForeground(theme) : theme.textMuted} wrapMode="none">
|
<text fg={index === store.selected ? selectedForeground(theme) : theme.textMuted} wrapMode="none">
|
||||||
{option.description}
|
{option().description}
|
||||||
</text>
|
</text>
|
||||||
</Show>
|
</Show>
|
||||||
</box>
|
</box>
|
||||||
)}
|
)}
|
||||||
</For>
|
</Index>
|
||||||
</scrollbox>
|
</scrollbox>
|
||||||
</box>
|
</box>
|
||||||
)
|
)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user