Compare commits
405
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6a3be31de | ||
|
|
d16c8c9f0f | ||
|
|
fffe20cbe5 | ||
|
|
f6da3c467b | ||
|
|
c0d9f21c0f | ||
|
|
a67b616139 | ||
|
|
2991547974 | ||
|
|
b59def2e4a | ||
|
|
d842353f39 | ||
|
|
d20ef569de | ||
|
|
ca2b871810 | ||
|
|
23ea8ba1ce | ||
|
|
c417fec246 | ||
|
|
5413b16b57 | ||
|
|
43c021ed80 | ||
|
|
3b005d29d7 | ||
|
|
635f70f477 | ||
|
|
adbb6037ac | ||
|
|
598d6d00e4 | ||
|
|
cf934357c9 | ||
|
|
8063e645c7 | ||
|
|
8ab206b443 | ||
|
|
ec5c96e10d | ||
|
|
d2a61290b9 | ||
|
|
10faf9e717 | ||
|
|
cba239bc8f | ||
|
|
6f5e3ddfb3 | ||
|
|
d412ba264a | ||
|
|
9780f2b792 | ||
|
|
5aa2078852 | ||
|
|
eb975bb89c | ||
|
|
9479fe3ce6 | ||
|
|
4393cf8dbe | ||
|
|
447a4ca8c3 | ||
|
|
40ac2549ff | ||
|
|
a9c56b813a | ||
|
|
b1b73c9deb | ||
|
|
774377330b | ||
|
|
e7a157ef8f | ||
|
|
3989b9fc7f | ||
|
|
8bfcdf4831 | ||
|
|
3632ba3785 | ||
|
|
b7b3824d76 | ||
|
|
b12efb2023 | ||
|
|
bd91cf220c | ||
|
|
9eb6731c21 | ||
|
|
11373755d9 | ||
|
|
00b5e9f6ca | ||
|
|
6b3f424e4d | ||
|
|
e7dfeec9c4 | ||
|
|
97893bd7e6 | ||
|
|
bfefdb3752 | ||
|
|
12b79c581e | ||
|
|
ac9b4c7ebf | ||
|
|
208af232ff | ||
|
|
600c6b4973 | ||
|
|
61007a9b94 | ||
|
|
52fe1a5ac5 | ||
|
|
468927e06a | ||
|
|
61562dd9f0 | ||
|
|
c86dd91310 | ||
|
|
9c85a37811 | ||
|
|
51bba6e634 | ||
|
|
e1089bc5de | ||
|
|
618c654aa0 | ||
|
|
4703e859bd | ||
|
|
a1dc4ebbe4 | ||
|
|
e4e6096510 | ||
|
|
c472734933 | ||
|
|
9d068c20bb | ||
|
|
48e4f2f45d | ||
|
|
bbf4574476 | ||
|
|
8bad513140 | ||
|
|
1ff5d888c2 | ||
|
|
5d25758400 | ||
|
|
16fdc90976 | ||
|
|
793542230f | ||
|
|
9de1242d9b | ||
|
|
b3afa84058 | ||
|
|
024a10bbb5 | ||
|
|
bef9ac96e2 | ||
|
|
24bb293136 | ||
|
|
45180104fe | ||
|
|
edd86e3fb7 | ||
|
|
4a72d57534 | ||
|
|
0068cb305f | ||
|
|
90044196bf | ||
|
|
963a926db2 | ||
|
|
0d3d48bb59 | ||
|
|
66eaba4bdc | ||
|
|
21b6e5404e | ||
|
|
a0fe59ab75 | ||
|
|
81ebf56cf1 | ||
|
|
429708e3d5 | ||
|
|
d50f825c6d | ||
|
|
47bfae52c0 | ||
|
|
52cf9e3423 | ||
|
|
a9b6debfa2 | ||
|
|
d6bf475749 | ||
|
|
f22580e943 | ||
|
|
6d98db57c7 | ||
|
|
59f127a250 | ||
|
|
3068e7dcf7 | ||
|
|
f83d62191a | ||
|
|
3b72857124 | ||
|
|
68cd105d9d | ||
|
|
e09af2cb4b | ||
|
|
14bd3b1d30 | ||
|
|
3a9c2152f7 | ||
|
|
7283bfa480 | ||
|
|
37d5099728 | ||
|
|
d45fc030b2 | ||
|
|
c7042c807f | ||
|
|
202f6f1be9 | ||
|
|
759635eefa | ||
|
|
a9981441ae | ||
|
|
71302de4f1 | ||
|
|
333b8e907b | ||
|
|
13f319b64f | ||
|
|
b573eadd9e | ||
|
|
50bfff89c0 | ||
|
|
fc5fc2c570 | ||
|
|
4069999b78 | ||
|
|
5ba9b47b3c | ||
|
|
7c0cc94023 | ||
|
|
3ed1bd2e8e | ||
|
|
ce6436280a | ||
|
|
e49204bd33 | ||
|
|
856c87d05c | ||
|
|
de35c3fb84 | ||
|
|
4359719f9a | ||
|
|
5e13527416 | ||
|
|
aba94c658f | ||
|
|
6e318ba567 | ||
|
|
ddddecf88a | ||
|
|
16cb77c094 | ||
|
|
a5564f730e | ||
|
|
a15c97bbfe | ||
|
|
a398eed8b8 | ||
|
|
a10fd8ca5c | ||
|
|
ff7513238b | ||
|
|
af1cd60d3e | ||
|
|
c66def2049 | ||
|
|
008ccb4729 | ||
|
|
bc232045a1 | ||
|
|
16cab556df | ||
|
|
66148df74b | ||
|
|
4611e08f09 | ||
|
|
bf6204f577 | ||
|
|
17cde9feb7 | ||
|
|
7eccbdc4ac | ||
|
|
ab072290fc | ||
|
|
ad9d83748c | ||
|
|
55b57e1aae | ||
|
|
21b7877beb | ||
|
|
de50234a1a | ||
|
|
d60102ba52 | ||
|
|
066a876f3d | ||
|
|
c07a241ca8 | ||
|
|
0a2fffa9b5 | ||
|
|
bdfa213ccf | ||
|
|
7f0b2ce1ac | ||
|
|
0a2d7af179 | ||
|
|
37652f48fb | ||
|
|
8b19c6c7e4 | ||
|
|
a5365ce294 | ||
|
|
f4a4514a9f | ||
|
|
154006469c | ||
|
|
a1214fff2e | ||
|
|
9fd43ec616 | ||
|
|
5731c268b6 | ||
|
|
f4d892d4e1 | ||
|
|
10b3702938 | ||
|
|
e96442310c | ||
|
|
5c722bf8c4 | ||
|
|
58cc5cdf2a | ||
|
|
3c6dcad2af | ||
|
|
2535f9febf | ||
|
|
25678fa504 | ||
|
|
d7f4f3ec1f | ||
|
|
16ccb39459 | ||
|
|
f8630fb188 | ||
|
|
72e604744d | ||
|
|
832be6e7eb | ||
|
|
8ba48ed71d | ||
|
|
cf266f6162 | ||
|
|
1e6589526d | ||
|
|
f6b3ffaf64 | ||
|
|
5d765d63d4 | ||
|
|
0e12dd62a3 | ||
|
|
2b957b5d1c | ||
|
|
31c7a0157c | ||
|
|
e728b94bca | ||
|
|
49040c0130 | ||
|
|
0d05238ee6 | ||
|
|
9b8a7da1e6 | ||
|
|
61fd21182c | ||
|
|
487c2b5e76 | ||
|
|
0e4703b227 | ||
|
|
84e0232bd5 | ||
|
|
35fbb011b2 | ||
|
|
6527a123f0 | ||
|
|
0377cfd37c | ||
|
|
edc933d816 | ||
|
|
0d608f6014 | ||
|
|
69a45ef7d7 | ||
|
|
1056b36eae | ||
|
|
35c737ac68 | ||
|
|
725a2c2e95 | ||
|
|
c724d2392f | ||
|
|
f5230d1f02 | ||
|
|
078111bd96 | ||
|
|
736f8882f5 | ||
|
|
37cf365927 | ||
|
|
b939470302 | ||
|
|
ef4b2baedc | ||
|
|
64d28ea457 | ||
|
|
2520780846 | ||
|
|
986c60353e | ||
|
|
5fc26c958a | ||
|
|
c1cf9cda6a | ||
|
|
10d376eab2 | ||
|
|
53fc8a861b | ||
|
|
1d8330331c | ||
|
|
7a03c7fe38 | ||
|
|
09bd32169c | ||
|
|
7ec32f834e | ||
|
|
205492c7e8 | ||
|
|
4c2e888709 | ||
|
|
c78fd097d1 | ||
|
|
340966195b | ||
|
|
92604b391b | ||
|
|
0c51feb9c2 | ||
|
|
d0b4169a6b | ||
|
|
1fc6c6fb2a | ||
|
|
14f9b95557 | ||
|
|
d3bf1fa1fa | ||
|
|
a8836c5615 | ||
|
|
779a27693a | ||
|
|
829d86840a | ||
|
|
e225294dd4 | ||
|
|
a673e3650d | ||
|
|
ff462dfd7a | ||
|
|
73443585e5 | ||
|
|
609ab069a9 | ||
|
|
ec3579d7cb | ||
|
|
f80a3fea31 | ||
|
|
43a8d1b1ae | ||
|
|
09fa84ccfc | ||
|
|
b981f0a205 | ||
|
|
767038afc3 | ||
|
|
a7774115c5 | ||
|
|
288bc88e40 | ||
|
|
6d36dbf9de | ||
|
|
4ab4baf3a4 | ||
|
|
90f05eb9c2 | ||
|
|
b63b6d04c6 | ||
|
|
8addaa7e08 | ||
|
|
a96bf8e62d | ||
|
|
c8bda598f5 | ||
|
|
c857cff585 | ||
|
|
fd9d2db755 | ||
|
|
b19fd14f80 | ||
|
|
715265de4b | ||
|
|
2048f32491 | ||
|
|
2d7ba43a21 | ||
|
|
bd198d8550 | ||
|
|
cbffbcdd3d | ||
|
|
2be8b2269f | ||
|
|
a0f469095c | ||
|
|
0ccb26df94 | ||
|
|
71fd5966ad | ||
|
|
c02230de4f | ||
|
|
aa2e2c76c0 | ||
|
|
7c2d4ee79a | ||
|
|
e3a2728fa3 | ||
|
|
18260b037b | ||
|
|
ad83dd3ad9 | ||
|
|
6f37315cd1 | ||
|
|
d81dce6a82 | ||
|
|
0bd11e970b | ||
|
|
7e29e1dd23 | ||
|
|
491a2adf8d | ||
|
|
c07d6487a8 | ||
|
|
9990e84d37 | ||
|
|
0b86adbe99 | ||
|
|
834a2c09d5 | ||
|
|
f13c17e654 | ||
|
|
a0611d92e4 | ||
|
|
c1fa257a92 | ||
|
|
7fd81dd93e | ||
|
|
d554e7aaef | ||
|
|
0b001c3e80 | ||
|
|
53b7cb62c4 | ||
|
|
c5e096c76a | ||
|
|
e1fc4a756b | ||
|
|
e5bc4cbbcf | ||
|
|
459d5ec19b | ||
|
|
8baa222621 | ||
|
|
ce1397cc34 | ||
|
|
dc7c5ced4c | ||
|
|
b8e8fe7e31 | ||
|
|
890085758f | ||
|
|
85f15893bc | ||
|
|
98be75b17c | ||
|
|
b5cc27b8ea | ||
|
|
05937b52cc | ||
|
|
62b82570e1 | ||
|
|
4bf75c0b44 | ||
|
|
a8a06c4983 | ||
|
|
b0b7fd143b | ||
|
|
140498eb4f | ||
|
|
ca5126e24d | ||
|
|
fb2b3e567c | ||
|
|
c672a1963b | ||
|
|
54bff6b120 | ||
|
|
ab3f198fab | ||
|
|
0057ef6336 | ||
|
|
4f604b3839 | ||
|
|
a20489584e | ||
|
|
a6b066bd47 | ||
|
|
37fdcac05a | ||
|
|
299bf1dca8 | ||
|
|
d685aa38ef | ||
|
|
995b23787c | ||
|
|
ed8e663e13 | ||
|
|
38cee3b848 | ||
|
|
6d116d4b54 | ||
|
|
7c4f111b34 | ||
|
|
f2fac29270 | ||
|
|
12892f0e12 | ||
|
|
9714a3558e | ||
|
|
e49a1d1f39 | ||
|
|
528565510d | ||
|
|
36cfda933d | ||
|
|
ecf5040966 | ||
|
|
7d56603c26 | ||
|
|
02b7cc8313 | ||
|
|
c9a52c9a85 | ||
|
|
dea668b0ea | ||
|
|
1bc3e92376 | ||
|
|
3f5acc3dff | ||
|
|
0588011476 | ||
|
|
bba72c82ae | ||
|
|
e95181a551 | ||
|
|
74e8c2e50f | ||
|
|
cdabafa264 | ||
|
|
0a92af60a0 | ||
|
|
c7808a4b01 | ||
|
|
7f978e07ff | ||
|
|
a4ae1bb9eb | ||
|
|
96a39803cc | ||
|
|
16f8f20b31 | ||
|
|
06b1684ddb | ||
|
|
c6e830c954 | ||
|
|
fc78c28df6 | ||
|
|
930a1bf358 | ||
|
|
6cf7f18cc9 | ||
|
|
3f59570ee6 | ||
|
|
304e956b5d | ||
|
|
538eaa42aa | ||
|
|
67c41fd389 | ||
|
|
83ea19770a | ||
|
|
3ace8543b2 | ||
|
|
eb855e1e31 | ||
|
|
5e53f054c6 | ||
|
|
5d5e184329 | ||
|
|
2fbb49ac30 | ||
|
|
c56b407e1d | ||
|
|
bdaa0e8b8c | ||
|
|
4e549b1c05 | ||
|
|
7be8e16c33 | ||
|
|
d1588f93a1 | ||
|
|
576696a370 | ||
|
|
2c6f9043e8 | ||
|
|
9f771ef0ae | ||
|
|
356715f67d | ||
|
|
540421267a | ||
|
|
e253398936 | ||
|
|
ee87e1f139 | ||
|
|
8887616457 | ||
|
|
905c034885 | ||
|
|
92f7c4943f | ||
|
|
10bde356b1 | ||
|
|
f7cc46cd9f | ||
|
|
d9ffe07391 | ||
|
|
c0702ed8bd | ||
|
|
b927b9dca6 | ||
|
|
4b7231be68 | ||
|
|
70a6fe96ea | ||
|
|
6e5971dff2 | ||
|
|
d48d6b3577 | ||
|
|
4b1668c3ef | ||
|
|
d85eb1b880 | ||
|
|
9637d70407 | ||
|
|
cfbbdc2e14 | ||
|
|
feb65201f6 | ||
|
|
f1f07a56d8 | ||
|
|
0fe313bd87 | ||
|
|
1fd676528d | ||
|
|
0a2801444b | ||
|
|
c9adbc7c21 | ||
|
|
7088bfabd7 | ||
|
|
dbdbfb8543 | ||
|
|
521803aaa3 |
@@ -13,9 +13,9 @@ runs:
|
|||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.bun
|
path: ~/.bun
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lockb', 'bun.lock') }}
|
key: ${{ runner.os }}-bun-${{ hashFiles('package.json') }}-${{ hashFiles('bun.lockb', 'bun.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-${{ hashFiles('package.json') }}-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install
|
run: bun install
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
#
|
||||||
|
# This file is intentionally in the wrong dir, will move and add later....
|
||||||
|
#
|
||||||
|
|
||||||
name: Guidelines Check
|
name: Guidelines Check
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -28,14 +28,14 @@ jobs:
|
|||||||
const versionPattern = /[v]?1\.0\./i;
|
const versionPattern = /[v]?1\.0\./i;
|
||||||
const isVersionRelated = versionPattern.test(title) || versionPattern.test(description);
|
const isVersionRelated = versionPattern.test(title) || versionPattern.test(description);
|
||||||
|
|
||||||
|
// Check for "nix" keyword
|
||||||
|
const nixPattern = /\bnix\b/i;
|
||||||
|
const isNixRelated = nixPattern.test(title) || nixPattern.test(description);
|
||||||
|
|
||||||
|
const labels = [];
|
||||||
|
|
||||||
if (isWebRelated) {
|
if (isWebRelated) {
|
||||||
// Add web label
|
labels.push('web');
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: issue.number,
|
|
||||||
labels: ['web']
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assign to adamdotdevin
|
// Assign to adamdotdevin
|
||||||
await github.rest.issues.addAssignees({
|
await github.rest.issues.addAssignees({
|
||||||
@@ -46,10 +46,18 @@ jobs:
|
|||||||
});
|
});
|
||||||
} else if (isVersionRelated) {
|
} else if (isVersionRelated) {
|
||||||
// Only add opentui if NOT web-related
|
// Only add opentui if NOT web-related
|
||||||
|
labels.push('opentui');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNixRelated) {
|
||||||
|
labels.push('nix');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (labels.length > 0) {
|
||||||
await github.rest.issues.addLabels({
|
await github.rest.issues.addLabels({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
issue_number: issue.number,
|
issue_number: issue.number,
|
||||||
labels: ['opentui']
|
labels: labels
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ jobs:
|
|||||||
{
|
{
|
||||||
"bash": {
|
"bash": {
|
||||||
"gh issue*": "allow",
|
"gh issue*": "allow",
|
||||||
"*": "deny"
|
"*": "deny"
|
||||||
},
|
},
|
||||||
"webfetch": "deny"
|
"webfetch": "deny"
|
||||||
}
|
}
|
||||||
run: |
|
run: |
|
||||||
opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created:'
|
opencode run -m opencode/claude-haiku-4-5 "A new issue has been created:'
|
||||||
|
|
||||||
Issue number:
|
Issue number:
|
||||||
${{ github.event.issue.number }}
|
${{ github.event.issue.number }}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
- opentui
|
- fix-snapshot-2
|
||||||
- v0
|
- v0
|
||||||
|
|
||||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: "sync-zed-extension"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
zed:
|
||||||
|
name: Release Zed Extension
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: ./.github/actions/setup-bun
|
||||||
|
|
||||||
|
- name: Get version tag
|
||||||
|
id: get_tag
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "release" ]; then
|
||||||
|
TAG="${{ github.event.release.tag_name }}"
|
||||||
|
else
|
||||||
|
TAG=$(git tag --list 'v[0-9]*.*' --sort=-version:refname | head -n 1)
|
||||||
|
fi
|
||||||
|
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Using tag: ${TAG}"
|
||||||
|
|
||||||
|
- name: Sync Zed extension
|
||||||
|
run: |
|
||||||
|
./script/sync-zed.ts ${{ steps.get_tag.outputs.tag }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||||
@@ -28,3 +28,9 @@ jobs:
|
|||||||
bun turbo test
|
bun turbo test
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
|
|
||||||
|
- name: Check SDK is up to date
|
||||||
|
run: |
|
||||||
|
bun ./packages/sdk/js/script/build.ts
|
||||||
|
git diff --exit-code packages/sdk/js/src/gen packages/sdk/js/dist
|
||||||
|
continue-on-error: false
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
name: Update Nix Hashes
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "bun.lock"
|
||||||
|
- "package.json"
|
||||||
|
- "packages/*/package.json"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "bun.lock"
|
||||||
|
- "package.json"
|
||||||
|
- "packages/*/package.json"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
SYSTEM: x86_64-linux
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- 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: Update flake.lock
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
nix flake update
|
||||||
|
|
||||||
|
- name: Update node_modules hash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
nix/scripts/update-hashes.sh
|
||||||
|
|
||||||
|
- name: Commit hash changes
|
||||||
|
env:
|
||||||
|
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
summarize() {
|
||||||
|
local status="$1"
|
||||||
|
{
|
||||||
|
echo "### Nix Hash Update"
|
||||||
|
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=(flake.lock flake.nix nix/node-modules.nix nix/hashes.json)
|
||||||
|
STATUS="$(git status --short -- "${FILES[@]}" || true)"
|
||||||
|
if [ -z "$STATUS" ]; then
|
||||||
|
summarize "no changes"
|
||||||
|
echo "No changes to tracked Nix files. Hashes are already up to date."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add "${FILES[@]}"
|
||||||
|
git commit -m "Update Nix flake.lock and hashes"
|
||||||
|
|
||||||
|
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||||
|
git push origin HEAD:"$BRANCH"
|
||||||
|
|
||||||
|
summarize "committed $(git rev-parse --short HEAD)"
|
||||||
@@ -13,3 +13,8 @@ dist
|
|||||||
.turbo
|
.turbo
|
||||||
**/.serena
|
**/.serena
|
||||||
.serena/
|
.serena/
|
||||||
|
/result
|
||||||
|
refs
|
||||||
|
Session.vim
|
||||||
|
opencode.json
|
||||||
|
a.out
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Git commit and push
|
description: Git commit and push
|
||||||
|
subtask: true
|
||||||
---
|
---
|
||||||
|
|
||||||
commit and push
|
commit and push
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
description: "Find issue(s) on github"
|
||||||
|
model: opencode/claude-haiku-4-5
|
||||||
|
---
|
||||||
|
|
||||||
|
Search through existing issues in sst/opencode using the gh cli to find issues matching this query:
|
||||||
|
|
||||||
|
$ARGUMENTS
|
||||||
|
|
||||||
|
Consider:
|
||||||
|
|
||||||
|
1. Similar titles or descriptions
|
||||||
|
2. Same error messages or symptoms
|
||||||
|
3. Related functionality or components
|
||||||
|
4. Similar feature requests
|
||||||
|
|
||||||
|
Please list any matching issues with:
|
||||||
|
|
||||||
|
- Issue number and title
|
||||||
|
- Brief explanation of why it matches the query
|
||||||
|
- Link to the issue
|
||||||
|
|
||||||
|
If no clear matches are found, say so.
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://opencode.ai/config.json",
|
|
||||||
"plugin": ["opencode-openai-codex-auth"],
|
|
||||||
"mcp": {
|
|
||||||
"weather": {
|
|
||||||
"type": "local",
|
|
||||||
"command": ["bun", "x", "@h1deya/mcp-server-weather"]
|
|
||||||
},
|
|
||||||
"context7": {
|
|
||||||
"type": "remote",
|
|
||||||
"url": "https://mcp.context7.com/mcp",
|
|
||||||
"headers": {
|
|
||||||
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"plugin": ["opencode-openai-codex-auth"],
|
||||||
|
"provider": {
|
||||||
|
"opencode": {
|
||||||
|
"options": {
|
||||||
|
// "baseURL": "http://localhost:8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/theme.json",
|
||||||
|
"defs": {
|
||||||
|
"nord0": "#2E3440",
|
||||||
|
"nord1": "#3B4252",
|
||||||
|
"nord2": "#434C5E",
|
||||||
|
"nord3": "#4C566A",
|
||||||
|
"nord4": "#D8DEE9",
|
||||||
|
"nord5": "#E5E9F0",
|
||||||
|
"nord6": "#ECEFF4",
|
||||||
|
"nord7": "#8FBCBB",
|
||||||
|
"nord8": "#88C0D0",
|
||||||
|
"nord9": "#81A1C1",
|
||||||
|
"nord10": "#5E81AC",
|
||||||
|
"nord11": "#BF616A",
|
||||||
|
"nord12": "#D08770",
|
||||||
|
"nord13": "#EBCB8B",
|
||||||
|
"nord14": "#A3BE8C",
|
||||||
|
"nord15": "#B48EAD"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"primary": {
|
||||||
|
"dark": "nord8",
|
||||||
|
"light": "nord10"
|
||||||
|
},
|
||||||
|
"secondary": {
|
||||||
|
"dark": "nord9",
|
||||||
|
"light": "nord9"
|
||||||
|
},
|
||||||
|
"accent": {
|
||||||
|
"dark": "nord7",
|
||||||
|
"light": "nord7"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"dark": "nord11",
|
||||||
|
"light": "nord11"
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"dark": "nord12",
|
||||||
|
"light": "nord12"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"dark": "nord14",
|
||||||
|
"light": "nord14"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"dark": "nord8",
|
||||||
|
"light": "nord10"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"dark": "nord4",
|
||||||
|
"light": "nord0"
|
||||||
|
},
|
||||||
|
"textMuted": {
|
||||||
|
"dark": "nord3",
|
||||||
|
"light": "nord1"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"dark": "nord0",
|
||||||
|
"light": "nord6"
|
||||||
|
},
|
||||||
|
"backgroundPanel": {
|
||||||
|
"dark": "nord1",
|
||||||
|
"light": "nord5"
|
||||||
|
},
|
||||||
|
"backgroundElement": {
|
||||||
|
"dark": "nord1",
|
||||||
|
"light": "nord4"
|
||||||
|
},
|
||||||
|
"border": {
|
||||||
|
"dark": "nord2",
|
||||||
|
"light": "nord3"
|
||||||
|
},
|
||||||
|
"borderActive": {
|
||||||
|
"dark": "nord3",
|
||||||
|
"light": "nord2"
|
||||||
|
},
|
||||||
|
"borderSubtle": {
|
||||||
|
"dark": "nord2",
|
||||||
|
"light": "nord3"
|
||||||
|
},
|
||||||
|
"diffAdded": {
|
||||||
|
"dark": "nord14",
|
||||||
|
"light": "nord14"
|
||||||
|
},
|
||||||
|
"diffRemoved": {
|
||||||
|
"dark": "nord11",
|
||||||
|
"light": "nord11"
|
||||||
|
},
|
||||||
|
"diffContext": {
|
||||||
|
"dark": "nord3",
|
||||||
|
"light": "nord3"
|
||||||
|
},
|
||||||
|
"diffHunkHeader": {
|
||||||
|
"dark": "nord3",
|
||||||
|
"light": "nord3"
|
||||||
|
},
|
||||||
|
"diffHighlightAdded": {
|
||||||
|
"dark": "nord14",
|
||||||
|
"light": "nord14"
|
||||||
|
},
|
||||||
|
"diffHighlightRemoved": {
|
||||||
|
"dark": "nord11",
|
||||||
|
"light": "nord11"
|
||||||
|
},
|
||||||
|
"diffAddedBg": {
|
||||||
|
"dark": "#3B4252",
|
||||||
|
"light": "#E5E9F0"
|
||||||
|
},
|
||||||
|
"diffRemovedBg": {
|
||||||
|
"dark": "#3B4252",
|
||||||
|
"light": "#E5E9F0"
|
||||||
|
},
|
||||||
|
"diffContextBg": {
|
||||||
|
"dark": "nord1",
|
||||||
|
"light": "nord5"
|
||||||
|
},
|
||||||
|
"diffLineNumber": {
|
||||||
|
"dark": "nord2",
|
||||||
|
"light": "nord4"
|
||||||
|
},
|
||||||
|
"diffAddedLineNumberBg": {
|
||||||
|
"dark": "#3B4252",
|
||||||
|
"light": "#E5E9F0"
|
||||||
|
},
|
||||||
|
"diffRemovedLineNumberBg": {
|
||||||
|
"dark": "#3B4252",
|
||||||
|
"light": "#E5E9F0"
|
||||||
|
},
|
||||||
|
"markdownText": {
|
||||||
|
"dark": "nord4",
|
||||||
|
"light": "nord0"
|
||||||
|
},
|
||||||
|
"markdownHeading": {
|
||||||
|
"dark": "nord8",
|
||||||
|
"light": "nord10"
|
||||||
|
},
|
||||||
|
"markdownLink": {
|
||||||
|
"dark": "nord9",
|
||||||
|
"light": "nord9"
|
||||||
|
},
|
||||||
|
"markdownLinkText": {
|
||||||
|
"dark": "nord7",
|
||||||
|
"light": "nord7"
|
||||||
|
},
|
||||||
|
"markdownCode": {
|
||||||
|
"dark": "nord14",
|
||||||
|
"light": "nord14"
|
||||||
|
},
|
||||||
|
"markdownBlockQuote": {
|
||||||
|
"dark": "nord3",
|
||||||
|
"light": "nord3"
|
||||||
|
},
|
||||||
|
"markdownEmph": {
|
||||||
|
"dark": "nord12",
|
||||||
|
"light": "nord12"
|
||||||
|
},
|
||||||
|
"markdownStrong": {
|
||||||
|
"dark": "nord13",
|
||||||
|
"light": "nord13"
|
||||||
|
},
|
||||||
|
"markdownHorizontalRule": {
|
||||||
|
"dark": "nord3",
|
||||||
|
"light": "nord3"
|
||||||
|
},
|
||||||
|
"markdownListItem": {
|
||||||
|
"dark": "nord8",
|
||||||
|
"light": "nord10"
|
||||||
|
},
|
||||||
|
"markdownListEnumeration": {
|
||||||
|
"dark": "nord7",
|
||||||
|
"light": "nord7"
|
||||||
|
},
|
||||||
|
"markdownImage": {
|
||||||
|
"dark": "nord9",
|
||||||
|
"light": "nord9"
|
||||||
|
},
|
||||||
|
"markdownImageText": {
|
||||||
|
"dark": "nord7",
|
||||||
|
"light": "nord7"
|
||||||
|
},
|
||||||
|
"markdownCodeBlock": {
|
||||||
|
"dark": "nord4",
|
||||||
|
"light": "nord0"
|
||||||
|
},
|
||||||
|
"syntaxComment": {
|
||||||
|
"dark": "nord3",
|
||||||
|
"light": "nord3"
|
||||||
|
},
|
||||||
|
"syntaxKeyword": {
|
||||||
|
"dark": "nord9",
|
||||||
|
"light": "nord9"
|
||||||
|
},
|
||||||
|
"syntaxFunction": {
|
||||||
|
"dark": "nord8",
|
||||||
|
"light": "nord8"
|
||||||
|
},
|
||||||
|
"syntaxVariable": {
|
||||||
|
"dark": "nord7",
|
||||||
|
"light": "nord7"
|
||||||
|
},
|
||||||
|
"syntaxString": {
|
||||||
|
"dark": "nord14",
|
||||||
|
"light": "nord14"
|
||||||
|
},
|
||||||
|
"syntaxNumber": {
|
||||||
|
"dark": "nord15",
|
||||||
|
"light": "nord15"
|
||||||
|
},
|
||||||
|
"syntaxType": {
|
||||||
|
"dark": "nord7",
|
||||||
|
"light": "nord7"
|
||||||
|
},
|
||||||
|
"syntaxOperator": {
|
||||||
|
"dark": "nord9",
|
||||||
|
"light": "nord9"
|
||||||
|
},
|
||||||
|
"syntaxPunctuation": {
|
||||||
|
"dark": "nord4",
|
||||||
|
"light": "nord0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "bun",
|
||||||
|
"request": "attach",
|
||||||
|
"name": "opencode (attach)",
|
||||||
|
"url": "ws://localhost:6499/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"oven.bun-vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -42,6 +42,38 @@ Want to take on an issue? Leave a comment and a maintainer may assign it to you
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> After touching `packages/opencode/src/server/server.ts`, run "./packages/sdk/js/script/build.ts" to regenerate the JS sdk.
|
> After touching `packages/opencode/src/server/server.ts`, run "./packages/sdk/js/script/build.ts" to regenerate the JS sdk.
|
||||||
|
|
||||||
|
### Setting up a Debugger
|
||||||
|
|
||||||
|
Bun debugging is currently rough around the edges. We hope this guide helps you get set up and avoid some pain points.
|
||||||
|
|
||||||
|
The most reliable way to debug OpenCode is to run it manually in a terminal via `bun run --inspect=<url> dev ...` and attach
|
||||||
|
your debugger via that URL. Other methods can result in breakpoints being mapped incorrectly, at least in VSCode (YMMV).
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
|
||||||
|
- `*.tsx` files won't have their breakpoints correctly mapped. This seems due to Bun currently not supporting source maps on code transformed
|
||||||
|
via `BunPlugin`s (currently necessary due to our dependency on `@opentui/solid`). Currently, the best you can do in terms of debugging `*.tsx`
|
||||||
|
files is writing a `debugger;` statement. Debugging facilities like stepping won't work, but at least you will be informed if a specific code
|
||||||
|
is triggered.
|
||||||
|
- If you want to run the OpenCode TUI and have breakpoints triggered in the server code, you might need to run `bun dev spawn` instead of
|
||||||
|
the usual `bun dev`. This is because `bun dev` runs the server in a worker thread and breakpoints might not work there.
|
||||||
|
|
||||||
|
Other tips and tricks:
|
||||||
|
|
||||||
|
- You might want to use `--inspect-wait` or `--inspect-brk` instead of `--inspect`, depending on your workflow
|
||||||
|
- Specifying `--inspect=ws://localhost:6499/` on every invocation can be tiresome, you may want to `export BUN_OPTIONS=--inspect=ws://localhost:6499/` instead
|
||||||
|
|
||||||
|
#### VSCode Setup
|
||||||
|
|
||||||
|
If you use VSCode, you can use our example configurations [.vscode/settings.example.json](.vscode/settings.example.json) and [.vscode/launch.example.json](.vscode/launch.example.json).
|
||||||
|
|
||||||
|
Some debug methods that can be problematic:
|
||||||
|
|
||||||
|
- Debug configurations with `"request": "launch"` can have breakpoints incorrectly mapped and thus unusable
|
||||||
|
- The same problem arises when running OpenCode in the VSCode `JavaScript Debug Terminal`
|
||||||
|
|
||||||
|
With that said, you may want to try these methods, as they might work for you.
|
||||||
|
|
||||||
## Pull Request Expectations
|
## Pull Request Expectations
|
||||||
|
|
||||||
- Try to keep pull requests small and focused.
|
- Try to keep pull requests small and focused.
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ curl -fsSL https://opencode.ai/install | bash
|
|||||||
npm i -g opencode-ai@latest # or bun/pnpm/yarn
|
npm i -g opencode-ai@latest # or bun/pnpm/yarn
|
||||||
scoop bucket add extras; scoop install extras/opencode # Windows
|
scoop bucket add extras; scoop install extras/opencode # Windows
|
||||||
choco install opencode # Windows
|
choco install opencode # Windows
|
||||||
brew install opencode # macOS and Linux
|
brew install opencode # macOS and Linux
|
||||||
paru -S opencode-bin # Arch Linux
|
paru -S opencode-bin # Arch Linux
|
||||||
|
mise use --pin -g ubi:sst/opencode # Any OS
|
||||||
|
nix run nixpkgs#opencode # or github:sst/opencode for latest dev branch
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
@@ -50,6 +52,22 @@ OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bas
|
|||||||
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
|
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Agents
|
||||||
|
|
||||||
|
OpenCode includes two built-in agents you can switch between,
|
||||||
|
you can switch between these using the `Tab` key.
|
||||||
|
|
||||||
|
- **build** - Default, full access agent for development work
|
||||||
|
- **plan** - Read-only agent for analysis and code exploration
|
||||||
|
- Denies file edits by default
|
||||||
|
- Asks permission before running bash commands
|
||||||
|
- Ideal for exploring unfamiliar codebases or planning changes
|
||||||
|
|
||||||
|
Also, included is a **general** subagent for complex searches and multi-step tasks.
|
||||||
|
This is used internally and can be invoked using `@general` in messages.
|
||||||
|
|
||||||
|
Learn more about [agents](https://opencode.ai/docs/agents).
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
For more info on how to configure OpenCode [**head over to our docs**](https://opencode.ai/docs).
|
For more info on how to configure OpenCode [**head over to our docs**](https://opencode.ai/docs).
|
||||||
@@ -58,6 +76,10 @@ For more info on how to configure OpenCode [**head over to our docs**](https://o
|
|||||||
|
|
||||||
If you're interested in contributing to OpenCode, please read our [contributing docs](./CONTRIBUTING.md) before submitting a pull request.
|
If you're interested in contributing to OpenCode, please read our [contributing docs](./CONTRIBUTING.md) before submitting a pull request.
|
||||||
|
|
||||||
|
### Building on OpenCode
|
||||||
|
|
||||||
|
If you are working on a project that's related to OpenCode and is using "opencode" as a part of its name; for example, "opencode-dashboard" or "opencode-mobile", please add a note to your README to clarify that it is not built by the OpenCode team and is not affiliated with us in anyway.
|
||||||
|
|
||||||
### FAQ
|
### FAQ
|
||||||
|
|
||||||
#### How is this different than Claude Code?
|
#### How is this different than Claude Code?
|
||||||
@@ -65,7 +87,7 @@ If you're interested in contributing to OpenCode, please read our [contributing
|
|||||||
It's very similar to Claude Code in terms of capability. Here are the key differences:
|
It's very similar to Claude Code in terms of capability. Here are the key differences:
|
||||||
|
|
||||||
- 100% open source
|
- 100% open source
|
||||||
- Not coupled to any provider. Although Anthropic is recommended, OpenCode can be used with OpenAI, Google or even local models. As models evolve the gaps between them will close and pricing will drop so being provider-agnostic is important.
|
- Not coupled to any provider. Although we recommend the models we provide through [OpenCode Zen](https://opencode.ai/zen); OpenCode can be used with Claude, OpenAI, Google or even local models. As models evolve the gaps between them will close and pricing will drop so being provider-agnostic is important.
|
||||||
- Out of the box LSP support
|
- Out of the box LSP support
|
||||||
- A focus on TUI. OpenCode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal.
|
- A focus on TUI. OpenCode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal.
|
||||||
- A client/server architecture. This for example can allow OpenCode to run on your computer, while you can drive it remotely from a mobile app. Meaning that the TUI frontend is just one of the possible clients.
|
- A client/server architecture. This for example can allow OpenCode to run on your computer, while you can drive it remotely from a mobile app. Meaning that the TUI frontend is just one of the possible clients.
|
||||||
|
|||||||
@@ -134,3 +134,15 @@
|
|||||||
| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) |
|
| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) |
|
||||||
| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) |
|
| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) |
|
||||||
| 2025-11-08 | 706,035 (+9,389) | 653,489 (+11,343) | 1,359,524 (+20,732) |
|
| 2025-11-08 | 706,035 (+9,389) | 653,489 (+11,343) | 1,359,524 (+20,732) |
|
||||||
|
| 2025-11-09 | 713,462 (+7,427) | 660,459 (+6,970) | 1,373,921 (+14,397) |
|
||||||
|
| 2025-11-10 | 722,288 (+8,826) | 668,225 (+7,766) | 1,390,513 (+16,592) |
|
||||||
|
| 2025-11-11 | 729,769 (+7,481) | 677,501 (+9,276) | 1,407,270 (+16,757) |
|
||||||
|
| 2025-11-12 | 740,180 (+10,411) | 686,454 (+8,953) | 1,426,634 (+19,364) |
|
||||||
|
| 2025-11-13 | 749,905 (+9,725) | 696,157 (+9,703) | 1,446,062 (+19,428) |
|
||||||
|
| 2025-11-14 | 759,928 (+10,023) | 705,237 (+9,080) | 1,465,165 (+19,103) |
|
||||||
|
| 2025-11-15 | 765,955 (+6,027) | 712,870 (+7,633) | 1,478,825 (+13,660) |
|
||||||
|
| 2025-11-16 | 771,069 (+5,114) | 716,596 (+3,726) | 1,487,665 (+8,840) |
|
||||||
|
| 2025-11-17 | 780,161 (+9,092) | 723,339 (+6,743) | 1,503,500 (+15,835) |
|
||||||
|
| 2025-11-18 | 791,563 (+11,402) | 732,544 (+9,205) | 1,524,107 (+20,607) |
|
||||||
|
| 2025-11-19 | 804,409 (+12,846) | 747,624 (+15,080) | 1,552,033 (+27,926) |
|
||||||
|
| 2025-11-20 | 814,620 (+10,211) | 757,907 (+10,283) | 1,572,527 (+20,494) |
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.15.0",
|
"@solidjs/router": "^0.15.0",
|
||||||
"@solidjs/start": "^1.1.0",
|
"@solidjs/start": "^1.1.0",
|
||||||
|
"chart.js": "4.5.1",
|
||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
"vinxi": "^0.5.7",
|
"vinxi": "^0.5.7",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
@@ -39,7 +41,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/core": {
|
"packages/console/core": {
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"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",
|
||||||
@@ -66,7 +68,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/function": {
|
"packages/console/function": {
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"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",
|
||||||
@@ -90,7 +92,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/mail": {
|
"packages/console/mail": {
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"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",
|
||||||
@@ -114,7 +116,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -154,7 +156,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "22.0.0",
|
"@octokit/rest": "22.0.0",
|
||||||
@@ -170,7 +172,7 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -178,6 +180,7 @@
|
|||||||
"@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/mcp": "0.0.8",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
@@ -188,8 +191,8 @@
|
|||||||
"@opencode-ai/plugin": "workspace:*",
|
"@opencode-ai/plugin": "workspace:*",
|
||||||
"@opencode-ai/script": "workspace:*",
|
"@opencode-ai/script": "workspace:*",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@opentui/core": "0.0.0-20251108-0c7899b1",
|
"@opentui/core": "0.1.47",
|
||||||
"@opentui/solid": "0.0.0-20251108-0c7899b1",
|
"@opentui/solid": "0.1.47",
|
||||||
"@parcel/watcher": "2.5.1",
|
"@parcel/watcher": "2.5.1",
|
||||||
"@pierre/precision-diffs": "catalog:",
|
"@pierre/precision-diffs": "catalog:",
|
||||||
"@solid-primitives/event-bus": "1.1.2",
|
"@solid-primitives/event-bus": "1.1.2",
|
||||||
@@ -248,7 +251,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
@@ -268,7 +271,7 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hey-api/openapi-ts": "0.81.0",
|
"@hey-api/openapi-ts": "0.81.0",
|
||||||
"@tsconfig/node22": "catalog:",
|
"@tsconfig/node22": "catalog:",
|
||||||
@@ -279,7 +282,7 @@
|
|||||||
},
|
},
|
||||||
"packages/slack": {
|
"packages/slack": {
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@slack/bolt": "^3.17.1",
|
"@slack/bolt": "^3.17.1",
|
||||||
@@ -292,7 +295,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -320,9 +323,19 @@
|
|||||||
"vite-plugin-solid": "catalog:",
|
"vite-plugin-solid": "catalog:",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"packages/util": {
|
||||||
|
"name": "@opencode-ai/util",
|
||||||
|
"version": "1.0.85",
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "catalog:",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "catalog:",
|
||||||
|
},
|
||||||
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cloudflare": "12.6.3",
|
"@astrojs/cloudflare": "12.6.3",
|
||||||
"@astrojs/markdown-remark": "6.3.1",
|
"@astrojs/markdown-remark": "6.3.1",
|
||||||
@@ -376,7 +389,7 @@
|
|||||||
"@types/bun": "1.3.0",
|
"@types/bun": "1.3.0",
|
||||||
"@types/node": "22.13.9",
|
"@types/node": "22.13.9",
|
||||||
"@typescript/native-preview": "7.0.0-dev.20251014.1",
|
"@typescript/native-preview": "7.0.0-dev.20251014.1",
|
||||||
"ai": "5.0.8",
|
"ai": "5.0.97",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"hono": "4.7.10",
|
"hono": "4.7.10",
|
||||||
@@ -411,12 +424,14 @@
|
|||||||
|
|
||||||
"@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/gateway": ["@ai-sdk/gateway@1.0.4", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-1roLdgMbFU3Nr4MC97/te7w6OqxsWBkDUkpbCcvxF3jz/ku91WVaJldn/PKU8feMKNyI5W9wnqhbjb1BqbExOQ=="],
|
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="],
|
||||||
|
|
||||||
"@ai-sdk/google": ["@ai-sdk/google@2.0.11", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-dnVIgSz1DZD/0gVau6ifYN3HZFN15HZwC9VjevTFfvrfSfbEvpXj5x/k/zk/0XuQrlQ5g8JiwJtxc9bx24x2xw=="],
|
"@ai-sdk/google": ["@ai-sdk/google@2.0.11", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-dnVIgSz1DZD/0gVau6ifYN3HZFN15HZwC9VjevTFfvrfSfbEvpXj5x/k/zk/0XuQrlQ5g8JiwJtxc9bx24x2xw=="],
|
||||||
|
|
||||||
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.16", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.9", "@ai-sdk/google": "2.0.11", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-tStlnOCRGRqKKJSCOtXhijX4r9kYVK2v+Vs7miJnfvr3sZfO8nRS0xnNhfgu17xuNi5LMMufeCYURTz4lKxzUQ=="],
|
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.16", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.9", "@ai-sdk/google": "2.0.11", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-tStlnOCRGRqKKJSCOtXhijX4r9kYVK2v+Vs7miJnfvr3sZfO8nRS0xnNhfgu17xuNi5LMMufeCYURTz4lKxzUQ=="],
|
||||||
|
|
||||||
|
"@ai-sdk/mcp": ["@ai-sdk/mcp@0.0.8", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q=="],
|
||||||
|
|
||||||
"@ai-sdk/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=="],
|
||||||
@@ -437,7 +452,7 @@
|
|||||||
|
|
||||||
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.2.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.1", "remark-smartypants": "^3.0.2", "shiki": "^3.0.0", "smol-toml": "^1.3.1", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-c5F5gGrkczUaTVgmMW9g1YMJGzOtRvjjhw6IfGuxarM6ct09MpwysP10US729dy07gg8y+ofVifezvP3BNsWZg=="],
|
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.2.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.1", "remark-smartypants": "^3.0.2", "shiki": "^3.0.0", "smol-toml": "^1.3.1", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-c5F5gGrkczUaTVgmMW9g1YMJGzOtRvjjhw6IfGuxarM6ct09MpwysP10US729dy07gg8y+ofVifezvP3BNsWZg=="],
|
||||||
|
|
||||||
"@astrojs/mdx": ["@astrojs/mdx@4.3.9", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.8", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "picocolors": "^1.1.1", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-80LHiM4z3FxAjATHNgFpa8nlTNSprAWB4UUKnr/QG56Pwk7uRnJWrXlok4wSCi/3fg8kTZ98A408Q91M+iqJdw=="],
|
"@astrojs/mdx": ["@astrojs/mdx@4.3.10", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.8", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "picocolors": "^1.1.1", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-2T5+XIr7PMqMeXhRofXY5NlY4lA0Km+wkfsqmr9lq5KXUHpGlKPQ9dlDZJP9E/CtljJyEBNS17zq66LrIJ1tiQ=="],
|
||||||
|
|
||||||
"@astrojs/prism": ["@astrojs/prism@3.2.0", "", { "dependencies": { "prismjs": "^1.29.0" } }, "sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw=="],
|
"@astrojs/prism": ["@astrojs/prism@3.2.0", "", { "dependencies": { "prismjs": "^1.29.0" } }, "sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw=="],
|
||||||
|
|
||||||
@@ -577,15 +592,15 @@
|
|||||||
|
|
||||||
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.9", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Drm7qlTKnvncEv+DANiQNEonq0H0LyIsoFZYJ6tJ8OhAoy5udIE8yp6BsVDYcIjcYLIybp4M7c/P7ly/56SoHg=="],
|
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.9", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Drm7qlTKnvncEv+DANiQNEonq0H0LyIsoFZYJ6tJ8OhAoy5udIE8yp6BsVDYcIjcYLIybp4M7c/P7ly/56SoHg=="],
|
||||||
|
|
||||||
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="],
|
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251105.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-nztUP35wTtUKM+681dBWtUNSySNWELTV+LY43oWy7ZhK19/iBJPQoFY7xpvF7zy4qOOShtise259B65DS4/71Q=="],
|
||||||
|
|
||||||
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="],
|
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251105.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WS/dvPYTW/+gs8s0UvDqDY7wcuIAg/hUpjrMNGepr+Mo38vMU39FYhJQOly99oJCXxMluQqAnRKg09b/9Gr+Rg=="],
|
||||||
|
|
||||||
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251011.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA=="],
|
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251105.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RdHRHo/hpjR6sNw529FkmslVSz/K3Pb1+i3fIoqUrHCrZOUYzFyz3nLeZh4EYaAhcztLWiSTwBv54bcl4sG3wA=="],
|
||||||
|
|
||||||
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251011.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ=="],
|
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251105.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-5zkxQCqLjwrqZVVJh92J2Drv6xifkP8kN2ltjHdwZQlVzfDW48d7tAtCm1ZooUv204ixvZFarusCfL+IRjExZg=="],
|
||||||
|
|
||||||
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251011.0", "", { "os": "win32", "cpu": "x64" }, "sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg=="],
|
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251105.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6BpkfjBIbGR+4FBOcZGcWDLM0XQuoI6R9Dublj/BKf4pv0/xJ4zHdnaYUb5NIlC75L55Ouqw0CEJasoKlMjgnw=="],
|
||||||
|
|
||||||
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20251008.0", "", {}, "sha512-dZLkO4PbCL0qcCSKzuW7KE4GYe49lI12LCfQ5y9XeSwgYBoAUbwH4gmJ6A0qUIURiTJTkGkRkhVPqpq2XNgYRA=="],
|
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20251008.0", "", {}, "sha512-dZLkO4PbCL0qcCSKzuW7KE4GYe49lI12LCfQ5y9XeSwgYBoAUbwH4gmJ6A0qUIURiTJTkGkRkhVPqpq2XNgYRA=="],
|
||||||
|
|
||||||
@@ -869,6 +884,8 @@
|
|||||||
|
|
||||||
"@kobalte/utils": ["@kobalte/utils@0.9.1", "", { "dependencies": { "@solid-primitives/event-listener": "^2.2.14", "@solid-primitives/keyed": "^1.2.0", "@solid-primitives/map": "^0.4.7", "@solid-primitives/media": "^2.2.4", "@solid-primitives/props": "^3.1.8", "@solid-primitives/refs": "^1.0.5", "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { "solid-js": "^1.8.8" } }, "sha512-eeU60A3kprIiBDAfv9gUJX1tXGLuZiKMajUfSQURAF2pk4ZoMYiqIzmrMBvzcxP39xnYttgTyQEVLwiTZnrV4w=="],
|
"@kobalte/utils": ["@kobalte/utils@0.9.1", "", { "dependencies": { "@solid-primitives/event-listener": "^2.2.14", "@solid-primitives/keyed": "^1.2.0", "@solid-primitives/map": "^0.4.7", "@solid-primitives/media": "^2.2.4", "@solid-primitives/props": "^3.1.8", "@solid-primitives/refs": "^1.0.5", "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { "solid-js": "^1.8.8" } }, "sha512-eeU60A3kprIiBDAfv9gUJX1tXGLuZiKMajUfSQURAF2pk4ZoMYiqIzmrMBvzcxP39xnYttgTyQEVLwiTZnrV4w=="],
|
||||||
|
|
||||||
|
"@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="],
|
||||||
|
|
||||||
"@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.0", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg=="],
|
"@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.0", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg=="],
|
||||||
|
|
||||||
"@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="],
|
"@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="],
|
||||||
@@ -961,25 +978,27 @@
|
|||||||
|
|
||||||
"@opencode-ai/ui": ["@opencode-ai/ui@workspace:packages/ui"],
|
"@opencode-ai/ui": ["@opencode-ai/ui@workspace:packages/ui"],
|
||||||
|
|
||||||
|
"@opencode-ai/util": ["@opencode-ai/util@workspace:packages/util"],
|
||||||
|
|
||||||
"@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"],
|
"@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"],
|
||||||
|
|
||||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||||
|
|
||||||
"@opentui/core": ["@opentui/core@0.0.0-20251108-0c7899b1", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20251108-0c7899b1", "@opentui/core-darwin-x64": "0.0.0-20251108-0c7899b1", "@opentui/core-linux-arm64": "0.0.0-20251108-0c7899b1", "@opentui/core-linux-x64": "0.0.0-20251108-0c7899b1", "@opentui/core-win32-arm64": "0.0.0-20251108-0c7899b1", "@opentui/core-win32-x64": "0.0.0-20251108-0c7899b1", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-uJ7wbVw2v5NnL6g3v72SjPLUwMl2wqOejUEo8t4NeBA8nsboSxggqkrqOYf6OOmCADoAqyFDY7akZMsz6HMZtg=="],
|
"@opentui/core": ["@opentui/core@0.1.47", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.47", "@opentui/core-darwin-x64": "0.1.47", "@opentui/core-linux-arm64": "0.1.47", "@opentui/core-linux-x64": "0.1.47", "@opentui/core-win32-arm64": "0.1.47", "@opentui/core-win32-x64": "0.1.47", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-gKcYX9EJ/e5VLEwBH2kalDr5xoI9MEanzQV7uV3Sb2Z9+ndwEUShKKna3odN8g4E20c4sX2VpwmB9hhl3Tsd9w=="],
|
||||||
|
|
||||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20251108-0c7899b1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DS9CmFmZZjwe6PIhz6zhZAsDx11DtyMFDxn8V3On2b8G892aBG6rHYtBBnsM28/1GGEJBTeDQ/jUXPVd6FNJ/g=="],
|
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.47", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0/u4VkJJPvW24cZzMaKf6Dm+VzeO1a94l6NV3AQ1Wb+pPTEyOmNWkRvj03ZrRLMCyQduaFVtlnor8DVCk6OHuQ=="],
|
||||||
|
|
||||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20251108-0c7899b1", "", { "os": "darwin", "cpu": "x64" }, "sha512-K4XwdmT6FTShn7EG8AKliPzO5H59R0XUlZi9+kfRVW59IIJtna5wxbu69SkA28dFoWj5i4yDumwoBI+tI7T6vg=="],
|
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.47", "", { "os": "darwin", "cpu": "x64" }, "sha512-y1+c/e+IaZAj5N02GnD+oaubbb5JiW5eKgF0h58kw73iXDMfynuoGOpREz58i1rUFYOMYJGdrSjEHtXk2pD2XA=="],
|
||||||
|
|
||||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20251108-0c7899b1", "", { "os": "linux", "cpu": "arm64" }, "sha512-3JUmxZeSvxV5yU7NEXSecy5Z1/LcVUMy1oWyusZgp96X0CTYAXMrolZt9IJDGO5raeO7JId1UaJmWW0r4DR8TA=="],
|
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.47", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZESHmqILtfb6FFEfi40JGKl8z0+LhOSoHgfOK1PPyuyRT9Mk8uXeQgPMF5W6Ac0pp4w+uWVC4TrFjijCCSiaUQ=="],
|
||||||
|
|
||||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20251108-0c7899b1", "", { "os": "linux", "cpu": "x64" }, "sha512-i/AQWGyanpPRpk9NK7Ze1tn+d5bqzM9wZFKNB3rd9d2Vbt/ROgBJItG6igz8vzKPKgnlHK4Gw9b5iG5sbjpd+Q=="],
|
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.47", "", { "os": "linux", "cpu": "x64" }, "sha512-qfvy1qshgnZMcAHQ3MS093IBjxM2pPx+kEnW7icsyud60zoJgoUugdN2kjgJiIJiYX3f3PgE68J6CVW2MCtYfQ=="],
|
||||||
|
|
||||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20251108-0c7899b1", "", { "os": "win32", "cpu": "arm64" }, "sha512-C7JLWuNN3w2txiVx3demwNwogVi4DQB5ZNHy2b09++kd2m449/RwGPyLcKpuoTzU4s/usYOeY4TxKIAd8cKedQ=="],
|
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.47", "", { "os": "win32", "cpu": "arm64" }, "sha512-f6OoPnaz303H6fudi8blS+iEcJtlFlcqdBoWnWnJQfN9rLmajW3Yf7RfpNOoLUlDcwxQLyTL/5EHwbcG8D4r7A=="],
|
||||||
|
|
||||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20251108-0c7899b1", "", { "os": "win32", "cpu": "x64" }, "sha512-mpOryp37YaHlTsN70LhiSn9hJJBktbyhlH/eB3N2K7H1ANYQVrekgBJ3rDxlH1GDVtRz6vLS3IDlyK75qNX4pg=="],
|
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.47", "", { "os": "win32", "cpu": "x64" }, "sha512-lQnJg7FucyyTbN/ybTj5FZ7S8OAfT5KxXDR5l9Sla7R5MIDY6nBXYM3GWeF81jzDd4K4Z/0hxNFtWSopEXRFYg=="],
|
||||||
|
|
||||||
"@opentui/solid": ["@opentui/solid@0.0.0-20251108-0c7899b1", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20251108-0c7899b1", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-tcsYnFGH/KBlQNG0IyZE2bisnm5NwN/w7theuWga3L1zoXqZqA5dQHutAVg4zkq5l/YKULeDI4jBlvz0lzH88A=="],
|
"@opentui/solid": ["@opentui/solid@0.1.47", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.47", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-azN2sf8X/6HiLkz8ip2lcY532ApNEkl+BHd+wml/HdwdgLE7nthgA6x8Pgvi7f4qkRmpeYATU+danIzB6K6B8A=="],
|
||||||
|
|
||||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||||
|
|
||||||
@@ -1129,49 +1148,49 @@
|
|||||||
|
|
||||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.2", "", { "os": "android", "cpu": "arm" }, "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.2", "", { "os": "android", "cpu": "arm64" }, "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.2", "", { "os": "linux", "cpu": "arm" }, "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.2", "", { "os": "linux", "cpu": "arm" }, "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
|
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.2", "", { "os": "linux", "cpu": "none" }, "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
|
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.2", "", { "os": "linux", "cpu": "none" }, "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.2", "", { "os": "linux", "cpu": "none" }, "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.2", "", { "os": "linux", "cpu": "x64" }, "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA=="],
|
||||||
|
|
||||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
|
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.2", "", { "os": "none", "cpu": "arm64" }, "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
|
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.2", "", { "os": "win32", "cpu": "x64" }, "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.2", "", { "os": "win32", "cpu": "x64" }, "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA=="],
|
||||||
|
|
||||||
"@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="],
|
"@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="],
|
||||||
|
|
||||||
@@ -1207,57 +1226,57 @@
|
|||||||
|
|
||||||
"@slack/web-api": ["@slack/web-api@6.13.0", "", { "dependencies": { "@slack/logger": "^3.0.0", "@slack/types": "^2.11.0", "@types/is-stream": "^1.1.0", "@types/node": ">=12.0.0", "axios": "^1.7.4", "eventemitter3": "^3.1.0", "form-data": "^2.5.0", "is-electron": "2.2.2", "is-stream": "^1.1.0", "p-queue": "^6.6.1", "p-retry": "^4.0.0" } }, "sha512-dv65crIgdh9ZYHrevLU6XFHTQwTyDmNqEqzuIrV+Vqe/vgiG6w37oex5ePDU1RGm2IJ90H8iOvHFvzdEO/vB+g=="],
|
"@slack/web-api": ["@slack/web-api@6.13.0", "", { "dependencies": { "@slack/logger": "^3.0.0", "@slack/types": "^2.11.0", "@types/is-stream": "^1.1.0", "@types/node": ">=12.0.0", "axios": "^1.7.4", "eventemitter3": "^3.1.0", "form-data": "^2.5.0", "is-electron": "2.2.2", "is-stream": "^1.1.0", "p-queue": "^6.6.1", "p-retry": "^4.0.0" } }, "sha512-dv65crIgdh9ZYHrevLU6XFHTQwTyDmNqEqzuIrV+Vqe/vgiG6w37oex5ePDU1RGm2IJ90H8iOvHFvzdEO/vB+g=="],
|
||||||
|
|
||||||
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="],
|
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA=="],
|
||||||
|
|
||||||
"@smithy/config-resolver": ["@smithy/config-resolver@4.4.2", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-4Jys0ni2tB2VZzgslbEgszZyMdTkPOFGA8g+So/NjR8oy6Qwaq4eSwsrRI+NMtb0Dq4kqCzGUu/nGUx7OM/xfw=="],
|
"@smithy/config-resolver": ["@smithy/config-resolver@4.4.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw=="],
|
||||||
|
|
||||||
"@smithy/core": ["@smithy/core@3.17.2", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ=="],
|
"@smithy/core": ["@smithy/core@3.18.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-vGSDXOJFZgOPTatSI1ly7Gwyy/d/R9zh2TO3y0JZ0uut5qQ88p9IaWaZYIWSSqtdekNM4CGok/JppxbAff4KcQ=="],
|
||||||
|
|
||||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-YVNMjhdz2pVto5bRdux7GMs0x1m0Afz3OcQy/4Yf9DH4fWOtroGH7uLvs7ZmDyoBJzLdegtIPpXrpJOZWvUXdw=="],
|
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ=="],
|
||||||
|
|
||||||
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="],
|
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA=="],
|
||||||
|
|
||||||
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="],
|
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg=="],
|
||||||
|
|
||||||
"@smithy/hash-node": ["@smithy/hash-node@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kKU0gVhx/ppVMntvUOZE7WRMFW86HuaxLwvqileBEjL7PoILI8/djoILw3gPQloGVE6O0oOzqafxeNi2KbnUJw=="],
|
"@smithy/hash-node": ["@smithy/hash-node@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA=="],
|
||||||
|
|
||||||
"@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-z6aDLGiHzsMhbS2MjetlIWopWz//K+mCoPXjW6aLr0mypF+Y7qdEh5TyJ20Onf9FbWHiWl4eC+rITdizpnXqOw=="],
|
"@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A=="],
|
||||||
|
|
||||||
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
|
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
|
||||||
|
|
||||||
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-hJRZuFS9UsElX4DJSJfoX4M1qXRH+VFiLMUnhsWvtOOUWRNvvOfDaUSdlNbjwv1IkpVjj/Rd/O59Jl3nhAcxow=="],
|
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A=="],
|
||||||
|
|
||||||
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="],
|
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.7", "", { "dependencies": { "@smithy/core": "^3.18.0", "@smithy/middleware-serde": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-i8Mi8OuY6Yi82Foe3iu7/yhBj1HBRoOQwBSsUNYglJTNSFaWYTNM2NauBBs/7pq2sqkLRqeUXA3Ogi2utzpUlQ=="],
|
||||||
|
|
||||||
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/protocol-http": "^5.3.4", "@smithy/service-error-classification": "^4.2.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-OhLx131znrEDxZPAvH/OYufR9d1nB2CQADyYFN4C3V/NQS7Mg4V6uvxHC/Dr96ZQW8IlHJTJ+vAhKt6oxWRndA=="],
|
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/service-error-classification": "^4.2.5", "@smithy/smithy-client": "^4.9.3", "@smithy/types": "^4.9.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-E7Vc6WHCHlzDRTx1W0jZ6J1L6ziEV0PIWcUdmfL4y+c8r7WYr6I+LkQudaD8Nfb7C5c4P3SQ972OmXHtv6m/OA=="],
|
||||||
|
|
||||||
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="],
|
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-La1ldWTJTZ5NqQyPqnCNeH9B+zjFhrNoQIL1jTh4zuqXRlmXhxYHhMtI1/92OlnoAtp6JoN7kzuwhWoXrBwPqg=="],
|
||||||
|
|
||||||
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="],
|
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ=="],
|
||||||
|
|
||||||
"@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="],
|
"@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.5", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg=="],
|
||||||
|
|
||||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="],
|
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.5", "", { "dependencies": { "@smithy/abort-controller": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw=="],
|
||||||
|
|
||||||
"@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="],
|
"@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="],
|
||||||
|
|
||||||
"@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="],
|
"@smithy/protocol-http": ["@smithy/protocol-http@5.3.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ=="],
|
||||||
|
|
||||||
"@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="],
|
"@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg=="],
|
||||||
|
|
||||||
"@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-aHb5cqXZocdzEkZ/CvhVjdw5l4r1aU/9iMEyoKzH4eXMowT6M0YjBpp7W/+XjkBnY8Xh0kVd55GKjnPKlCwinQ=="],
|
"@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ=="],
|
||||||
|
|
||||||
"@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="],
|
"@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0" } }, "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ=="],
|
||||||
|
|
||||||
"@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-y5ozxeQ9omVjbnJo9dtTsdXj9BEvGx2X8xvRgKnV+/7wLBuYJQL6dOa/qMY6omyHi7yjt1OA97jZLoVRYi8lxA=="],
|
"@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA=="],
|
||||||
|
|
||||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="],
|
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.5", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w=="],
|
||||||
|
|
||||||
"@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="],
|
"@smithy/smithy-client": ["@smithy/smithy-client@4.9.3", "", { "dependencies": { "@smithy/core": "^3.18.0", "@smithy/middleware-endpoint": "^4.3.7", "@smithy/middleware-stack": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-8tlueuTgV5n7inQCkhyptrB3jo2AO80uGrps/XTYZivv5MFQKKBj3CIWIGMI2fRY5LEduIiazOhAWdFknY1O9w=="],
|
||||||
|
|
||||||
"@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="],
|
"@smithy/types": ["@smithy/types@4.9.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA=="],
|
||||||
|
|
||||||
"@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="],
|
"@smithy/url-parser": ["@smithy/url-parser@4.2.5", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ=="],
|
||||||
|
|
||||||
"@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
|
"@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
|
||||||
|
|
||||||
@@ -1269,19 +1288,19 @@
|
|||||||
|
|
||||||
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
|
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
|
||||||
|
|
||||||
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.5", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GwaGjv/QLuL/QHQaqhf/maM7+MnRFQQs7Bsl6FlaeK6lm6U7mV5AAnVabw68cIoMl5FQFyKK62u7RWRzWL25OQ=="],
|
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.3", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-kbpuXbEf2YQ9zEE6eeVnUCQWO0e1BjMnKrXL8rfXgiWA0m8/E0leU4oSNzxP04WfCmW8vjEqaDeXWxwE4tpOjQ=="],
|
||||||
|
|
||||||
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.8", "", { "dependencies": { "@smithy/config-resolver": "^4.4.2", "@smithy/credential-provider-imds": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-gIoTf9V/nFSIZ0TtgDNLd+Ws59AJvijmMDYrOozoMHPJaG9cMRdqNO50jZTlbM6ydzQYY8L/mQ4tKSw/TB+s6g=="],
|
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.9", "", { "dependencies": { "@smithy/config-resolver": "^4.4.3", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.3", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-dgyribrVWN5qE5usYJ0m5M93mVM3L3TyBPZWe1Xl6uZlH2gzfQx3dz+ZCdW93lWqdedJRkOecnvbnoEEXRZ5VQ=="],
|
||||||
|
|
||||||
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="],
|
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A=="],
|
||||||
|
|
||||||
"@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
|
"@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
|
||||||
|
|
||||||
"@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="],
|
"@smithy/util-middleware": ["@smithy/util-middleware@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA=="],
|
||||||
|
|
||||||
"@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="],
|
"@smithy/util-retry": ["@smithy/util-retry@4.2.5", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg=="],
|
||||||
|
|
||||||
"@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="],
|
"@smithy/util-stream": ["@smithy/util-stream@4.5.6", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ=="],
|
||||||
|
|
||||||
"@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
|
"@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
|
||||||
|
|
||||||
@@ -1321,7 +1340,7 @@
|
|||||||
|
|
||||||
"@solidjs/meta": ["@solidjs/meta@0.29.4", "", { "peerDependencies": { "solid-js": ">=1.8.4" } }, "sha512-zdIWBGpR9zGx1p1bzIPqF5Gs+Ks/BH8R6fWhmUa/dcK1L2rUC8BAcZJzNRYBQv74kScf1TSOs0EY//Vd/I0V8g=="],
|
"@solidjs/meta": ["@solidjs/meta@0.29.4", "", { "peerDependencies": { "solid-js": ">=1.8.4" } }, "sha512-zdIWBGpR9zGx1p1bzIPqF5Gs+Ks/BH8R6fWhmUa/dcK1L2rUC8BAcZJzNRYBQv74kScf1TSOs0EY//Vd/I0V8g=="],
|
||||||
|
|
||||||
"@solidjs/router": ["@solidjs/router@0.15.3", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw=="],
|
"@solidjs/router": ["@solidjs/router@0.15.4", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-WOpgg9a9T638cR+5FGbFi/IV4l2FpmBs1GpIMSPa0Ce9vyJN7Wts+X2PqMf9IYn0zUj2MlSJtm1gp7/HI/n5TQ=="],
|
||||||
|
|
||||||
"@solidjs/start": ["@solidjs/start@1.2.0", "", { "dependencies": { "@tanstack/server-functions-plugin": "1.121.21", "@vinxi/plugin-directives": "^0.5.0", "@vinxi/server-components": "^0.5.0", "cookie-es": "^2.0.0", "defu": "^6.1.2", "error-stack-parser": "^2.1.4", "html-to-image": "^1.11.11", "radix3": "^1.1.0", "seroval": "^1.0.2", "seroval-plugins": "^1.0.2", "shiki": "^1.26.1", "source-map-js": "^1.0.2", "terracotta": "^1.0.4", "tinyglobby": "^0.2.2", "vite-plugin-solid": "^2.11.1" }, "peerDependencies": { "vinxi": "^0.5.7" } }, "sha512-SRv1g3R+4sxZnxCBPK1IedtLKsPhPJ7W/Yv4xEHjM4jJGPWi3ed35/yd0D5zhRK0C7zJIkZKbhnR/S3g8JUD5w=="],
|
"@solidjs/start": ["@solidjs/start@1.2.0", "", { "dependencies": { "@tanstack/server-functions-plugin": "1.121.21", "@vinxi/plugin-directives": "^0.5.0", "@vinxi/server-components": "^0.5.0", "cookie-es": "^2.0.0", "defu": "^6.1.2", "error-stack-parser": "^2.1.4", "html-to-image": "^1.11.11", "radix3": "^1.1.0", "seroval": "^1.0.2", "seroval-plugins": "^1.0.2", "shiki": "^1.26.1", "source-map-js": "^1.0.2", "terracotta": "^1.0.4", "tinyglobby": "^0.2.2", "vite-plugin-solid": "^2.11.1" }, "peerDependencies": { "vinxi": "^0.5.7" } }, "sha512-SRv1g3R+4sxZnxCBPK1IedtLKsPhPJ7W/Yv4xEHjM4jJGPWi3ed35/yd0D5zhRK0C7zJIkZKbhnR/S3g8JUD5w=="],
|
||||||
|
|
||||||
@@ -1455,7 +1474,7 @@
|
|||||||
|
|
||||||
"@types/scheduler": ["@types/scheduler@0.26.0", "", {}, "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA=="],
|
"@types/scheduler": ["@types/scheduler@0.26.0", "", {}, "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA=="],
|
||||||
|
|
||||||
"@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="],
|
"@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="],
|
||||||
|
|
||||||
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
||||||
|
|
||||||
@@ -1491,6 +1510,8 @@
|
|||||||
|
|
||||||
"@vercel/nft": ["@vercel/nft@0.30.3", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^10.4.5", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-UEq+eF0ocEf9WQCV1gktxKhha36KDs7jln5qii6UpPf5clMqDc0p3E7d9l2Smx0i9Pm1qpq4S4lLfNl97bbv6w=="],
|
"@vercel/nft": ["@vercel/nft@0.30.3", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^10.4.5", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-UEq+eF0ocEf9WQCV1gktxKhha36KDs7jln5qii6UpPf5clMqDc0p3E7d9l2Smx0i9Pm1qpq4S4lLfNl97bbv6w=="],
|
||||||
|
|
||||||
|
"@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="],
|
||||||
|
|
||||||
"@vinxi/listhen": ["@vinxi/listhen@1.5.6", "", { "dependencies": { "@parcel/watcher": "^2.3.0", "@parcel/watcher-wasm": "2.3.0", "citty": "^0.1.5", "clipboardy": "^4.0.0", "consola": "^3.2.3", "defu": "^6.1.4", "get-port-please": "^3.1.2", "h3": "^1.10.0", "http-shutdown": "^1.2.2", "jiti": "^1.21.0", "mlly": "^1.5.0", "node-forge": "^1.3.1", "pathe": "^1.1.2", "std-env": "^3.7.0", "ufo": "^1.3.2", "untun": "^0.1.3", "uqr": "^0.1.2" }, "bin": { "listen": "bin/listhen.mjs", "listhen": "bin/listhen.mjs" } }, "sha512-WSN1z931BtasZJlgPp704zJFnQFRg7yzSjkm3MzAWQYe4uXFXlFr1hc5Ac2zae5/HDOz5x1/zDM5Cb54vTCnWw=="],
|
"@vinxi/listhen": ["@vinxi/listhen@1.5.6", "", { "dependencies": { "@parcel/watcher": "^2.3.0", "@parcel/watcher-wasm": "2.3.0", "citty": "^0.1.5", "clipboardy": "^4.0.0", "consola": "^3.2.3", "defu": "^6.1.4", "get-port-please": "^3.1.2", "h3": "^1.10.0", "http-shutdown": "^1.2.2", "jiti": "^1.21.0", "mlly": "^1.5.0", "node-forge": "^1.3.1", "pathe": "^1.1.2", "std-env": "^3.7.0", "ufo": "^1.3.2", "untun": "^0.1.3", "uqr": "^0.1.2" }, "bin": { "listen": "bin/listhen.mjs", "listhen": "bin/listhen.mjs" } }, "sha512-WSN1z931BtasZJlgPp704zJFnQFRg7yzSjkm3MzAWQYe4uXFXlFr1hc5Ac2zae5/HDOz5x1/zDM5Cb54vTCnWw=="],
|
||||||
|
|
||||||
"@vinxi/plugin-directives": ["@vinxi/plugin-directives@0.5.1", "", { "dependencies": { "@babel/parser": "^7.23.5", "acorn": "^8.10.0", "acorn-jsx": "^5.3.2", "acorn-loose": "^8.3.0", "acorn-typescript": "^1.4.3", "astring": "^1.8.6", "magicast": "^0.2.10", "recast": "^0.23.4", "tslib": "^2.6.2" }, "peerDependencies": { "vinxi": "^0.5.5" } }, "sha512-pH/KIVBvBt7z7cXrUH/9uaqcdxjegFC7+zvkZkdOyWzs+kQD5KPf3cl8kC+5ayzXHT+OMlhGhyitytqN3cGmHg=="],
|
"@vinxi/plugin-directives": ["@vinxi/plugin-directives@0.5.1", "", { "dependencies": { "@babel/parser": "^7.23.5", "acorn": "^8.10.0", "acorn-jsx": "^5.3.2", "acorn-loose": "^8.3.0", "acorn-typescript": "^1.4.3", "astring": "^1.8.6", "magicast": "^0.2.10", "recast": "^0.23.4", "tslib": "^2.6.2" }, "peerDependencies": { "vinxi": "^0.5.5" } }, "sha512-pH/KIVBvBt7z7cXrUH/9uaqcdxjegFC7+zvkZkdOyWzs+kQD5KPf3cl8kC+5ayzXHT+OMlhGhyitytqN3cGmHg=="],
|
||||||
@@ -1525,7 +1546,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.8", "", { "dependencies": { "@ai-sdk/gateway": "1.0.4", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.1", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-qbnhj046UvG30V1S5WhjBn+RBGEAmi8PSZWqMhRsE3EPxvO5BcePXTZFA23e9MYyWS9zr4Vm8Mv3wQXwLmtIBw=="],
|
"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=="],
|
||||||
|
|
||||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||||
|
|
||||||
@@ -1587,7 +1608,7 @@
|
|||||||
|
|
||||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||||
|
|
||||||
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
|
"autoprefixer": ["autoprefixer@10.4.22", "", { "dependencies": { "browserslist": "^4.27.0", "caniuse-lite": "^1.0.30001754", "fraction.js": "^5.3.4", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg=="],
|
||||||
|
|
||||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||||
|
|
||||||
@@ -1617,9 +1638,9 @@
|
|||||||
|
|
||||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||||
|
|
||||||
"bare-events": ["bare-events@2.8.1", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ=="],
|
"bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="],
|
||||||
|
|
||||||
"bare-fs": ["bare-fs@4.5.0", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ=="],
|
"bare-fs": ["bare-fs@4.5.1", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg=="],
|
||||||
|
|
||||||
"bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="],
|
"bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="],
|
||||||
|
|
||||||
@@ -1669,7 +1690,7 @@
|
|||||||
|
|
||||||
"brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="],
|
"brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="],
|
||||||
|
|
||||||
"browserslist": ["browserslist@4.27.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", "electron-to-chromium": "^1.5.238", "node-releases": "^2.0.26", "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw=="],
|
"browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="],
|
||||||
|
|
||||||
"buffer": ["buffer@4.9.2", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="],
|
"buffer": ["buffer@4.9.2", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="],
|
||||||
|
|
||||||
@@ -1679,19 +1700,19 @@
|
|||||||
|
|
||||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||||
|
|
||||||
"bun-ffi-structs": ["bun-ffi-structs@0.1.0", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-NoRfJ81pgLIHCzw624/2GS2FuxcU0G4SRJww/4PXvheNVUPSIUjkOC6v1/8rk66tJVCb9oR0D6rDNKK0qT5O2Q=="],
|
"bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
||||||
|
|
||||||
"bun-webgpu": ["bun-webgpu@0.1.3", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.3", "bun-webgpu-darwin-x64": "^0.1.3", "bun-webgpu-linux-x64": "^0.1.3", "bun-webgpu-win32-x64": "^0.1.3" } }, "sha512-IXFxaIi4rgsEEpl9n/QVDm5RajCK/0FcOXZeMb52YRjoiAR1YVYK5hLrXT8cm+KDi6LVahA9GJFqOR4yiloVCw=="],
|
"bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
|
||||||
|
|
||||||
"bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KkNQ9gT7dxGDndQaHTTHss9miukqpczML3pO2nZJoT/nITwe9lw3ZGFJMujkW41BUQ1mDYKFgo5nBGf9xYHPAg=="],
|
"bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eDgLN9teKTfmvrCqgwwmWNsNszxYs7IZdCqk0S1DCarvMhr4wcajoSBlA/nQA0/owwLduPTS8xxCnQp4/N/gDg=="],
|
||||||
|
|
||||||
"bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-TODWnMUbCoqD/wqzlB3oGOBIUWIFly0lqMeBFz/MBV+ndjbnkNrP9huaZJCTkCVEPKGtd1FCM3ExZUtBbnGziA=="],
|
"bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-X+PjwJUWenUmdQBP8EtdItMyieQ6Nlpn+BH518oaouDiSnWj5+b0Y7DNDZJq7Ezom4EaxmqL/uGYZK3aCQ7CXg=="],
|
||||||
|
|
||||||
"bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.3", "", { "os": "linux", "cpu": "x64" }, "sha512-lVHORoVu1G61XVM8CRRqUsqr6w8kMlpuSpbPGpKUpmvrsoay6ymXAhT5lRPKyrGNamHUQTknmWdI59aRDCfLtQ=="],
|
"bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-zMLs2YIGB+/jxrYFXaFhVKX/GBt05UTF45lc9srcHc9JXGjEj+12CIo1CHLTAWatXMTqt0Jsu6ukWEoWVT/ayA=="],
|
||||||
|
|
||||||
"bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.3", "", { "os": "win32", "cpu": "x64" }, "sha512-vlspsFffctJlBnFfs2lW3QgDD6LyFu8VT18ryID7Qka5poTj0clGVRxz7DFRi7yva3GovEGw/82z/WVc5US8Pw=="],
|
"bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-Z5yAK28xrcm8Wb5k7TZ8FJKpOI/r+aVCRdlHYAqI2SDJFN3nD4mJs900X6kNVmG/xFzb5yOuKVYWGg+6ZXWbyA=="],
|
||||||
|
|
||||||
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
||||||
|
|
||||||
@@ -1711,7 +1732,7 @@
|
|||||||
|
|
||||||
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
|
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001753", "", {}, "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001754", "", {}, "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg=="],
|
||||||
|
|
||||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||||
|
|
||||||
@@ -1725,6 +1746,8 @@
|
|||||||
|
|
||||||
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
|
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
|
||||||
|
|
||||||
|
"chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="],
|
||||||
|
|
||||||
"cheerio": ["cheerio@1.0.0-rc.12", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "htmlparser2": "^8.0.1", "parse5": "^7.0.0", "parse5-htmlparser2-tree-adapter": "^7.0.0" } }, "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q=="],
|
"cheerio": ["cheerio@1.0.0-rc.12", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "htmlparser2": "^8.0.1", "parse5": "^7.0.0", "parse5-htmlparser2-tree-adapter": "^7.0.0" } }, "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q=="],
|
||||||
|
|
||||||
"cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
|
"cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
|
||||||
@@ -1931,7 +1954,7 @@
|
|||||||
|
|
||||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.245", "", {}, "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.250", "", {}, "sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw=="],
|
||||||
|
|
||||||
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||||
|
|
||||||
@@ -2029,7 +2052,7 @@
|
|||||||
|
|
||||||
"expressive-code": ["expressive-code@0.41.3", "", { "dependencies": { "@expressive-code/core": "^0.41.3", "@expressive-code/plugin-frames": "^0.41.3", "@expressive-code/plugin-shiki": "^0.41.3", "@expressive-code/plugin-text-markers": "^0.41.3" } }, "sha512-YLnD62jfgBZYrXIPQcJ0a51Afv9h8VlWqEGK9uU2T5nL/5rb8SnA86+7+mgCZe5D34Tff5RNEA5hjNVJYHzrFg=="],
|
"expressive-code": ["expressive-code@0.41.3", "", { "dependencies": { "@expressive-code/core": "^0.41.3", "@expressive-code/plugin-frames": "^0.41.3", "@expressive-code/plugin-shiki": "^0.41.3", "@expressive-code/plugin-text-markers": "^0.41.3" } }, "sha512-YLnD62jfgBZYrXIPQcJ0a51Afv9h8VlWqEGK9uU2T5nL/5rb8SnA86+7+mgCZe5D34Tff5RNEA5hjNVJYHzrFg=="],
|
||||||
|
|
||||||
"exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="],
|
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
|
||||||
|
|
||||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||||
|
|
||||||
@@ -2085,7 +2108,7 @@
|
|||||||
|
|
||||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
||||||
|
|
||||||
"framer-motion": ["framer-motion@8.5.5", "", { "dependencies": { "@motionone/dom": "^10.15.3", "hey-listen": "^1.0.8", "tslib": "^2.4.0" }, "optionalDependencies": { "@emotion/is-prop-valid": "^0.8.2" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-5IDx5bxkjWHWUF3CVJoSyUVOtrbAxtzYBBowRE2uYI/6VYhkEBD+rbTHEGuUmbGHRj6YqqSfoG7Aa1cLyWCrBA=="],
|
"framer-motion": ["framer-motion@8.5.5", "", { "dependencies": { "@motionone/dom": "^10.15.3", "hey-listen": "^1.0.8", "tslib": "^2.4.0" }, "optionalDependencies": { "@emotion/is-prop-valid": "^0.8.2" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-5IDx5bxkjWHWUF3CVJoSyUVOtrbAxtzYBBowRE2uYI/6VYhkEBD+rbTHEGuUmbGHRj6YqqSfoG7Aa1cLyWCrBA=="],
|
||||||
|
|
||||||
@@ -2683,7 +2706,7 @@
|
|||||||
|
|
||||||
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
||||||
|
|
||||||
"miniflare": ["miniflare@4.20251011.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-5oAaz6lqZus4QFwzEJiNtgpjZR2TBVwBeIhOW33V4gu+l23EukpKja831tFIX2o6sOD/hqZmKZHplOrWl3YGtQ=="],
|
"miniflare": ["miniflare@4.20251105.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251105.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-n+lCQbGLPjHFm5EKMohxCl+hLIki9rIlJSU9FkYKdJ62cGacetmTH5IgWUZhUFFM+NqhqZLOuWXTAsoZTm0hog=="],
|
||||||
|
|
||||||
"minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
|
"minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
|
||||||
|
|
||||||
@@ -2855,7 +2878,7 @@
|
|||||||
|
|
||||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||||
|
|
||||||
"path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="],
|
"path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="],
|
||||||
|
|
||||||
"path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
|
"path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
|
||||||
|
|
||||||
@@ -3073,7 +3096,7 @@
|
|||||||
|
|
||||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
|
"rollup": ["rollup@4.53.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.2", "@rollup/rollup-android-arm64": "4.53.2", "@rollup/rollup-darwin-arm64": "4.53.2", "@rollup/rollup-darwin-x64": "4.53.2", "@rollup/rollup-freebsd-arm64": "4.53.2", "@rollup/rollup-freebsd-x64": "4.53.2", "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", "@rollup/rollup-linux-arm-musleabihf": "4.53.2", "@rollup/rollup-linux-arm64-gnu": "4.53.2", "@rollup/rollup-linux-arm64-musl": "4.53.2", "@rollup/rollup-linux-loong64-gnu": "4.53.2", "@rollup/rollup-linux-ppc64-gnu": "4.53.2", "@rollup/rollup-linux-riscv64-gnu": "4.53.2", "@rollup/rollup-linux-riscv64-musl": "4.53.2", "@rollup/rollup-linux-s390x-gnu": "4.53.2", "@rollup/rollup-linux-x64-gnu": "4.53.2", "@rollup/rollup-linux-x64-musl": "4.53.2", "@rollup/rollup-openharmony-arm64": "4.53.2", "@rollup/rollup-win32-arm64-msvc": "4.53.2", "@rollup/rollup-win32-ia32-msvc": "4.53.2", "@rollup/rollup-win32-x64-gnu": "4.53.2", "@rollup/rollup-win32-x64-msvc": "4.53.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g=="],
|
||||||
|
|
||||||
"rollup-plugin-visualizer": ["rollup-plugin-visualizer@6.0.5", "", { "dependencies": { "open": "^8.0.0", "picomatch": "^4.0.2", "source-map": "^0.7.4", "yargs": "^17.5.1" }, "peerDependencies": { "rolldown": "1.x || ^1.0.0-beta", "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rolldown", "rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg=="],
|
"rollup-plugin-visualizer": ["rollup-plugin-visualizer@6.0.5", "", { "dependencies": { "open": "^8.0.0", "picomatch": "^4.0.2", "source-map": "^0.7.4", "yargs": "^17.5.1" }, "peerDependencies": { "rolldown": "1.x || ^1.0.0-beta", "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rolldown", "rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg=="],
|
||||||
|
|
||||||
@@ -3525,9 +3548,9 @@
|
|||||||
|
|
||||||
"wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
|
"wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
|
||||||
|
|
||||||
"workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="],
|
"workerd": ["workerd@1.20251105.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251105.0", "@cloudflare/workerd-darwin-arm64": "1.20251105.0", "@cloudflare/workerd-linux-64": "1.20251105.0", "@cloudflare/workerd-linux-arm64": "1.20251105.0", "@cloudflare/workerd-windows-64": "1.20251105.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-8D1UmsxrRr3Go7enbYCsYoiWeGn66u1WFNojPSgtjp7z8pV2cXskjr05vQ1OOzl7+rg1hDDofnCJqVwChMym8g=="],
|
||||||
|
|
||||||
"wrangler": ["wrangler@4.45.4", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.9", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-niXT7B463wQi7WXIHjYK8txgWhuKQLrGmhjoR58SnPhlkq4wGjd3rFrkVyRc/O58clGTfs672BSGOph4XMoQKw=="],
|
"wrangler": ["wrangler@4.46.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.9", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251105.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251105.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251014.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-WRROO7CL+MW/E44RMT4X7w32qPjufiPpGdey5D6H7iKzzVqfUkTRULxYBfWANiU1yGnsiCXQtu3Ap0G2TmohtA=="],
|
||||||
|
|
||||||
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||||
|
|
||||||
@@ -3557,7 +3580,7 @@
|
|||||||
|
|
||||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||||
|
|
||||||
"yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="],
|
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
|
||||||
|
|
||||||
"yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="],
|
"yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="],
|
||||||
|
|
||||||
@@ -3597,7 +3620,7 @@
|
|||||||
|
|
||||||
"@ai-sdk/amazon-bedrock/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"@ai-sdk/amazon-bedrock/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.1", "", { "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-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g=="],
|
"@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
||||||
|
|
||||||
"@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="],
|
"@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="],
|
||||||
|
|
||||||
@@ -3605,6 +3628,8 @@
|
|||||||
|
|
||||||
"@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="],
|
"@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
||||||
|
|
||||||
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||||
|
|
||||||
"@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="],
|
"@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="],
|
||||||
@@ -3727,6 +3752,8 @@
|
|||||||
|
|
||||||
"@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
"@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
||||||
|
|
||||||
|
"@opencode-ai/desktop/@solidjs/router": ["@solidjs/router@0.15.3", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw=="],
|
||||||
|
|
||||||
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/types": "3.4.2" } }, "sha512-I5baLVi/ynLEOZoWSAMlACHNnG+yw5HDmse0oe+GW6U1u+ULdEB3UHiVWaHoJSSONV7tlcVxuaMy74sREDkSvg=="],
|
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/types": "3.4.2" } }, "sha512-I5baLVi/ynLEOZoWSAMlACHNnG+yw5HDmse0oe+GW6U1u+ULdEB3UHiVWaHoJSSONV7tlcVxuaMy74sREDkSvg=="],
|
||||||
|
|
||||||
"@opencode-ai/web/@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="],
|
"@opencode-ai/web/@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="],
|
||||||
@@ -3799,8 +3826,6 @@
|
|||||||
|
|
||||||
"@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
|
"@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
|
||||||
|
|
||||||
"@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="],
|
|
||||||
|
|
||||||
"@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
"@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||||
|
|
||||||
"@vercel/nft/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
"@vercel/nft/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||||
@@ -3813,7 +3838,7 @@
|
|||||||
|
|
||||||
"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/provider-utils": ["@ai-sdk/provider-utils@3.0.1", "", { "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-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g=="],
|
"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=="],
|
||||||
|
|
||||||
@@ -3909,6 +3934,8 @@
|
|||||||
|
|
||||||
"gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
"gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||||
|
|
||||||
|
"gel/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
"giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
"giget/tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
|
"giget/tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
|
||||||
@@ -3961,7 +3988,7 @@
|
|||||||
|
|
||||||
"named-placeholders/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
|
"named-placeholders/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
|
||||||
|
|
||||||
"nitropack/c12": ["c12@3.3.1", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-LcWQ01LT9tkoUINHgpIOv3mMs+Abv7oVCrtpMRi1PaapVEpWoMga5WuT7/DqFTu7URP9ftbOmimNw1KNIGh9DQ=="],
|
"nitropack/c12": ["c12@3.3.2", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-QkikB2X5voO1okL3QsES0N690Sn/K9WokXqUsDQsWy5SnYb+psYQFGA10iy1bZHj3fjISKsI67Q90gruvWWM3A=="],
|
||||||
|
|
||||||
"nitropack/globby": ["globby@15.0.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "fast-glob": "^3.3.3", "ignore": "^7.0.5", "path-type": "^6.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.3.0" } }, "sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw=="],
|
"nitropack/globby": ["globby@15.0.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "fast-glob": "^3.3.3", "ignore": "^7.0.5", "path-type": "^6.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.3.0" } }, "sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw=="],
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
[install]
|
[install]
|
||||||
exact = true
|
exact = true
|
||||||
|
|||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1763618868,
|
||||||
|
"narHash": "sha256-v5afmLjn/uyD9EQuPBn7nZuaZVV9r+JerayK/4wvdWA=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a8d610af3f1a5fb71e23e08434d8d61a466fc942",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
description = "OpenCode development flake";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
nixpkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
systems = [
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-darwin"
|
||||||
|
"x86_64-darwin"
|
||||||
|
];
|
||||||
|
lib = nixpkgs.lib;
|
||||||
|
forEachSystem = lib.genAttrs systems;
|
||||||
|
pkgsFor = system: nixpkgs.legacyPackages.${system};
|
||||||
|
packageJson = builtins.fromJSON (builtins.readFile ./packages/opencode/package.json);
|
||||||
|
bunTarget = {
|
||||||
|
"aarch64-linux" = "bun-linux-arm64";
|
||||||
|
"x86_64-linux" = "bun-linux-x64";
|
||||||
|
"aarch64-darwin" = "bun-darwin-arm64";
|
||||||
|
"x86_64-darwin" = "bun-darwin-x64";
|
||||||
|
};
|
||||||
|
defaultNodeModules = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
||||||
|
hashesFile = "${./nix}/hashes.json";
|
||||||
|
hashesData =
|
||||||
|
if builtins.pathExists hashesFile then builtins.fromJSON (builtins.readFile hashesFile) else { };
|
||||||
|
nodeModulesHash = hashesData.nodeModules or defaultNodeModules;
|
||||||
|
modelsDev = forEachSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in
|
||||||
|
pkgs."models-dev"
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells = forEachSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
bun
|
||||||
|
nodejs_20
|
||||||
|
pkg-config
|
||||||
|
openssl
|
||||||
|
git
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
packages = forEachSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
mkNodeModules = pkgs.callPackage ./nix/node-modules.nix {
|
||||||
|
hash = nodeModulesHash;
|
||||||
|
};
|
||||||
|
mkPackage = pkgs.callPackage ./nix/opencode.nix { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = mkPackage {
|
||||||
|
version = packageJson.version;
|
||||||
|
src = ./.;
|
||||||
|
scripts = ./nix/scripts;
|
||||||
|
target = bunTarget.${system};
|
||||||
|
modelsDev = "${modelsDev.${system}}/dist/_api.json";
|
||||||
|
mkNodeModules = mkNodeModules;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
apps = forEachSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
opencode-dev = {
|
||||||
|
type = "app";
|
||||||
|
meta = {
|
||||||
|
description = "Nix devshell shell for OpenCode";
|
||||||
|
runtimeInputs = [ pkgs.bun ];
|
||||||
|
};
|
||||||
|
program = "${
|
||||||
|
pkgs.writeShellApplication {
|
||||||
|
name = "opencode-dev";
|
||||||
|
text = ''
|
||||||
|
exec bun run dev "$@"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
}/bin/opencode-dev";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"nodeModules": "sha256-bPiUpHGtgwVxHQHXBprpc6fFeJqW6/x7dwtQZBq29oU="
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{ hash, lib, stdenvNoCC, bun, cacert, curl }:
|
||||||
|
args:
|
||||||
|
stdenvNoCC.mkDerivation {
|
||||||
|
pname = "opencode-node_modules";
|
||||||
|
version = args.version;
|
||||||
|
src = args.src;
|
||||||
|
|
||||||
|
impureEnvVars =
|
||||||
|
lib.fetchers.proxyImpureEnvVars
|
||||||
|
++ [
|
||||||
|
"GIT_PROXY_COMMAND"
|
||||||
|
"SOCKS_SERVER"
|
||||||
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = [ bun cacert curl ];
|
||||||
|
|
||||||
|
dontConfigure = true;
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
export HOME=$(mktemp -d)
|
||||||
|
export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
|
||||||
|
bun install \
|
||||||
|
--cpu="*" \
|
||||||
|
--os="*" \
|
||||||
|
--frozen-lockfile \
|
||||||
|
--ignore-scripts \
|
||||||
|
--no-progress \
|
||||||
|
--linker=isolated
|
||||||
|
bun --bun ${args.canonicalizeScript}
|
||||||
|
bun --bun ${args.normalizeBinsScript}
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out
|
||||||
|
while IFS= read -r dir; do
|
||||||
|
rel="''${dir#./}"
|
||||||
|
dest="$out/$rel"
|
||||||
|
mkdir -p "$(dirname "$dest")"
|
||||||
|
cp -R "$dir" "$dest"
|
||||||
|
done < <(find . -type d -name node_modules -prune | sort)
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
dontFixup = true;
|
||||||
|
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
outputHashMode = "recursive";
|
||||||
|
outputHash = hash;
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
{ lib, stdenv, stdenvNoCC, bun, fzf, ripgrep, makeBinaryWrapper }:
|
||||||
|
args:
|
||||||
|
let
|
||||||
|
scripts = args.scripts;
|
||||||
|
mkModules =
|
||||||
|
attrs:
|
||||||
|
args.mkNodeModules (
|
||||||
|
attrs
|
||||||
|
// {
|
||||||
|
canonicalizeScript = scripts + "/canonicalize-node-modules.ts";
|
||||||
|
normalizeBinsScript = scripts + "/normalize-bun-binaries.ts";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
stdenvNoCC.mkDerivation (finalAttrs: {
|
||||||
|
pname = "opencode";
|
||||||
|
version = args.version;
|
||||||
|
|
||||||
|
src = args.src;
|
||||||
|
|
||||||
|
node_modules = mkModules {
|
||||||
|
version = finalAttrs.version;
|
||||||
|
src = finalAttrs.src;
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
bun
|
||||||
|
makeBinaryWrapper
|
||||||
|
];
|
||||||
|
|
||||||
|
configurePhase = ''
|
||||||
|
runHook preConfigure
|
||||||
|
cp -R ${finalAttrs.node_modules}/. .
|
||||||
|
runHook postConfigure
|
||||||
|
'';
|
||||||
|
|
||||||
|
env.MODELS_DEV_API_JSON = args.modelsDev;
|
||||||
|
env.OPENCODE_VERSION = args.version;
|
||||||
|
env.OPENCODE_CHANNEL = "stable";
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
|
||||||
|
cp ${scripts + "/bun-build.ts"} bun-build.ts
|
||||||
|
|
||||||
|
substituteInPlace bun-build.ts \
|
||||||
|
--replace '@VERSION@' "${finalAttrs.version}"
|
||||||
|
|
||||||
|
export BUN_COMPILE_TARGET=${args.target}
|
||||||
|
bun --bun bun-build.ts
|
||||||
|
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
dontStrip = true;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
cd packages/opencode
|
||||||
|
if [ ! -f opencode ]; then
|
||||||
|
echo "ERROR: opencode binary not found in $(pwd)"
|
||||||
|
ls -la
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -f opencode-worker.js ]; then
|
||||||
|
echo "ERROR: opencode worker bundle not found in $(pwd)"
|
||||||
|
ls -la
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
install -Dm755 opencode $out/bin/opencode
|
||||||
|
install -Dm644 opencode-worker.js $out/bin/opencode-worker.js
|
||||||
|
if [ -f opencode-assets.manifest ]; then
|
||||||
|
while IFS= read -r asset; do
|
||||||
|
[ -z "$asset" ] && continue
|
||||||
|
if [ ! -f "$asset" ]; then
|
||||||
|
echo "ERROR: referenced asset \"$asset\" missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
install -Dm644 "$asset" "$out/bin/$(basename "$asset")"
|
||||||
|
done < opencode-assets.manifest
|
||||||
|
fi
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
postFixup = ''
|
||||||
|
wrapProgram "$out/bin/opencode" --prefix PATH : ${lib.makeBinPath [ fzf ripgrep ]}
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "AI coding agent built for the terminal";
|
||||||
|
longDescription = ''
|
||||||
|
OpenCode is a terminal-based agent that can build anything.
|
||||||
|
It combines a TypeScript/JavaScript core with a Go-based TUI
|
||||||
|
to provide an interactive AI coding experience.
|
||||||
|
'';
|
||||||
|
homepage = "https://github.com/sst/opencode";
|
||||||
|
license = lib.licenses.mit;
|
||||||
|
platforms = [
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-darwin"
|
||||||
|
"x86_64-darwin"
|
||||||
|
];
|
||||||
|
mainProgram = "opencode";
|
||||||
|
};
|
||||||
|
})
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import solidPlugin from "./packages/opencode/node_modules/@opentui/solid/scripts/solid-plugin"
|
||||||
|
import path from "path"
|
||||||
|
import fs from "fs"
|
||||||
|
|
||||||
|
const version = "@VERSION@"
|
||||||
|
const pkg = path.join(process.cwd(), "packages/opencode")
|
||||||
|
const parser = fs.realpathSync(path.join(pkg, "./node_modules/@opentui/core/parser.worker.js"))
|
||||||
|
const worker = "./src/cli/cmd/tui/worker.ts"
|
||||||
|
const target = process.env["BUN_COMPILE_TARGET"]
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
throw new Error("BUN_COMPILE_TARGET not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
process.chdir(pkg)
|
||||||
|
|
||||||
|
const manifestName = "opencode-assets.manifest"
|
||||||
|
const manifestPath = path.join(pkg, manifestName)
|
||||||
|
|
||||||
|
const readTrackedAssets = () => {
|
||||||
|
if (!fs.existsSync(manifestPath)) return []
|
||||||
|
return fs
|
||||||
|
.readFileSync(manifestPath, "utf8")
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter((line) => line.length > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeTrackedAssets = () => {
|
||||||
|
for (const file of readTrackedAssets()) {
|
||||||
|
const filePath = path.join(pkg, file)
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
fs.rmSync(filePath, { force: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const assets = new Set<string>()
|
||||||
|
|
||||||
|
const addAsset = async (p: string) => {
|
||||||
|
const file = path.basename(p)
|
||||||
|
const dest = path.join(pkg, file)
|
||||||
|
await Bun.write(dest, Bun.file(p))
|
||||||
|
assets.add(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTrackedAssets()
|
||||||
|
|
||||||
|
const result = await Bun.build({
|
||||||
|
conditions: ["browser"],
|
||||||
|
tsconfig: "./tsconfig.json",
|
||||||
|
plugins: [solidPlugin],
|
||||||
|
sourcemap: "external",
|
||||||
|
entrypoints: ["./src/index.ts", parser, worker],
|
||||||
|
define: {
|
||||||
|
OPENCODE_VERSION: `'@VERSION@'`,
|
||||||
|
OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/" + path.relative(pkg, parser).replace(/\\/g, "/"),
|
||||||
|
OPENCODE_CHANNEL: "'latest'",
|
||||||
|
},
|
||||||
|
compile: {
|
||||||
|
target,
|
||||||
|
outfile: "opencode",
|
||||||
|
execArgv: ["--user-agent=opencode/" + version, '--env-file=""', "--"],
|
||||||
|
windows: {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
console.error("Build failed!")
|
||||||
|
for (const log of result.logs) {
|
||||||
|
console.error(log)
|
||||||
|
}
|
||||||
|
throw new Error("Compilation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
const assetOutputs = result.outputs?.filter((x) => x.kind === "asset") ?? []
|
||||||
|
for (const x of assetOutputs) {
|
||||||
|
await addAsset(x.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bundle = await Bun.build({
|
||||||
|
entrypoints: [worker],
|
||||||
|
tsconfig: "./tsconfig.json",
|
||||||
|
plugins: [solidPlugin],
|
||||||
|
target: "bun",
|
||||||
|
outdir: "./.opencode-worker",
|
||||||
|
sourcemap: "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!bundle.success) {
|
||||||
|
console.error("Worker build failed!")
|
||||||
|
for (const log of bundle.logs) {
|
||||||
|
console.error(log)
|
||||||
|
}
|
||||||
|
throw new Error("Worker compilation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
const workerAssets = bundle.outputs?.filter((x) => x.kind === "asset") ?? []
|
||||||
|
for (const x of workerAssets) {
|
||||||
|
await addAsset(x.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = bundle.outputs.find((x) => x.kind === "entry-point")
|
||||||
|
if (!output) {
|
||||||
|
throw new Error("Worker build produced no entry-point output")
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest = path.join(pkg, "opencode-worker.js")
|
||||||
|
await Bun.write(dest, Bun.file(output.path))
|
||||||
|
fs.rmSync(path.dirname(output.path), { recursive: true, force: true })
|
||||||
|
|
||||||
|
const list = Array.from(assets)
|
||||||
|
await Bun.write(manifestPath, list.length > 0 ? list.join("\n") + "\n" : "")
|
||||||
|
|
||||||
|
console.log("Build successful!")
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import { lstat, mkdir, readdir, rm, symlink } from "fs/promises"
|
||||||
|
import { join, relative } from "path"
|
||||||
|
|
||||||
|
type SemverLike = {
|
||||||
|
valid: (value: string) => string | null
|
||||||
|
rcompare: (left: string, right: string) => number
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entry = {
|
||||||
|
dir: string
|
||||||
|
version: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = process.cwd()
|
||||||
|
const bunRoot = join(root, "node_modules/.bun")
|
||||||
|
const linkRoot = join(bunRoot, "node_modules")
|
||||||
|
const directories = (await readdir(bunRoot)).sort()
|
||||||
|
const versions = new Map<string, Entry[]>()
|
||||||
|
|
||||||
|
for (const entry of directories) {
|
||||||
|
const full = join(bunRoot, entry)
|
||||||
|
const info = await lstat(full)
|
||||||
|
if (!info.isDirectory()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const marker = entry.lastIndexOf("@")
|
||||||
|
if (marker <= 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const slug = entry.slice(0, marker).replace(/\+/g, "/")
|
||||||
|
const version = entry.slice(marker + 1)
|
||||||
|
const list = versions.get(slug) ?? []
|
||||||
|
list.push({ dir: full, version, label: entry })
|
||||||
|
versions.set(slug, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
const semverModule = (await import(join(bunRoot, "node_modules/semver"))) as
|
||||||
|
| SemverLike
|
||||||
|
| {
|
||||||
|
default: SemverLike
|
||||||
|
}
|
||||||
|
const semver = "default" in semverModule ? semverModule.default : semverModule
|
||||||
|
const selections = new Map<string, Entry>()
|
||||||
|
|
||||||
|
for (const [slug, list] of versions) {
|
||||||
|
list.sort((a, b) => {
|
||||||
|
const left = semver.valid(a.version)
|
||||||
|
const right = semver.valid(b.version)
|
||||||
|
if (left && right) {
|
||||||
|
const delta = semver.rcompare(left, right)
|
||||||
|
if (delta !== 0) {
|
||||||
|
return delta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (left && !right) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if (!left && right) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return b.version.localeCompare(a.version)
|
||||||
|
})
|
||||||
|
selections.set(slug, list[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
await rm(linkRoot, { recursive: true, force: true })
|
||||||
|
await mkdir(linkRoot, { recursive: true })
|
||||||
|
|
||||||
|
const rewrites: string[] = []
|
||||||
|
|
||||||
|
for (const [slug, entry] of Array.from(selections.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
|
||||||
|
const parts = slug.split("/")
|
||||||
|
const leaf = parts.pop()
|
||||||
|
if (!leaf) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const parent = join(linkRoot, ...parts)
|
||||||
|
await mkdir(parent, { recursive: true })
|
||||||
|
const linkPath = join(parent, leaf)
|
||||||
|
const desired = join(entry.dir, "node_modules", slug)
|
||||||
|
const relativeTarget = relative(parent, desired)
|
||||||
|
const resolved = relativeTarget.length === 0 ? "." : relativeTarget
|
||||||
|
await rm(linkPath, { recursive: true, force: true })
|
||||||
|
await symlink(resolved, linkPath)
|
||||||
|
rewrites.push(slug + " -> " + resolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrites.sort()
|
||||||
|
console.log("[canonicalize-node-modules] rebuilt", rewrites.length, "links")
|
||||||
|
for (const line of rewrites.slice(0, 20)) {
|
||||||
|
console.log(" ", line)
|
||||||
|
}
|
||||||
|
if (rewrites.length > 20) {
|
||||||
|
console.log(" ...")
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import { lstat, mkdir, readdir, rm, symlink } from "fs/promises"
|
||||||
|
import { join, relative } from "path"
|
||||||
|
|
||||||
|
type PackageManifest = {
|
||||||
|
name?: string
|
||||||
|
bin?: string | Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = process.cwd()
|
||||||
|
const bunRoot = join(root, "node_modules/.bun")
|
||||||
|
const bunEntries = (await safeReadDir(bunRoot)).sort()
|
||||||
|
let rewritten = 0
|
||||||
|
|
||||||
|
for (const entry of bunEntries) {
|
||||||
|
const modulesRoot = join(bunRoot, entry, "node_modules")
|
||||||
|
if (!(await exists(modulesRoot))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const binRoot = join(modulesRoot, ".bin")
|
||||||
|
await rm(binRoot, { recursive: true, force: true })
|
||||||
|
await mkdir(binRoot, { recursive: true })
|
||||||
|
|
||||||
|
const packageDirs = await collectPackages(modulesRoot)
|
||||||
|
for (const packageDir of packageDirs) {
|
||||||
|
const manifest = await readManifest(packageDir)
|
||||||
|
if (!manifest) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const binField = manifest.bin
|
||||||
|
if (!binField) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const seen = new Set<string>()
|
||||||
|
if (typeof binField === "string") {
|
||||||
|
const fallback = manifest.name ?? packageDir.split("/").pop()
|
||||||
|
if (fallback) {
|
||||||
|
await linkBinary(binRoot, fallback, packageDir, binField, seen)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const entries = Object.entries(binField).sort((a, b) => a[0].localeCompare(b[0]))
|
||||||
|
for (const [name, target] of entries) {
|
||||||
|
await linkBinary(binRoot, name, packageDir, target, seen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[normalize-bun-binaries] rewrote ${rewritten} links`)
|
||||||
|
|
||||||
|
async function collectPackages(modulesRoot: string) {
|
||||||
|
const found: string[] = []
|
||||||
|
const topLevel = (await safeReadDir(modulesRoot)).sort()
|
||||||
|
for (const name of topLevel) {
|
||||||
|
if (name === ".bin" || name === ".bun") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const full = join(modulesRoot, name)
|
||||||
|
if (!(await isDirectory(full))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (name.startsWith("@")) {
|
||||||
|
const scoped = (await safeReadDir(full)).sort()
|
||||||
|
for (const child of scoped) {
|
||||||
|
const scopedDir = join(full, child)
|
||||||
|
if (await isDirectory(scopedDir)) {
|
||||||
|
found.push(scopedDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
found.push(full)
|
||||||
|
}
|
||||||
|
return found.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readManifest(dir: string) {
|
||||||
|
const file = Bun.file(join(dir, "package.json"))
|
||||||
|
if (!(await file.exists())) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const data = (await file.json()) as PackageManifest
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
async function linkBinary(binRoot: string, name: string, packageDir: string, target: string, seen: Set<string>) {
|
||||||
|
if (!name || !target) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const normalizedName = normalizeBinName(name)
|
||||||
|
if (seen.has(normalizedName)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const resolved = join(packageDir, target)
|
||||||
|
const script = Bun.file(resolved)
|
||||||
|
if (!(await script.exists())) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen.add(normalizedName)
|
||||||
|
const destination = join(binRoot, normalizedName)
|
||||||
|
const relativeTarget = relative(binRoot, resolved) || "."
|
||||||
|
await rm(destination, { force: true })
|
||||||
|
await symlink(relativeTarget, destination)
|
||||||
|
rewritten++
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exists(path: string) {
|
||||||
|
try {
|
||||||
|
await lstat(path)
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isDirectory(path: string) {
|
||||||
|
try {
|
||||||
|
const info = await lstat(path)
|
||||||
|
return info.isDirectory()
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function safeReadDir(path: string) {
|
||||||
|
try {
|
||||||
|
return await readdir(path)
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeBinName(name: string) {
|
||||||
|
const slash = name.lastIndexOf("/")
|
||||||
|
if (slash >= 0) {
|
||||||
|
return name.slice(slash + 1)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
Executable
+112
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DUMMY="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||||
|
SYSTEM=${SYSTEM:-x86_64-linux}
|
||||||
|
DEFAULT_HASH_FILE=${MODULES_HASH_FILE:-nix/hashes.json}
|
||||||
|
HASH_FILE=${HASH_FILE:-$DEFAULT_HASH_FILE}
|
||||||
|
|
||||||
|
if [ ! -f "$HASH_FILE" ]; then
|
||||||
|
cat >"$HASH_FILE" <<EOF
|
||||||
|
{
|
||||||
|
"nodeModules": "$DUMMY"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
if ! git ls-files --error-unmatch "$HASH_FILE" >/dev/null 2>&1; then
|
||||||
|
git add -N "$HASH_FILE" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
export DUMMY
|
||||||
|
export NIX_KEEP_OUTPUTS=1
|
||||||
|
export NIX_KEEP_DERIVATIONS=1
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "${JSON_OUTPUT:-}" "${BUILD_LOG:-}" "${TMP_EXPR:-}"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
write_node_modules_hash() {
|
||||||
|
local value="$1"
|
||||||
|
local temp
|
||||||
|
temp=$(mktemp)
|
||||||
|
jq --arg value "$value" '.nodeModules = $value' "$HASH_FILE" >"$temp"
|
||||||
|
mv "$temp" "$HASH_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
TARGET="packages.${SYSTEM}.default"
|
||||||
|
MODULES_ATTR=".#packages.${SYSTEM}.default.node_modules"
|
||||||
|
CORRECT_HASH=""
|
||||||
|
|
||||||
|
DRV_PATH="$(nix eval --raw "${MODULES_ATTR}.drvPath")"
|
||||||
|
|
||||||
|
echo "Setting dummy node_modules outputHash for ${SYSTEM}..."
|
||||||
|
write_node_modules_hash "$DUMMY"
|
||||||
|
|
||||||
|
BUILD_LOG=$(mktemp)
|
||||||
|
JSON_OUTPUT=$(mktemp)
|
||||||
|
|
||||||
|
echo "Building node_modules for ${SYSTEM} to discover correct outputHash..."
|
||||||
|
echo "Attempting to realize derivation: ${DRV_PATH}"
|
||||||
|
REALISE_OUT=$(nix-store --realise "$DRV_PATH" --keep-failed 2>&1 | tee "$BUILD_LOG" || true)
|
||||||
|
|
||||||
|
BUILD_PATH=$(echo "$REALISE_OUT" | grep "^/nix/store/" | head -n1 || true)
|
||||||
|
if [ -n "$BUILD_PATH" ] && [ -d "$BUILD_PATH" ]; then
|
||||||
|
echo "Realized node_modules output: $BUILD_PATH"
|
||||||
|
CORRECT_HASH=$(nix hash path --sri "$BUILD_PATH" 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
CORRECT_HASH="$(grep -E 'got:\s+sha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | awk '{print $2}' | head -n1 || true)"
|
||||||
|
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
CORRECT_HASH="$(grep -A2 'hash mismatch' "$BUILD_LOG" | grep 'got:' | awk '{print $2}' | sed 's/sha256:/sha256-/' || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
echo "Searching for kept failed build directory..."
|
||||||
|
KEPT_DIR=$(grep -oE "build directory.*'[^']+'" "$BUILD_LOG" | grep -oE "'/[^']+'" | tr -d "'" | head -n1)
|
||||||
|
|
||||||
|
if [ -z "$KEPT_DIR" ]; then
|
||||||
|
KEPT_DIR=$(grep -oE '/nix/var/nix/builds/[^ ]+' "$BUILD_LOG" | head -n1)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$KEPT_DIR" ] && [ -d "$KEPT_DIR" ]; then
|
||||||
|
echo "Found kept build directory: $KEPT_DIR"
|
||||||
|
if [ -d "$KEPT_DIR/build" ]; then
|
||||||
|
HASH_PATH="$KEPT_DIR/build"
|
||||||
|
else
|
||||||
|
HASH_PATH="$KEPT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Attempting to hash: $HASH_PATH"
|
||||||
|
ls -la "$HASH_PATH" || true
|
||||||
|
|
||||||
|
if [ -d "$HASH_PATH/node_modules" ]; then
|
||||||
|
CORRECT_HASH=$(nix hash path --sri "$HASH_PATH" 2>/dev/null || true)
|
||||||
|
echo "Computed hash from kept build: $CORRECT_HASH"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
echo "Failed to determine correct node_modules hash for ${SYSTEM}."
|
||||||
|
echo "Build log:"
|
||||||
|
cat "$BUILD_LOG"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
write_node_modules_hash "$CORRECT_HASH"
|
||||||
|
|
||||||
|
jq -e --arg hash "$CORRECT_HASH" '.nodeModules == $hash' "$HASH_FILE" >/dev/null
|
||||||
|
|
||||||
|
echo "node_modules hash updated for ${SYSTEM}: $CORRECT_HASH"
|
||||||
|
|
||||||
|
rm -f "$BUILD_LOG"
|
||||||
|
unset BUILD_LOG
|
||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
"description": "AI-powered development tool",
|
"description": "AI-powered development tool",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "bun@1.3.1",
|
"packageManager": "bun@1.3.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
||||||
"typecheck": "bun turbo typecheck",
|
"typecheck": "bun turbo typecheck",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"@solidjs/meta": "0.29.4",
|
"@solidjs/meta": "0.29.4",
|
||||||
"@tailwindcss/vite": "4.1.11",
|
"@tailwindcss/vite": "4.1.11",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
"ai": "5.0.8",
|
"ai": "5.0.97",
|
||||||
"hono": "4.7.10",
|
"hono": "4.7.10",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"luxon": "3.6.1",
|
"luxon": "3.6.1",
|
||||||
|
|||||||
@@ -7,19 +7,20 @@
|
|||||||
"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 bun sst shell --stage=dev bun dev",
|
||||||
"build": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
"build": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||||
"start": "vinxi start",
|
"start": "vinxi start",
|
||||||
"version": "1.0.47"
|
"version": "1.0.85"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
|
"@jsx-email/render": "1.1.1",
|
||||||
|
"@kobalte/core": "catalog:",
|
||||||
|
"@openauthjs/openauth": "catalog:",
|
||||||
"@opencode-ai/console-core": "workspace:*",
|
"@opencode-ai/console-core": "workspace:*",
|
||||||
"@opencode-ai/console-mail": "workspace:*",
|
"@opencode-ai/console-mail": "workspace:*",
|
||||||
"@openauthjs/openauth": "catalog:",
|
|
||||||
"@kobalte/core": "catalog:",
|
|
||||||
"@jsx-email/render": "1.1.1",
|
|
||||||
"@opencode-ai/console-resource": "workspace:*",
|
"@opencode-ai/console-resource": "workspace:*",
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.15.0",
|
"@solidjs/router": "^0.15.0",
|
||||||
"@solidjs/start": "^1.1.0",
|
"@solidjs/start": "^1.1.0",
|
||||||
|
"chart.js": "4.5.1",
|
||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
"vinxi": "^0.5.7",
|
"vinxi": "^0.5.7",
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
|
|||||||
@@ -202,6 +202,14 @@ export function IconZai(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function IconGoogle(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg {...props} viewBox="0 0 50 50" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M49.04,24.001l-1.082-0.043h-0.001C36.134,23.492,26.508,13.866,26.042,2.043L25.999,0.96C25.978,0.424,25.537,0,25,0 s-0.978,0.424-0.999,0.96l-0.043,1.083C23.492,13.866,13.866,23.492,2.042,23.958L0.96,24.001C0.424,24.022,0,24.463,0,25 c0,0.537,0.424,0.978,0.961,0.999l1.082,0.042c11.823,0.467,21.449,10.093,21.915,21.916l0.043,1.083C24.022,49.576,24.463,50,25,50 s0.978-0.424,0.999-0.96l0.043-1.083c0.466-11.823,10.092-21.449,21.915-21.916l1.082-0.042C49.576,25.978,50,25.537,50,25 C50,24.463,49.576,24.022,49.04,24.001z"></path>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function IconStealth(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
export function IconStealth(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 18" fill="none">
|
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 18" fill="none">
|
||||||
@@ -212,3 +220,30 @@ export function IconStealth(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
|||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function IconChevronLeft(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg {...props} viewBox="0 0 20 20" fill="none">
|
||||||
|
<path d="M12 15L7 10L12 5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconChevronRight(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg {...props} viewBox="0 0 20 20" fill="none">
|
||||||
|
<path d="M8 5L13 10L8 15" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconBreakdown(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
|
<path d="M2 12L2 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||||
|
<path d="M6 12L6 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||||
|
<path d="M10 12L10 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||||
|
<path d="M14 12L14 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ export const config = {
|
|||||||
|
|
||||||
// Static stats (used on landing page)
|
// Static stats (used on landing page)
|
||||||
stats: {
|
stats: {
|
||||||
contributors: "250",
|
contributors: "300",
|
||||||
commits: "3,500",
|
commits: "4,000",
|
||||||
monthlyUsers: "300,000",
|
monthlyUsers: "300,000",
|
||||||
},
|
},
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const getUserEmail = query(async (workspaceID: string) => {
|
|||||||
|
|
||||||
export default function WorkspaceLayout(props: RouteSectionProps) {
|
export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const userEmail = createAsync(() => getUserEmail(params.id))
|
const userEmail = createAsync(() => getUserEmail(params.id!))
|
||||||
return (
|
return (
|
||||||
<main data-page="workspace">
|
<main data-page="workspace">
|
||||||
<Link rel="icon" type="image/svg+xml" href="/favicon-zen.svg" />
|
<Link rel="icon" type="image/svg+xml" href="/favicon-zen.svg" />
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import "./[id].css"
|
|||||||
|
|
||||||
export default function WorkspaceLayout(props: RouteSectionProps) {
|
export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const userInfo = createAsync(() => querySessionInfo(params.id))
|
const userInfo = createAsync(() => querySessionInfo(params.id!))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main data-page="workspace">
|
<main data-page="workspace">
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const createSessionUrl = action(async (workspaceID: string, returnUrl: string) =
|
|||||||
export function BillingSection() {
|
export function BillingSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
// ORIGINAL CODE - COMMENTED OUT FOR TESTING
|
// ORIGINAL CODE - COMMENTED OUT FOR TESTING
|
||||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
const billingInfo = createAsync(() => queryBillingInfo(params.id!))
|
||||||
const checkoutAction = useAction(createCheckoutUrl)
|
const checkoutAction = useAction(createCheckoutUrl)
|
||||||
const checkoutSubmission = useSubmission(createCheckoutUrl)
|
const checkoutSubmission = useSubmission(createCheckoutUrl)
|
||||||
const sessionAction = useAction(createSessionUrl)
|
const sessionAction = useAction(createSessionUrl)
|
||||||
@@ -51,7 +51,7 @@ export function BillingSection() {
|
|||||||
const amount = parseInt(store.addBalanceAmount)
|
const amount = parseInt(store.addBalanceAmount)
|
||||||
const baseUrl = window.location.href
|
const baseUrl = window.location.href
|
||||||
|
|
||||||
const checkout = await checkoutAction(params.id, amount, baseUrl, baseUrl)
|
const checkout = await checkoutAction(params.id!, amount, baseUrl, baseUrl)
|
||||||
if (checkout && checkout.data) {
|
if (checkout && checkout.data) {
|
||||||
setStore("checkoutRedirecting", true)
|
setStore("checkoutRedirecting", true)
|
||||||
window.location.href = checkout.data
|
window.location.href = checkout.data
|
||||||
@@ -60,7 +60,7 @@ export function BillingSection() {
|
|||||||
|
|
||||||
async function onClickSession() {
|
async function onClickSession() {
|
||||||
const baseUrl = window.location.href
|
const baseUrl = window.location.href
|
||||||
const sessionUrl = await sessionAction(params.id, baseUrl)
|
const sessionUrl = await sessionAction(params.id!, baseUrl)
|
||||||
if (sessionUrl && sessionUrl.data) {
|
if (sessionUrl && sessionUrl.data) {
|
||||||
setStore("sessionRedirecting", true)
|
setStore("sessionRedirecting", true)
|
||||||
window.location.href = sessionUrl.data
|
window.location.href = sessionUrl.data
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { queryBillingInfo, querySessionInfo } from "../../common"
|
|||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const userInfo = createAsync(() => querySessionInfo(params.id))
|
const userInfo = createAsync(() => querySessionInfo(params.id!))
|
||||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
const billingInfo = createAsync(() => queryBillingInfo(params.id!))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-page="workspace-[id]">
|
<div data-page="workspace-[id]">
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function MonthlyLimitSection() {
|
|||||||
const params = useParams()
|
const params = useParams()
|
||||||
const submission = useSubmission(setMonthlyLimit)
|
const submission = useSubmission(setMonthlyLimit)
|
||||||
const [store, setStore] = createStore({ show: false })
|
const [store, setStore] = createStore({ show: false })
|
||||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
const billingInfo = createAsync(() => queryBillingInfo(params.id!))
|
||||||
|
|
||||||
let input: HTMLInputElement
|
let input: HTMLInputElement
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const downloadReceipt = action(async (workspaceID: string, paymentID: string) =>
|
|||||||
|
|
||||||
export function PaymentSection() {
|
export function PaymentSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const payments = createAsync(() => getPaymentsInfo(params.id))
|
const payments = createAsync(() => getPaymentsInfo(params.id!))
|
||||||
const downloadReceiptAction = useAction(downloadReceipt)
|
const downloadReceiptAction = useAction(downloadReceipt)
|
||||||
|
|
||||||
// DUMMY DATA FOR TESTING
|
// DUMMY DATA FOR TESTING
|
||||||
@@ -89,7 +89,7 @@ export function PaymentSection() {
|
|||||||
<td data-slot="payment-receipt">
|
<td data-slot="payment-receipt">
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const receiptUrl = await downloadReceiptAction(params.id, payment.paymentID!)
|
const receiptUrl = await downloadReceiptAction(params.id!, payment.paymentID!)
|
||||||
if (receiptUrl) {
|
if (receiptUrl) {
|
||||||
window.open(receiptUrl, "_blank")
|
window.open(receiptUrl, "_blank")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const setReload = action(async (form: FormData) => {
|
|||||||
|
|
||||||
export function ReloadSection() {
|
export function ReloadSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
const billingInfo = createAsync(() => queryBillingInfo(params.id!))
|
||||||
const setReloadSubmission = useSubmission(setReload)
|
const setReloadSubmission = useSubmission(setReload)
|
||||||
const reloadSubmission = useSubmission(reload)
|
const reloadSubmission = useSubmission(reload)
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
.root {
|
||||||
|
[data-component="empty-state"] {
|
||||||
|
padding: var(--space-20) var(--space-6);
|
||||||
|
text-align: center;
|
||||||
|
border: 1px dashed var(--color-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
height: 400px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="filter-container"] {
|
||||||
|
margin-bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
|
||||||
|
[data-component="dropdown"] {
|
||||||
|
[data-slot="trigger"] {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
box-shadow: 0 0 0 3px var(--color-accent-alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="chevron"] {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="dropdown"] {
|
||||||
|
min-width: 200px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--space-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="month-picker"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="month-button"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: none;
|
||||||
|
border: none !important;
|
||||||
|
color: var(--color-text);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="month-label"] {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text);
|
||||||
|
line-height: 1.5;
|
||||||
|
min-width: 140px;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="model-item"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
color: var(--color-text);
|
||||||
|
border: none !important;
|
||||||
|
background: none;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="chart-container"] {
|
||||||
|
padding: var(--space-6);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
[data-slot="chart-container"] {
|
||||||
|
height: 300px;
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="empty-state"] {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
import { and, Database, eq, gte, inArray, isNull, lte, or, sql, sum } from "@opencode-ai/console-core/drizzle/index.js"
|
||||||
|
import { UsageTable } from "@opencode-ai/console-core/schema/billing.sql.js"
|
||||||
|
import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js"
|
||||||
|
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||||
|
import { AuthTable } from "@opencode-ai/console-core/schema/auth.sql.js"
|
||||||
|
import { createAsync, query, useParams } from "@solidjs/router"
|
||||||
|
import { createEffect, createMemo, onCleanup, Show, For } from "solid-js"
|
||||||
|
import { createStore } from "solid-js/store"
|
||||||
|
import { withActor } from "~/context/auth.withActor"
|
||||||
|
import { Dropdown } from "~/component/dropdown"
|
||||||
|
import { IconChevronLeft, IconChevronRight } from "~/component/icon"
|
||||||
|
import styles from "./graph-section.module.css"
|
||||||
|
import {
|
||||||
|
Chart,
|
||||||
|
BarController,
|
||||||
|
BarElement,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
type ChartConfiguration,
|
||||||
|
} from "chart.js"
|
||||||
|
|
||||||
|
Chart.register(BarController, BarElement, CategoryScale, LinearScale, Tooltip, Legend)
|
||||||
|
|
||||||
|
async function getCosts(workspaceID: string, year: number, month: number) {
|
||||||
|
"use server"
|
||||||
|
return withActor(async () => {
|
||||||
|
const startDate = new Date(year, month, 1)
|
||||||
|
const endDate = new Date(year, month + 1, 0)
|
||||||
|
|
||||||
|
// First query: get usage data without joining keys
|
||||||
|
const usageData = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
date: sql<string>`DATE(${UsageTable.timeCreated})`,
|
||||||
|
model: UsageTable.model,
|
||||||
|
totalCost: sum(UsageTable.cost),
|
||||||
|
keyId: UsageTable.keyID,
|
||||||
|
})
|
||||||
|
.from(UsageTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(UsageTable.workspaceID, workspaceID),
|
||||||
|
gte(UsageTable.timeCreated, startDate),
|
||||||
|
lte(UsageTable.timeCreated, endDate),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.groupBy(sql`DATE(${UsageTable.timeCreated})`, UsageTable.model, UsageTable.keyID)
|
||||||
|
.then((x) =>
|
||||||
|
x.map((r) => ({
|
||||||
|
...r,
|
||||||
|
totalCost: r.totalCost ? parseInt(r.totalCost) : 0,
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get unique key IDs from usage
|
||||||
|
const usageKeyIds = new Set(usageData.map((r) => r.keyId).filter((id) => id !== null))
|
||||||
|
|
||||||
|
// Second query: get all existing keys plus any keys from usage
|
||||||
|
const keysData = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
keyId: KeyTable.id,
|
||||||
|
keyName: KeyTable.name,
|
||||||
|
userEmail: AuthTable.subject,
|
||||||
|
timeDeleted: KeyTable.timeDeleted,
|
||||||
|
})
|
||||||
|
.from(KeyTable)
|
||||||
|
.innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)))
|
||||||
|
.innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")))
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(KeyTable.workspaceID, workspaceID),
|
||||||
|
usageKeyIds.size > 0
|
||||||
|
? or(inArray(KeyTable.id, Array.from(usageKeyIds)), isNull(KeyTable.timeDeleted))
|
||||||
|
: isNull(KeyTable.timeDeleted),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.orderBy(AuthTable.subject, KeyTable.name),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
usage: usageData,
|
||||||
|
keys: keysData.map((key) => ({
|
||||||
|
id: key.keyId,
|
||||||
|
displayName:
|
||||||
|
key.timeDeleted !== null
|
||||||
|
? `${key.userEmail} - ${key.keyName} (deleted)`
|
||||||
|
: `${key.userEmail} - ${key.keyName}`,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}, workspaceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryCosts = query(getCosts, "costs.get")
|
||||||
|
|
||||||
|
const MODEL_COLORS: Record<string, string> = {
|
||||||
|
"claude-sonnet-4-5": "#D4745C",
|
||||||
|
"claude-sonnet-4": "#E8B4A4",
|
||||||
|
"claude-opus-4": "#C8A098",
|
||||||
|
"claude-haiku-4-5": "#F0D8D0",
|
||||||
|
"claude-3-5-haiku": "#F8E8E0",
|
||||||
|
"gpt-5.1": "#4A90E2",
|
||||||
|
"gpt-5.1-codex": "#6BA8F0",
|
||||||
|
"gpt-5": "#7DB8F8",
|
||||||
|
"gpt-5-codex": "#9FCAFF",
|
||||||
|
"gpt-5-nano": "#B8D8FF",
|
||||||
|
"grok-code": "#8B5CF6",
|
||||||
|
"big-pickle": "#10B981",
|
||||||
|
"kimi-k2": "#F59E0B",
|
||||||
|
"qwen3-coder": "#EC4899",
|
||||||
|
"glm-4.6": "#14B8A6",
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModelColor(model: string): string {
|
||||||
|
if (MODEL_COLORS[model]) return MODEL_COLORS[model]
|
||||||
|
|
||||||
|
const hash = model.split("").reduce((acc, char) => char.charCodeAt(0) + ((acc << 5) - acc), 0)
|
||||||
|
const hue = Math.abs(hash) % 360
|
||||||
|
return `hsl(${hue}, 50%, 65%)`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateLabel(dateStr: string): string {
|
||||||
|
const date = new Date()
|
||||||
|
const [y, m, d] = dateStr.split("-").map(Number)
|
||||||
|
date.setFullYear(y)
|
||||||
|
date.setMonth(m - 1)
|
||||||
|
date.setDate(d)
|
||||||
|
date.setHours(0, 0, 0, 0)
|
||||||
|
const month = date.toLocaleDateString("en-US", { month: "short" })
|
||||||
|
const day = date.getUTCDate().toString().padStart(2, "0")
|
||||||
|
return `${month} ${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOpacityToColor(color: string, opacity: number): string {
|
||||||
|
if (color.startsWith("#")) {
|
||||||
|
const r = parseInt(color.slice(1, 3), 16)
|
||||||
|
const g = parseInt(color.slice(3, 5), 16)
|
||||||
|
const b = parseInt(color.slice(5, 7), 16)
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${opacity})`
|
||||||
|
}
|
||||||
|
if (color.startsWith("hsl")) return color.replace(")", `, ${opacity})`).replace("hsl", "hsla")
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GraphSection() {
|
||||||
|
let canvasRef: HTMLCanvasElement | undefined
|
||||||
|
let chartInstance: Chart | undefined
|
||||||
|
const params = useParams()
|
||||||
|
const now = new Date()
|
||||||
|
const [store, setStore] = createStore({
|
||||||
|
data: null as Awaited<ReturnType<typeof getCosts>> | null,
|
||||||
|
year: now.getFullYear(),
|
||||||
|
month: now.getMonth(),
|
||||||
|
key: null as string | null,
|
||||||
|
model: null as string | null,
|
||||||
|
modelDropdownOpen: false,
|
||||||
|
keyDropdownOpen: false,
|
||||||
|
})
|
||||||
|
const initialData = createAsync(() => queryCosts(params.id!, store.year, store.month))
|
||||||
|
|
||||||
|
const onPreviousMonth = async () => {
|
||||||
|
const month = store.month === 0 ? 11 : store.month - 1
|
||||||
|
const year = store.month === 0 ? store.year - 1 : store.year
|
||||||
|
const data = await getCosts(params.id!, year, month)
|
||||||
|
setStore({ month, year, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onNextMonth = async () => {
|
||||||
|
const month = store.month === 11 ? 0 : store.month + 1
|
||||||
|
const year = store.month === 11 ? store.year + 1 : store.year
|
||||||
|
setStore({ month, year, data: await getCosts(params.id!, year, month) })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSelectModel = (model: string | null) => setStore({ model, modelDropdownOpen: false })
|
||||||
|
|
||||||
|
const onSelectKey = (keyID: string | null) => setStore({ key: keyID, keyDropdownOpen: false })
|
||||||
|
|
||||||
|
const getData = createMemo(() => store.data ?? initialData())
|
||||||
|
|
||||||
|
const getModels = createMemo(() => {
|
||||||
|
const data = getData()
|
||||||
|
if (!data?.usage) return []
|
||||||
|
return Array.from(new Set(data.usage.map((row) => row.model))).sort()
|
||||||
|
})
|
||||||
|
|
||||||
|
const getDates = createMemo(() => {
|
||||||
|
const daysInMonth = new Date(store.year, store.month + 1, 0).getDate()
|
||||||
|
return Array.from({ length: daysInMonth }, (_, i) => {
|
||||||
|
const date = new Date(store.year, store.month, i + 1)
|
||||||
|
return date.toISOString().split("T")[0]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const getKeyName = (keyID: string | null): string => {
|
||||||
|
if (!keyID || !store.data?.keys) return "All Keys"
|
||||||
|
const found = store.data.keys.find((k) => k.id === keyID)
|
||||||
|
return found?.displayName ?? "All Keys"
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatMonthYear = () =>
|
||||||
|
new Date(store.year, store.month, 1).toLocaleDateString("en-US", { month: "long", year: "numeric" })
|
||||||
|
|
||||||
|
const isCurrentMonth = () => store.year === now.getFullYear() && store.month === now.getMonth()
|
||||||
|
|
||||||
|
const chartConfig = createMemo((): ChartConfiguration | null => {
|
||||||
|
const data = getData()
|
||||||
|
const dates = getDates()
|
||||||
|
if (!data?.usage?.length) return null
|
||||||
|
|
||||||
|
const dailyData = new Map<string, Map<string, number>>()
|
||||||
|
for (const dateKey of dates) dailyData.set(dateKey, new Map())
|
||||||
|
|
||||||
|
data.usage
|
||||||
|
.filter((row) => (store.key ? row.keyId === store.key : true))
|
||||||
|
.forEach((row) => {
|
||||||
|
const dayMap = dailyData.get(row.date)
|
||||||
|
if (!dayMap) return
|
||||||
|
dayMap.set(row.model, (dayMap.get(row.model) ?? 0) + row.totalCost)
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredModels = store.model === null ? getModels() : [store.model]
|
||||||
|
|
||||||
|
const datasets = filteredModels.map((model) => {
|
||||||
|
const color = getModelColor(model)
|
||||||
|
return {
|
||||||
|
label: model,
|
||||||
|
data: dates.map((date) => (dailyData.get(date)?.get(model) || 0) / 100_000_000),
|
||||||
|
backgroundColor: color,
|
||||||
|
hoverBackgroundColor: color,
|
||||||
|
borderWidth: 0,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "bar",
|
||||||
|
data: {
|
||||||
|
labels: dates.map(formatDateLabel),
|
||||||
|
datasets,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
grid: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
maxRotation: 0,
|
||||||
|
autoSkipPadding: 20,
|
||||||
|
color: "rgba(255, 255, 255, 0.5)",
|
||||||
|
font: {
|
||||||
|
family: "monospace",
|
||||||
|
size: 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true,
|
||||||
|
beginAtZero: true,
|
||||||
|
grid: {
|
||||||
|
color: "rgba(255, 255, 255, 0.1)",
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: "rgba(255, 255, 255, 0.5)",
|
||||||
|
font: {
|
||||||
|
family: "monospace",
|
||||||
|
size: 11,
|
||||||
|
},
|
||||||
|
callback: (value) => {
|
||||||
|
const num = Number(value)
|
||||||
|
return num >= 1000 ? `$${(num / 1000).toFixed(1)}k` : `$${num.toFixed(0)}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
tooltip: {
|
||||||
|
mode: "index",
|
||||||
|
intersect: false,
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.9)",
|
||||||
|
titleColor: "rgba(255, 255, 255, 0.9)",
|
||||||
|
bodyColor: "rgba(255, 255, 255, 0.8)",
|
||||||
|
borderColor: "rgba(255, 255, 255, 0.1)",
|
||||||
|
borderWidth: 1,
|
||||||
|
padding: 12,
|
||||||
|
displayColors: true,
|
||||||
|
callbacks: {
|
||||||
|
label: (context) => {
|
||||||
|
const value = context.parsed.y
|
||||||
|
if (!value || value === 0) return
|
||||||
|
return `${context.dataset.label}: $${value.toFixed(2)}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: "bottom",
|
||||||
|
labels: {
|
||||||
|
color: "rgba(255, 255, 255, 0.7)",
|
||||||
|
font: {
|
||||||
|
size: 12,
|
||||||
|
},
|
||||||
|
padding: 16,
|
||||||
|
boxWidth: 16,
|
||||||
|
boxHeight: 16,
|
||||||
|
usePointStyle: false,
|
||||||
|
},
|
||||||
|
onHover: (event, legendItem, legend) => {
|
||||||
|
const chart = legend.chart
|
||||||
|
chart.data.datasets?.forEach((dataset, i) => {
|
||||||
|
const meta = chart.getDatasetMeta(i)
|
||||||
|
const baseColor = getModelColor(dataset.label || "")
|
||||||
|
const color = i === legendItem.datasetIndex ? baseColor : addOpacityToColor(baseColor, 0.3)
|
||||||
|
meta.data.forEach((bar: any) => {
|
||||||
|
bar.options.backgroundColor = color
|
||||||
|
})
|
||||||
|
})
|
||||||
|
chart.update("none")
|
||||||
|
},
|
||||||
|
onLeave: (event, legendItem, legend) => {
|
||||||
|
const chart = legend.chart
|
||||||
|
chart.data.datasets?.forEach((dataset, i) => {
|
||||||
|
const meta = chart.getDatasetMeta(i)
|
||||||
|
const baseColor = getModelColor(dataset.label || "")
|
||||||
|
meta.data.forEach((bar: any) => {
|
||||||
|
bar.options.backgroundColor = baseColor
|
||||||
|
})
|
||||||
|
})
|
||||||
|
chart.update("none")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const config = chartConfig()
|
||||||
|
if (!config || !canvasRef) return
|
||||||
|
|
||||||
|
if (chartInstance) chartInstance.destroy()
|
||||||
|
chartInstance = new Chart(canvasRef, config)
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => chartInstance?.destroy())
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section class={styles.root}>
|
||||||
|
<div data-slot="section-title">
|
||||||
|
<h2>Cost</h2>
|
||||||
|
<p>Usage costs broken down by model.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={getData()}>
|
||||||
|
<div data-slot="filter-container">
|
||||||
|
<div data-slot="month-picker">
|
||||||
|
<button data-slot="month-button" onClick={onPreviousMonth}>
|
||||||
|
<IconChevronLeft />
|
||||||
|
</button>
|
||||||
|
<span data-slot="month-label">{formatMonthYear()}</span>
|
||||||
|
<button data-slot="month-button" onClick={onNextMonth} disabled={isCurrentMonth()}>
|
||||||
|
<IconChevronRight />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<Dropdown
|
||||||
|
trigger={store.model === null ? "All Models" : store.model}
|
||||||
|
open={store.modelDropdownOpen}
|
||||||
|
onOpenChange={(open) => setStore({ modelDropdownOpen: open })}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<button data-slot="model-item" onClick={() => onSelectModel(null)}>
|
||||||
|
<span>All Models</span>
|
||||||
|
</button>
|
||||||
|
<For each={getModels()}>
|
||||||
|
{(model) => (
|
||||||
|
<button data-slot="model-item" onClick={() => onSelectModel(model)}>
|
||||||
|
<span>{model}</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</>
|
||||||
|
</Dropdown>
|
||||||
|
<Dropdown
|
||||||
|
trigger={getKeyName(store.key)}
|
||||||
|
open={store.keyDropdownOpen}
|
||||||
|
onOpenChange={(open) => setStore({ keyDropdownOpen: open })}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<button data-slot="model-item" onClick={() => onSelectKey(null)}>
|
||||||
|
<span>All Keys</span>
|
||||||
|
</button>
|
||||||
|
<For each={getData()?.keys || []}>
|
||||||
|
{(key) => (
|
||||||
|
<button data-slot="model-item" onClick={() => onSelectKey(key.id)}>
|
||||||
|
<span>{key.displayName}</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show
|
||||||
|
when={chartConfig()}
|
||||||
|
fallback={
|
||||||
|
<div data-component="empty-state">
|
||||||
|
<p>No usage data available for the selected period.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div data-slot="chart-container">
|
||||||
|
<canvas ref={canvasRef} />
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,13 +5,14 @@ import { NewUserSection } from "./new-user-section"
|
|||||||
import { UsageSection } from "./usage-section"
|
import { UsageSection } from "./usage-section"
|
||||||
import { ModelSection } from "./model-section"
|
import { ModelSection } from "./model-section"
|
||||||
import { ProviderSection } from "./provider-section"
|
import { ProviderSection } from "./provider-section"
|
||||||
|
import { GraphSection } from "./graph-section"
|
||||||
import { IconLogo } from "~/component/icon"
|
import { IconLogo } from "~/component/icon"
|
||||||
import { querySessionInfo, queryBillingInfo, createCheckoutUrl, formatBalance } from "../common"
|
import { querySessionInfo, queryBillingInfo, createCheckoutUrl, formatBalance } from "../common"
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const userInfo = createAsync(() => querySessionInfo(params.id))
|
const userInfo = createAsync(() => querySessionInfo(params.id!))
|
||||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
const billingInfo = createAsync(() => queryBillingInfo(params.id!))
|
||||||
const checkoutAction = useAction(createCheckoutUrl)
|
const checkoutAction = useAction(createCheckoutUrl)
|
||||||
const checkoutSubmission = useSubmission(createCheckoutUrl)
|
const checkoutSubmission = useSubmission(createCheckoutUrl)
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
@@ -21,7 +22,7 @@ export default function () {
|
|||||||
|
|
||||||
async function onClickCheckout() {
|
async function onClickCheckout() {
|
||||||
const baseUrl = window.location.href
|
const baseUrl = window.location.href
|
||||||
const checkout = await checkoutAction(params.id, billingInfo()!.reloadAmount, baseUrl, baseUrl)
|
const checkout = await checkoutAction(params.id!, billingInfo()!.reloadAmount, baseUrl, baseUrl)
|
||||||
if (checkout && checkout.data) {
|
if (checkout && checkout.data) {
|
||||||
setStore("checkoutRedirecting", true)
|
setStore("checkoutRedirecting", true)
|
||||||
window.location.href = checkout.data
|
window.location.href = checkout.data
|
||||||
@@ -66,6 +67,9 @@ export default function () {
|
|||||||
|
|
||||||
<div data-slot="sections">
|
<div data-slot="sections">
|
||||||
<NewUserSection />
|
<NewUserSection />
|
||||||
|
<Show when={userInfo()?.isAdmin}>
|
||||||
|
<GraphSection />
|
||||||
|
</Show>
|
||||||
<ModelSection />
|
<ModelSection />
|
||||||
<Show when={userInfo()?.isAdmin}>
|
<Show when={userInfo()?.isAdmin}>
|
||||||
<ProviderSection />
|
<ProviderSection />
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const listKeys = query(async (workspaceID: string) => {
|
|||||||
|
|
||||||
export function KeySection() {
|
export function KeySection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const keys = createAsync(() => listKeys(params.id))
|
const keys = createAsync(() => listKeys(params.id!))
|
||||||
const submission = useSubmission(createKey)
|
const submission = useSubmission(createKey)
|
||||||
const [store, setStore] = createStore({ show: false })
|
const [store, setStore] = createStore({ show: false })
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ const roleOptions = [
|
|||||||
|
|
||||||
export function MemberSection() {
|
export function MemberSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const data = createAsync(() => listMembers(params.id))
|
const data = createAsync(() => listMembers(params.id!))
|
||||||
const submission = useSubmission(inviteMember)
|
const submission = useSubmission(inviteMember)
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
show: false,
|
show: false,
|
||||||
@@ -328,7 +328,7 @@ export function MemberSection() {
|
|||||||
{(member) => (
|
{(member) => (
|
||||||
<MemberRow
|
<MemberRow
|
||||||
member={member}
|
member={member}
|
||||||
workspaceID={params.id}
|
workspaceID={params.id!}
|
||||||
actorID={data()!.actorID}
|
actorID={data()!.actorID}
|
||||||
actorRole={data()!.actorRole}
|
actorRole={data()!.actorRole}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,11 +5,21 @@ import { withActor } from "~/context/auth.withActor"
|
|||||||
import { ZenData } from "@opencode-ai/console-core/model.js"
|
import { ZenData } from "@opencode-ai/console-core/model.js"
|
||||||
import styles from "./model-section.module.css"
|
import styles from "./model-section.module.css"
|
||||||
import { querySessionInfo } from "../common"
|
import { querySessionInfo } from "../common"
|
||||||
import { IconAlibaba, IconAnthropic, IconMoonshotAI, IconOpenAI, IconStealth, IconXai, IconZai } from "~/component/icon"
|
import {
|
||||||
|
IconAlibaba,
|
||||||
|
IconAnthropic,
|
||||||
|
IconGoogle,
|
||||||
|
IconMoonshotAI,
|
||||||
|
IconOpenAI,
|
||||||
|
IconStealth,
|
||||||
|
IconXai,
|
||||||
|
IconZai,
|
||||||
|
} from "~/component/icon"
|
||||||
|
|
||||||
const getModelLab = (modelId: string) => {
|
const getModelLab = (modelId: string) => {
|
||||||
if (modelId.startsWith("claude")) return "Anthropic"
|
if (modelId.startsWith("claude")) return "Anthropic"
|
||||||
if (modelId.startsWith("gpt")) return "OpenAI"
|
if (modelId.startsWith("gpt")) return "OpenAI"
|
||||||
|
if (modelId.startsWith("gemini")) return "Google"
|
||||||
if (modelId.startsWith("kimi")) return "Moonshot AI"
|
if (modelId.startsWith("kimi")) return "Moonshot AI"
|
||||||
if (modelId.startsWith("glm")) return "Z.ai"
|
if (modelId.startsWith("glm")) return "Z.ai"
|
||||||
if (modelId.startsWith("qwen")) return "Alibaba"
|
if (modelId.startsWith("qwen")) return "Alibaba"
|
||||||
@@ -22,9 +32,19 @@ const getModelsInfo = query(async (workspaceID: string) => {
|
|||||||
return withActor(async () => {
|
return withActor(async () => {
|
||||||
return {
|
return {
|
||||||
all: Object.entries(ZenData.list().models)
|
all: Object.entries(ZenData.list().models)
|
||||||
.filter(([id, _model]) => !["claude-3-5-haiku", "minimax-m2"].includes(id))
|
.filter(([id, _model]) => !["claude-3-5-haiku"].includes(id))
|
||||||
.filter(([id, _model]) => !id.startsWith("an-"))
|
.filter(([id, _model]) => !id.startsWith("alpha-"))
|
||||||
.sort(([_idA, modelA], [_idB, modelB]) => modelA.name.localeCompare(modelB.name))
|
.sort(([idA, modelA], [idB, modelB]) => {
|
||||||
|
const priority = ["big-pickle", "grok", "claude", "gpt", "gemini"]
|
||||||
|
const getPriority = (id: string) => {
|
||||||
|
const index = priority.findIndex((p) => id.startsWith(p))
|
||||||
|
return index === -1 ? Infinity : index
|
||||||
|
}
|
||||||
|
const pA = getPriority(idA)
|
||||||
|
const pB = getPriority(idB)
|
||||||
|
if (pA !== pB) return pA - pB
|
||||||
|
return modelA.name.localeCompare(modelB.name)
|
||||||
|
})
|
||||||
.map(([id, model]) => ({ id, name: model.name })),
|
.map(([id, model]) => ({ id, name: model.name })),
|
||||||
disabled: await Model.listDisabled(),
|
disabled: await Model.listDisabled(),
|
||||||
}
|
}
|
||||||
@@ -52,8 +72,8 @@ const updateModel = action(async (form: FormData) => {
|
|||||||
|
|
||||||
export function ModelSection() {
|
export function ModelSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const modelsInfo = createAsync(() => getModelsInfo(params.id))
|
const modelsInfo = createAsync(() => getModelsInfo(params.id!))
|
||||||
const userInfo = createAsync(() => querySessionInfo(params.id))
|
const userInfo = createAsync(() => querySessionInfo(params.id!))
|
||||||
|
|
||||||
const modelsWithLab = createMemo(() => {
|
const modelsWithLab = createMemo(() => {
|
||||||
const info = modelsInfo()
|
const info = modelsInfo()
|
||||||
@@ -96,6 +116,8 @@ export function ModelSection() {
|
|||||||
return <IconOpenAI width={16} height={16} />
|
return <IconOpenAI width={16} height={16} />
|
||||||
case "Anthropic":
|
case "Anthropic":
|
||||||
return <IconAnthropic width={16} height={16} />
|
return <IconAnthropic width={16} height={16} />
|
||||||
|
case "Google":
|
||||||
|
return <IconGoogle width={16} height={16} />
|
||||||
case "Moonshot AI":
|
case "Moonshot AI":
|
||||||
return <IconMoonshotAI width={16} height={16} />
|
return <IconMoonshotAI width={16} height={16} />
|
||||||
case "Z.ai":
|
case "Z.ai":
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ const listKeys = query(async (workspaceID: string) => {
|
|||||||
export function NewUserSection() {
|
export function NewUserSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const [copiedKey, setCopiedKey] = createSignal(false)
|
const [copiedKey, setCopiedKey] = createSignal(false)
|
||||||
const keys = createAsync(() => listKeys(params.id))
|
const keys = createAsync(() => listKeys(params.id!))
|
||||||
const usage = createAsync(() => getUsageInfo(params.id))
|
const usage = createAsync(() => getUsageInfo(params.id!))
|
||||||
const isNew = createMemo(() => {
|
const isNew = createMemo(() => {
|
||||||
const keysList = keys()
|
const keysList = keys()
|
||||||
const usageList = usage()
|
const usageList = usage()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const listProviders = query(async (workspaceID: string) => {
|
|||||||
|
|
||||||
function ProviderRow(props: { provider: Provider }) {
|
function ProviderRow(props: { provider: Provider }) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const providers = createAsync(() => listProviders(params.id))
|
const providers = createAsync(() => listProviders(params.id!))
|
||||||
const saveSubmission = useSubmission(saveProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key)
|
const saveSubmission = useSubmission(saveProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key)
|
||||||
const removeSubmission = useSubmission(
|
const removeSubmission = useSubmission(
|
||||||
removeProvider,
|
removeProvider,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const updateWorkspace = action(async (form: FormData) => {
|
|||||||
|
|
||||||
export function SettingsSection() {
|
export function SettingsSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id))
|
const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id!))
|
||||||
const submission = useSubmission(updateWorkspace)
|
const submission = useSubmission(updateWorkspace)
|
||||||
const [store, setStore] = createStore({ show: false })
|
const [store, setStore] = createStore({ show: false })
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
.root {
|
.root {
|
||||||
|
/* Empty state */
|
||||||
[data-component="empty-state"] {
|
[data-component="empty-state"] {
|
||||||
padding: var(--space-20) var(--space-6);
|
padding: var(--space-20) var(--space-6);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: 1px dashed var(--color-border);
|
border: 1px dashed var(--color-border);
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var(--border-radius-sm);
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-2);
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
line-height: 1.5;
|
|
||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Table container */
|
||||||
[data-slot="usage-table"] {
|
[data-slot="usage-table"] {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Table element */
|
||||||
[data-slot="usage-table-element"] {
|
[data-slot="usage-table-element"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
|
|
||||||
&[data-slot="usage-model"] {
|
&[data-slot="usage-model"] {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-weight: 400;
|
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
@@ -56,33 +54,133 @@
|
|||||||
|
|
||||||
&[data-slot="usage-cost"] {
|
&[data-slot="usage-cost"] {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="tokens-with-breakdown"] {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="breakdown-button"] {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.15s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="breakdown-popup"] {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
background: var(--color-bg);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
padding: var(--space-2);
|
||||||
|
z-index: 10;
|
||||||
|
min-width: 180px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr {
|
tbody tr:last-child td {
|
||||||
&:last-child td {
|
border-bottom: none;
|
||||||
border-bottom: none;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pagination */
|
||||||
|
[data-slot="pagination"] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-4) 0;
|
||||||
|
border-top: 1px solid var(--color-border-muted);
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: var(--space-2) var(--space-4);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background: var(--color-bg-tertiary);
|
||||||
|
border-color: var(--color-border-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 40rem) {
|
/* Mobile responsive */
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
[data-slot="usage-table-element"] {
|
||||||
th,
|
th,
|
||||||
td {
|
td {
|
||||||
padding: var(--space-2) var(--space-3);
|
padding: var(--space-2) var(--space-3);
|
||||||
font-size: var(--font-size-xs);
|
font-size: var(--font-size-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
/* Hide Model column on mobile */
|
||||||
&:nth-child(2) /* Model */ {
|
th:nth-child(2),
|
||||||
display: none;
|
td:nth-child(2) {
|
||||||
}
|
display: none;
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
&:nth-child(2) /* Model */ {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Breakdown popup content */
|
||||||
|
[data-slot="breakdown-row"] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-4);
|
||||||
|
padding: var(--space-1) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="breakdown-label"] {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="breakdown-value"] {
|
||||||
|
color: var(--color-text);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +1,69 @@
|
|||||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||||
import { query, useParams, createAsync } from "@solidjs/router"
|
import { createAsync, query, useParams } from "@solidjs/router"
|
||||||
import { createMemo, For, Show } from "solid-js"
|
import { createMemo, For, Show, createEffect, createSignal } from "solid-js"
|
||||||
import { formatDateUTC, formatDateForTable } from "../common"
|
import { formatDateUTC, formatDateForTable } from "../common"
|
||||||
import { withActor } from "~/context/auth.withActor"
|
import { withActor } from "~/context/auth.withActor"
|
||||||
|
import { IconChevronLeft, IconChevronRight, IconBreakdown } from "~/component/icon"
|
||||||
import styles from "./usage-section.module.css"
|
import styles from "./usage-section.module.css"
|
||||||
|
import { createStore } from "solid-js/store"
|
||||||
|
|
||||||
const getUsageInfo = query(async (workspaceID: string) => {
|
const PAGE_SIZE = 50
|
||||||
|
|
||||||
|
async function getUsageInfo(workspaceID: string, page: number) {
|
||||||
"use server"
|
"use server"
|
||||||
return withActor(async () => {
|
return withActor(async () => {
|
||||||
return await Billing.usages()
|
return await Billing.usages(page, PAGE_SIZE)
|
||||||
}, workspaceID)
|
}, workspaceID)
|
||||||
}, "usage.list")
|
}
|
||||||
|
|
||||||
|
const queryUsageInfo = query(getUsageInfo, "usage.list")
|
||||||
|
|
||||||
export function UsageSection() {
|
export function UsageSection() {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
// ORIGINAL CODE - COMMENTED OUT FOR TESTING
|
const usage = createAsync(() => queryUsageInfo(params.id!, 0))
|
||||||
const usage = createAsync(() => getUsageInfo(params.id))
|
const [store, setStore] = createStore({ page: 0, usage: [] as Awaited<ReturnType<typeof getUsageInfo>> })
|
||||||
|
const [openBreakdownId, setOpenBreakdownId] = createSignal<string | null>(null)
|
||||||
|
|
||||||
// DUMMY DATA FOR TESTING
|
createEffect(() => {
|
||||||
// const usage = () => [
|
setStore({ usage: usage() })
|
||||||
// {
|
}, [usage])
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 0).toISOString(), // Today
|
|
||||||
// model: "claude-3-5-sonnet-20241022",
|
createEffect(() => {
|
||||||
// inputTokens: 1247,
|
if (!openBreakdownId()) return
|
||||||
// outputTokens: 423,
|
|
||||||
// cost: 125400000, // $1.254
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
// },
|
const target = e.target as HTMLElement
|
||||||
// {
|
if (!target.closest('[data-slot="tokens-with-breakdown"]')) {
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 0.5).toISOString(), // 12 hours ago
|
setOpenBreakdownId(null)
|
||||||
// model: "claude-3-haiku-20240307",
|
}
|
||||||
// inputTokens: 892,
|
}
|
||||||
// outputTokens: 156,
|
|
||||||
// cost: 23500000, // $0.235
|
document.addEventListener("click", handleClickOutside)
|
||||||
// },
|
return () => document.removeEventListener("click", handleClickOutside)
|
||||||
// {
|
})
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 1).toISOString(), // Yesterday
|
|
||||||
// model: "claude-3-5-sonnet-20241022",
|
const hasResults = createMemo(() => store.usage && store.usage.length > 0)
|
||||||
// inputTokens: 2134,
|
const canGoPrev = createMemo(() => store.page > 0)
|
||||||
// outputTokens: 687,
|
const canGoNext = createMemo(() => store.usage && store.usage.length === PAGE_SIZE)
|
||||||
// cost: 234700000, // $2.347
|
|
||||||
// },
|
const calculateTotalInputTokens = (u: Awaited<ReturnType<typeof getUsageInfo>>[0]) => {
|
||||||
// {
|
return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWrite5mTokens ?? 0) + (u.cacheWrite1hTokens ?? 0)
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 1.3).toISOString(), // 1.3 days ago
|
}
|
||||||
// model: "gpt-4o-mini",
|
|
||||||
// inputTokens: 567,
|
const goPrev = async () => {
|
||||||
// outputTokens: 234,
|
const usage = await getUsageInfo(params.id!, store.page - 1)
|
||||||
// cost: 8900000, // $0.089
|
setStore({
|
||||||
// },
|
page: store.page - 1,
|
||||||
// {
|
usage,
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 2).toISOString(), // 2 days ago
|
})
|
||||||
// model: "claude-3-opus-20240229",
|
}
|
||||||
// inputTokens: 1893,
|
const goNext = async () => {
|
||||||
// outputTokens: 945,
|
const usage = await getUsageInfo(params.id!, store.page + 1)
|
||||||
// cost: 445600000, // $4.456
|
setStore({
|
||||||
// },
|
page: store.page + 1,
|
||||||
// {
|
usage,
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 2.7).toISOString(), // 2.7 days ago
|
})
|
||||||
// model: "gpt-4o",
|
}
|
||||||
// inputTokens: 1456,
|
|
||||||
// outputTokens: 532,
|
|
||||||
// cost: 156800000, // $1.568
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 3).toISOString(), // 3 days ago
|
|
||||||
// model: "claude-3-haiku-20240307",
|
|
||||||
// inputTokens: 634,
|
|
||||||
// outputTokens: 89,
|
|
||||||
// cost: 12300000, // $0.123
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// timeCreated: new Date(Date.now() - 86400000 * 4).toISOString(), // 4 days ago
|
|
||||||
// model: "claude-3-5-sonnet-20241022",
|
|
||||||
// inputTokens: 3245,
|
|
||||||
// outputTokens: 1123,
|
|
||||||
// cost: 387200000, // $3.872
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section class={styles.root}>
|
<section class={styles.root}>
|
||||||
@@ -85,7 +73,7 @@ export function UsageSection() {
|
|||||||
</div>
|
</div>
|
||||||
<div data-slot="usage-table">
|
<div data-slot="usage-table">
|
||||||
<Show
|
<Show
|
||||||
when={usage() && usage()!.length > 0}
|
when={hasResults()}
|
||||||
fallback={
|
fallback={
|
||||||
<div data-component="empty-state">
|
<div data-component="empty-state">
|
||||||
<p>Make your first API call to get started.</p>
|
<p>Make your first API call to get started.</p>
|
||||||
@@ -103,16 +91,51 @@ export function UsageSection() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<For each={usage()!}>
|
<For each={store.usage}>
|
||||||
{(usage) => {
|
{(usage, index) => {
|
||||||
const date = createMemo(() => new Date(usage.timeCreated))
|
const date = createMemo(() => new Date(usage.timeCreated))
|
||||||
|
const totalInputTokens = createMemo(() => calculateTotalInputTokens(usage))
|
||||||
|
const breakdownId = `breakdown-${index()}`
|
||||||
|
const isOpen = createMemo(() => openBreakdownId() === breakdownId)
|
||||||
|
const isClaude = usage.model.toLowerCase().includes("claude")
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td data-slot="usage-date" title={formatDateUTC(date())}>
|
<td data-slot="usage-date" title={formatDateUTC(date())}>
|
||||||
{formatDateForTable(date())}
|
{formatDateForTable(date())}
|
||||||
</td>
|
</td>
|
||||||
<td data-slot="usage-model">{usage.model}</td>
|
<td data-slot="usage-model">{usage.model}</td>
|
||||||
<td data-slot="usage-tokens">{usage.inputTokens}</td>
|
<td data-slot="usage-tokens">
|
||||||
|
<div data-slot="tokens-with-breakdown" onClick={(e) => e.stopPropagation()}>
|
||||||
|
<button
|
||||||
|
data-slot="breakdown-button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setOpenBreakdownId(isOpen() ? null : breakdownId)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconBreakdown />
|
||||||
|
</button>
|
||||||
|
<span onClick={() => setOpenBreakdownId(null)}>{totalInputTokens()}</span>
|
||||||
|
<Show when={isOpen()}>
|
||||||
|
<div data-slot="breakdown-popup" onClick={(e) => e.stopPropagation()}>
|
||||||
|
<div data-slot="breakdown-row">
|
||||||
|
<span data-slot="breakdown-label">Input</span>
|
||||||
|
<span data-slot="breakdown-value">{usage.inputTokens}</span>
|
||||||
|
</div>
|
||||||
|
<div data-slot="breakdown-row">
|
||||||
|
<span data-slot="breakdown-label">Cache Read</span>
|
||||||
|
<span data-slot="breakdown-value">{usage.cacheReadTokens ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
<Show when={isClaude}>
|
||||||
|
<div data-slot="breakdown-row">
|
||||||
|
<span data-slot="breakdown-label">Cache Write</span>
|
||||||
|
<span data-slot="breakdown-value">{usage.cacheWrite5mTokens ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td data-slot="usage-tokens">{usage.outputTokens}</td>
|
<td data-slot="usage-tokens">{usage.outputTokens}</td>
|
||||||
<td data-slot="usage-cost">${((usage.cost ?? 0) / 100000000).toFixed(4)}</td>
|
<td data-slot="usage-cost">${((usage.cost ?? 0) / 100000000).toFixed(4)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -121,6 +144,16 @@ export function UsageSection() {
|
|||||||
</For>
|
</For>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<Show when={canGoPrev() || canGoNext()}>
|
||||||
|
<div data-slot="pagination">
|
||||||
|
<button disabled={!canGoPrev()} onClick={goPrev}>
|
||||||
|
<IconChevronLeft />
|
||||||
|
</button>
|
||||||
|
<button disabled={!canGoNext()} onClick={goNext}>
|
||||||
|
<IconChevronRight />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -15,72 +15,99 @@ import { logger } from "./logger"
|
|||||||
import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError, RateLimitError } from "./error"
|
import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError, RateLimitError } from "./error"
|
||||||
import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider"
|
import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider"
|
||||||
import { anthropicHelper } from "./provider/anthropic"
|
import { anthropicHelper } from "./provider/anthropic"
|
||||||
|
import { googleHelper } from "./provider/google"
|
||||||
import { openaiHelper } from "./provider/openai"
|
import { openaiHelper } from "./provider/openai"
|
||||||
import { oaCompatHelper } from "./provider/openai-compatible"
|
import { oaCompatHelper } from "./provider/openai-compatible"
|
||||||
import { createRateLimiter } from "./rateLimiter"
|
import { createRateLimiter } from "./rateLimiter"
|
||||||
|
|
||||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||||
|
type RetryOptions = {
|
||||||
|
excludeProviders: string[]
|
||||||
|
retryCount: number
|
||||||
|
}
|
||||||
|
|
||||||
export async function handler(
|
export async function handler(
|
||||||
input: APIEvent,
|
input: APIEvent,
|
||||||
opts: {
|
opts: {
|
||||||
format: ZenData.Format
|
format: ZenData.Format
|
||||||
parseApiKey: (headers: Headers) => string | undefined
|
parseApiKey: (headers: Headers) => string | undefined
|
||||||
|
parseModel: (url: string, body: any) => string
|
||||||
|
parseIsStream: (url: string, body: any) => boolean
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
type AuthInfo = Awaited<ReturnType<typeof authenticate>>
|
type AuthInfo = Awaited<ReturnType<typeof authenticate>>
|
||||||
type ModelInfo = Awaited<ReturnType<typeof validateModel>>
|
type ModelInfo = Awaited<ReturnType<typeof validateModel>>
|
||||||
type ProviderInfo = Awaited<ReturnType<typeof selectProvider>>
|
type ProviderInfo = Awaited<ReturnType<typeof selectProvider>>
|
||||||
|
|
||||||
|
const MAX_RETRIES = 3
|
||||||
const FREE_WORKSPACES = [
|
const FREE_WORKSPACES = [
|
||||||
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
|
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
|
||||||
"wrk_01K6W1A3VE0KMNVSCQT43BG2SX", // opencode bench
|
"wrk_01K6W1A3VE0KMNVSCQT43BG2SX", // opencode bench
|
||||||
]
|
]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const url = input.request.url
|
||||||
const body = await input.request.json()
|
const body = await input.request.json()
|
||||||
const ip = input.request.headers.get("x-real-ip") ?? ""
|
const ip = input.request.headers.get("x-real-ip") ?? ""
|
||||||
|
const model = opts.parseModel(url, body)
|
||||||
|
const isStream = opts.parseIsStream(url, body)
|
||||||
logger.metric({
|
logger.metric({
|
||||||
is_tream: !!body.stream,
|
is_tream: isStream,
|
||||||
session: input.request.headers.get("x-opencode-session"),
|
session: input.request.headers.get("x-opencode-session"),
|
||||||
request: input.request.headers.get("x-opencode-request"),
|
request: input.request.headers.get("x-opencode-request"),
|
||||||
})
|
})
|
||||||
const zenData = ZenData.list()
|
const zenData = ZenData.list()
|
||||||
const modelInfo = validateModel(zenData, body.model)
|
const modelInfo = validateModel(zenData, model)
|
||||||
const providerInfo = selectProvider(zenData, modelInfo, ip)
|
|
||||||
const authInfo = await authenticate(modelInfo, providerInfo)
|
|
||||||
const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
|
const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
|
||||||
await rateLimiter?.check()
|
await rateLimiter?.check()
|
||||||
validateBilling(authInfo, modelInfo)
|
|
||||||
validateModelSettings(authInfo)
|
|
||||||
updateProviderKey(authInfo, providerInfo)
|
|
||||||
logger.metric({ provider: providerInfo.id })
|
|
||||||
|
|
||||||
// Request to model provider
|
const retriableRequest = async (retry: RetryOptions = { excludeProviders: [], retryCount: 0 }) => {
|
||||||
const startTimestamp = Date.now()
|
const providerInfo = selectProvider(zenData, modelInfo, ip, retry)
|
||||||
const reqUrl = providerInfo.modifyUrl(providerInfo.api)
|
const authInfo = await authenticate(modelInfo, providerInfo)
|
||||||
const reqBody = JSON.stringify(
|
validateBilling(authInfo, modelInfo)
|
||||||
providerInfo.modifyBody({
|
validateModelSettings(authInfo)
|
||||||
...createBodyConverter(opts.format, providerInfo.format)(body),
|
updateProviderKey(authInfo, providerInfo)
|
||||||
model: providerInfo.model,
|
logger.metric({ provider: providerInfo.id })
|
||||||
}),
|
|
||||||
)
|
const startTimestamp = Date.now()
|
||||||
logger.debug("REQUEST URL: " + reqUrl)
|
const reqUrl = providerInfo.modifyUrl(providerInfo.api, providerInfo.model, isStream)
|
||||||
logger.debug("REQUEST: " + reqBody.substring(0, 300) + "...")
|
const reqBody = JSON.stringify(
|
||||||
const res = await fetch(reqUrl, {
|
providerInfo.modifyBody({
|
||||||
method: "POST",
|
...createBodyConverter(opts.format, providerInfo.format)(body),
|
||||||
headers: (() => {
|
model: providerInfo.model,
|
||||||
const headers = input.request.headers
|
}),
|
||||||
headers.delete("host")
|
)
|
||||||
headers.delete("content-length")
|
logger.debug("REQUEST URL: " + reqUrl)
|
||||||
providerInfo.modifyHeaders(headers, body, providerInfo.apiKey)
|
logger.debug("REQUEST: " + reqBody.substring(0, 300) + "...")
|
||||||
Object.entries(providerInfo.headerMappings ?? {}).forEach(([k, v]) => {
|
const res = await fetch(reqUrl, {
|
||||||
headers.set(k, headers.get(v)!)
|
method: "POST",
|
||||||
|
headers: (() => {
|
||||||
|
const headers = new Headers(input.request.headers)
|
||||||
|
providerInfo.modifyHeaders(headers, body, providerInfo.apiKey)
|
||||||
|
Object.entries(providerInfo.headerMappings ?? {}).forEach(([k, v]) => {
|
||||||
|
headers.set(k, headers.get(v)!)
|
||||||
|
})
|
||||||
|
headers.delete("host")
|
||||||
|
headers.delete("content-length")
|
||||||
|
headers.delete("x-opencode-request")
|
||||||
|
headers.delete("x-opencode-session")
|
||||||
|
return headers
|
||||||
|
})(),
|
||||||
|
body: reqBody,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Try another provider => stop retrying if using fallback provider
|
||||||
|
if (res.status !== 200 && modelInfo.fallbackProvider && providerInfo.id !== modelInfo.fallbackProvider) {
|
||||||
|
return retriableRequest({
|
||||||
|
excludeProviders: [...retry.excludeProviders, providerInfo.id],
|
||||||
|
retryCount: retry.retryCount + 1,
|
||||||
})
|
})
|
||||||
return headers
|
}
|
||||||
})(),
|
|
||||||
body: reqBody,
|
return { providerInfo, authInfo, res, startTimestamp }
|
||||||
})
|
}
|
||||||
|
|
||||||
|
const { providerInfo, authInfo, res, startTimestamp } = await retriableRequest()
|
||||||
|
|
||||||
// Scrub response headers
|
// Scrub response headers
|
||||||
const resHeaders = new Headers()
|
const resHeaders = new Headers()
|
||||||
@@ -93,7 +120,7 @@ export async function handler(
|
|||||||
logger.debug("STATUS: " + res.status + " " + res.statusText)
|
logger.debug("STATUS: " + res.status + " " + res.statusText)
|
||||||
|
|
||||||
// Handle non-streaming response
|
// Handle non-streaming response
|
||||||
if (!body.stream) {
|
if (!isStream) {
|
||||||
const responseConverter = createResponseConverter(providerInfo.format, opts.format)
|
const responseConverter = createResponseConverter(providerInfo.format, opts.format)
|
||||||
const json = await res.json()
|
const json = await res.json()
|
||||||
const body = JSON.stringify(responseConverter(json))
|
const body = JSON.stringify(responseConverter(json))
|
||||||
@@ -148,7 +175,7 @@ export async function handler(
|
|||||||
responseLength += value.length
|
responseLength += value.length
|
||||||
buffer += decoder.decode(value, { stream: true })
|
buffer += decoder.decode(value, { stream: true })
|
||||||
|
|
||||||
const parts = buffer.split("\n\n")
|
const parts = buffer.split(providerInfo.streamSeparator)
|
||||||
buffer = parts.pop() ?? ""
|
buffer = parts.pop() ?? ""
|
||||||
|
|
||||||
for (let part of parts) {
|
for (let part of parts) {
|
||||||
@@ -236,19 +263,25 @@ export async function handler(
|
|||||||
return { id: modelId, ...modelData }
|
return { id: modelId, ...modelData }
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectProvider(zenData: ZenData, modelInfo: ModelInfo, ip: string) {
|
function selectProvider(zenData: ZenData, modelInfo: ModelInfo, ip: string, retry: RetryOptions) {
|
||||||
const providers = modelInfo.providers
|
const provider = (() => {
|
||||||
.filter((provider) => !provider.disabled)
|
if (retry.retryCount === MAX_RETRIES) {
|
||||||
.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
|
return modelInfo.providers.find((provider) => provider.id === modelInfo.fallbackProvider)
|
||||||
|
}
|
||||||
|
|
||||||
// Use the last 2 characters of IP address to select a provider
|
const providers = modelInfo.providers
|
||||||
const lastChars = ip.slice(-2)
|
.filter((provider) => !provider.disabled)
|
||||||
const index = parseInt(lastChars, 16) % providers.length
|
.filter((provider) => !retry.excludeProviders.includes(provider.id))
|
||||||
const provider = providers[index || 0]
|
.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
|
||||||
|
|
||||||
if (!(provider.id in zenData.providers)) {
|
// Use the last 2 characters of IP address to select a provider
|
||||||
throw new ModelError(`Provider ${provider.id} not supported`)
|
const lastChars = ip.slice(-2)
|
||||||
}
|
const index = parseInt(lastChars, 16) % providers.length
|
||||||
|
return providers[index || 0]
|
||||||
|
})()
|
||||||
|
|
||||||
|
if (!provider) throw new ModelError("No provider available")
|
||||||
|
if (!(provider.id in zenData.providers)) throw new ModelError(`Provider ${provider.id} not supported`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...provider,
|
...provider,
|
||||||
@@ -256,6 +289,7 @@ export async function handler(
|
|||||||
...(() => {
|
...(() => {
|
||||||
const format = zenData.providers[provider.id].format
|
const format = zenData.providers[provider.id].format
|
||||||
if (format === "anthropic") return anthropicHelper
|
if (format === "anthropic") return anthropicHelper
|
||||||
|
if (format === "google") return googleHelper
|
||||||
if (format === "openai") return openaiHelper
|
if (format === "openai") return openaiHelper
|
||||||
return oaCompatHelper
|
return oaCompatHelper
|
||||||
})(),
|
})(),
|
||||||
@@ -264,7 +298,7 @@ export async function handler(
|
|||||||
|
|
||||||
async function authenticate(modelInfo: ModelInfo, providerInfo: ProviderInfo) {
|
async function authenticate(modelInfo: ModelInfo, providerInfo: ProviderInfo) {
|
||||||
const apiKey = opts.parseApiKey(input.request.headers)
|
const apiKey = opts.parseApiKey(input.request.headers)
|
||||||
if (!apiKey) {
|
if (!apiKey || apiKey === "public") {
|
||||||
if (modelInfo.allowAnonymous) return
|
if (modelInfo.allowAnonymous) return
|
||||||
throw new AuthError("Missing API key.")
|
throw new AuthError("Missing API key.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const anthropicHelper = {
|
|||||||
service_tier: "standard_only",
|
service_tier: "standard_only",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
streamSeparator: "\n\n",
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
let usage: Usage
|
let usage: Usage
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { ProviderHelper } from "./provider"
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
promptTokenCount: 11453,
|
||||||
|
candidatesTokenCount: 71,
|
||||||
|
totalTokenCount: 11625,
|
||||||
|
cachedContentTokenCount: 8100,
|
||||||
|
promptTokensDetails: [
|
||||||
|
{modality: "TEXT",tokenCount: 11453}
|
||||||
|
],
|
||||||
|
cacheTokensDetails: [
|
||||||
|
{modality: "TEXT",tokenCount: 8100}
|
||||||
|
],
|
||||||
|
thoughtsTokenCount: 101
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
type Usage = {
|
||||||
|
promptTokenCount?: number
|
||||||
|
candidatesTokenCount?: number
|
||||||
|
totalTokenCount?: number
|
||||||
|
cachedContentTokenCount?: number
|
||||||
|
promptTokensDetails?: { modality: string; tokenCount: number }[]
|
||||||
|
cacheTokensDetails?: { modality: string; tokenCount: number }[]
|
||||||
|
thoughtsTokenCount?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const googleHelper = {
|
||||||
|
format: "google",
|
||||||
|
modifyUrl: (providerApi: string, model?: string, isStream?: boolean) =>
|
||||||
|
`${providerApi}/models/${model}:${isStream ? "streamGenerateContent?alt=sse" : "generateContent"}`,
|
||||||
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
|
headers.set("x-goog-api-key", apiKey)
|
||||||
|
},
|
||||||
|
modifyBody: (body: Record<string, any>) => {
|
||||||
|
return body
|
||||||
|
},
|
||||||
|
streamSeparator: "\r\n\r\n",
|
||||||
|
createUsageParser: () => {
|
||||||
|
let usage: Usage
|
||||||
|
|
||||||
|
return {
|
||||||
|
parse: (chunk: string) => {
|
||||||
|
if (!chunk.startsWith("data: ")) return
|
||||||
|
|
||||||
|
let json
|
||||||
|
try {
|
||||||
|
json = JSON.parse(chunk.slice(6)) as { usageMetadata?: Usage }
|
||||||
|
} catch (e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json.usageMetadata) return
|
||||||
|
usage = json.usageMetadata
|
||||||
|
},
|
||||||
|
retrieve: () => usage,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
normalizeUsage: (usage: Usage) => {
|
||||||
|
const inputTokens = usage.promptTokenCount ?? 0
|
||||||
|
const outputTokens = usage.candidatesTokenCount ?? 0
|
||||||
|
const reasoningTokens = usage.thoughtsTokenCount ?? 0
|
||||||
|
const cacheReadTokens = usage.cachedContentTokenCount ?? 0
|
||||||
|
return {
|
||||||
|
inputTokens: inputTokens - cacheReadTokens,
|
||||||
|
outputTokens,
|
||||||
|
reasoningTokens,
|
||||||
|
cacheReadTokens,
|
||||||
|
cacheWrite5mTokens: undefined,
|
||||||
|
cacheWrite1hTokens: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} satisfies ProviderHelper
|
||||||
@@ -33,6 +33,7 @@ export const oaCompatHelper = {
|
|||||||
...(body.stream ? { stream_options: { include_usage: true } } : {}),
|
...(body.stream ? { stream_options: { include_usage: true } } : {}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
streamSeparator: "\n\n",
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
let usage: Usage
|
let usage: Usage
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export const openaiHelper = {
|
|||||||
modifyBody: (body: Record<string, any>) => {
|
modifyBody: (body: Record<string, any>) => {
|
||||||
return body
|
return body
|
||||||
},
|
},
|
||||||
|
streamSeparator: "\n\n",
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
let usage: Usage
|
let usage: Usage
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ import {
|
|||||||
|
|
||||||
export type ProviderHelper = {
|
export type ProviderHelper = {
|
||||||
format: ZenData.Format
|
format: ZenData.Format
|
||||||
modifyUrl: (providerApi: string) => string
|
modifyUrl: (providerApi: string, model?: string, isStream?: boolean) => string
|
||||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => void
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => void
|
||||||
modifyBody: (body: Record<string, any>) => Record<string, any>
|
modifyBody: (body: Record<string, any>) => Record<string, any>
|
||||||
|
streamSeparator: string
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
parse: (chunk: string) => void
|
parse: (chunk: string) => void
|
||||||
retrieve: () => any
|
retrieve: () => any
|
||||||
|
|||||||
@@ -5,5 +5,7 @@ export function POST(input: APIEvent) {
|
|||||||
return handler(input, {
|
return handler(input, {
|
||||||
format: "oa-compat",
|
format: "oa-compat",
|
||||||
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
|
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
|
||||||
|
parseModel: (url: string, body: any) => body.model,
|
||||||
|
parseIsStream: (url: string, body: any) => !!body.stream,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,7 @@ export function POST(input: APIEvent) {
|
|||||||
return handler(input, {
|
return handler(input, {
|
||||||
format: "anthropic",
|
format: "anthropic",
|
||||||
parseApiKey: (headers: Headers) => headers.get("x-api-key") ?? undefined,
|
parseApiKey: (headers: Headers) => headers.get("x-api-key") ?? undefined,
|
||||||
|
parseModel: (url: string, body: any) => body.model,
|
||||||
|
parseIsStream: (url: string, body: any) => !!body.stream,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type { APIEvent } from "@solidjs/start/server"
|
||||||
|
import { handler } from "~/routes/zen/util/handler"
|
||||||
|
|
||||||
|
export function POST(input: APIEvent) {
|
||||||
|
return handler(input, {
|
||||||
|
format: "google",
|
||||||
|
parseApiKey: (headers: Headers) => headers.get("x-goog-api-key") ?? undefined,
|
||||||
|
parseModel: (url: string, body: any) => url.split("/").pop()?.split(":")?.[0] ?? "",
|
||||||
|
parseIsStream: (url: string, body: any) =>
|
||||||
|
// ie. url: https://opencode.ai/zen/v1/models/gemini-3-pro:streamGenerateContent?alt=sse'
|
||||||
|
url.split("/").pop()?.split(":")?.[1]?.startsWith("streamGenerateContent") ?? false,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -5,5 +5,7 @@ export function POST(input: APIEvent) {
|
|||||||
return handler(input, {
|
return handler(input, {
|
||||||
format: "openai",
|
format: "openai",
|
||||||
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
|
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
|
||||||
|
parseModel: (url: string, body: any) => body.model,
|
||||||
|
parseIsStream: (url: string, body: any) => !!body.stream,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -57,14 +57,15 @@ export namespace Billing {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usages = async () => {
|
export const usages = async (page = 0, pageSize = 50) => {
|
||||||
return await Database.use((tx) =>
|
return await Database.use((tx) =>
|
||||||
tx
|
tx
|
||||||
.select()
|
.select()
|
||||||
.from(UsageTable)
|
.from(UsageTable)
|
||||||
.where(eq(UsageTable.workspaceID, Actor.workspace()))
|
.where(eq(UsageTable.workspaceID, Actor.workspace()))
|
||||||
.orderBy(sql`${UsageTable.timeCreated} DESC`)
|
.orderBy(sql`${UsageTable.timeCreated} DESC`)
|
||||||
.limit(100),
|
.limit(pageSize)
|
||||||
|
.offset(page * pageSize),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Actor } from "./actor"
|
|||||||
import { Resource } from "@opencode-ai/console-resource"
|
import { Resource } from "@opencode-ai/console-resource"
|
||||||
|
|
||||||
export namespace ZenData {
|
export namespace ZenData {
|
||||||
const FormatSchema = z.enum(["anthropic", "openai", "oa-compat"])
|
const FormatSchema = z.enum(["anthropic", "google", "openai", "oa-compat"])
|
||||||
export type Format = z.infer<typeof FormatSchema>
|
export type Format = z.infer<typeof FormatSchema>
|
||||||
|
|
||||||
const ModelCostSchema = z.object({
|
const ModelCostSchema = z.object({
|
||||||
@@ -25,6 +25,7 @@ export namespace ZenData {
|
|||||||
cost200K: ModelCostSchema.optional(),
|
cost200K: ModelCostSchema.optional(),
|
||||||
allowAnonymous: z.boolean().optional(),
|
allowAnonymous: z.boolean().optional(),
|
||||||
rateLimit: z.number().optional(),
|
rateLimit: z.number().optional(),
|
||||||
|
fallbackProvider: z.string().optional(),
|
||||||
providers: z.array(
|
providers: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ export default {
|
|||||||
if (
|
if (
|
||||||
url.pathname !== "/zen/v1/chat/completions" &&
|
url.pathname !== "/zen/v1/chat/completions" &&
|
||||||
url.pathname !== "/zen/v1/messages" &&
|
url.pathname !== "/zen/v1/messages" &&
|
||||||
url.pathname !== "/zen/v1/responses"
|
url.pathname !== "/zen/v1/responses" &&
|
||||||
|
!url.pathname.startsWith("/zen/v1/models/")
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"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,14 +8,12 @@
|
|||||||
<title>OpenCode</title>
|
<title>OpenCode</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="antialiased overscroll-none select-none text-12-regular">
|
<body class="antialiased overscroll-none select-none text-12-regular">
|
||||||
<!-- <script> -->
|
<script>
|
||||||
<!-- ;(function () { -->
|
;(function () {
|
||||||
<!-- const savedTheme = localStorage.getItem("theme") || "opencode" -->
|
const savedTheme = localStorage.getItem("theme") || "oc-1"
|
||||||
<!-- const savedDarkMode = localStorage.getItem("darkMode") !== "false" -->
|
document.documentElement.setAttribute("data-theme", savedTheme)
|
||||||
<!-- document.documentElement.setAttribute("data-theme", savedTheme) -->
|
})()
|
||||||
<!-- document.documentElement.setAttribute("data-dark", savedDarkMode.toString()) -->
|
</script>
|
||||||
<!-- })() -->
|
|
||||||
<!-- </script> -->
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script src="/src/index.tsx" type="module"></script>
|
<script src="/src/index.tsx" type="module"></script>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const handleFileSelect = (path: string | undefined) => {
|
const handleFileSelect = (path: string | undefined) => {
|
||||||
if (!path) return
|
if (!path) return
|
||||||
addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 })
|
addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 })
|
||||||
setStore("popoverIsOpen", false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { flat, active, onInput, onKeyDown, refetch } = useFilteredList<string>({
|
const { flat, active, onInput, onKeyDown, refetch } = useFilteredList<string>({
|
||||||
@@ -114,16 +113,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
createEffect(
|
|
||||||
on(
|
|
||||||
() => session.prompt.cursor(),
|
|
||||||
(cursor) => {
|
|
||||||
if (cursor === undefined) return
|
|
||||||
queueMicrotask(() => setCursorPosition(editorRef, cursor))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const parseFromDOM = (): Prompt => {
|
const parseFromDOM = (): Prompt => {
|
||||||
const newParts: Prompt = []
|
const newParts: Prompt = []
|
||||||
let position = 0
|
let position = 0
|
||||||
@@ -173,118 +162,70 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addPart = (part: ContentPart) => {
|
const addPart = (part: ContentPart) => {
|
||||||
|
const selection = window.getSelection()
|
||||||
|
if (!selection || selection.rangeCount === 0) return
|
||||||
|
|
||||||
const cursorPosition = getCursorPosition(editorRef)
|
const cursorPosition = getCursorPosition(editorRef)
|
||||||
const prompt = session.prompt.current()
|
const prompt = session.prompt.current()
|
||||||
const rawText = prompt.map((p) => p.content).join("")
|
const rawText = prompt.map((p) => p.content).join("")
|
||||||
const textBeforeCursor = rawText.substring(0, cursorPosition)
|
const textBeforeCursor = rawText.substring(0, cursorPosition)
|
||||||
const atMatch = textBeforeCursor.match(/@(\S*)$/)
|
const atMatch = textBeforeCursor.match(/@(\S*)$/)
|
||||||
|
|
||||||
const startIndex = atMatch ? atMatch.index! : cursorPosition
|
if (part.type === "file") {
|
||||||
const endIndex = atMatch ? cursorPosition : cursorPosition
|
const pill = document.createElement("span")
|
||||||
|
pill.textContent = part.content
|
||||||
|
pill.setAttribute("data-type", "file")
|
||||||
|
pill.setAttribute("data-path", part.path)
|
||||||
|
pill.setAttribute("contenteditable", "false")
|
||||||
|
pill.style.userSelect = "text"
|
||||||
|
pill.style.cursor = "default"
|
||||||
|
|
||||||
const pushText = (acc: { parts: ContentPart[]; runningIndex: number }, value: string) => {
|
const gap = document.createTextNode(" ")
|
||||||
if (!value) return
|
const range = selection.getRangeAt(0)
|
||||||
const last = acc.parts[acc.parts.length - 1]
|
|
||||||
if (last && last.type === "text") {
|
if (atMatch) {
|
||||||
acc.parts[acc.parts.length - 1] = {
|
let node: Node | null = range.startContainer
|
||||||
type: "text",
|
let offset = range.startOffset
|
||||||
content: last.content + value,
|
let runningLength = 0
|
||||||
start: last.start,
|
|
||||||
end: last.end + value.length,
|
const walker = document.createTreeWalker(editorRef, NodeFilter.SHOW_TEXT, null)
|
||||||
|
let currentNode = walker.nextNode()
|
||||||
|
while (currentNode) {
|
||||||
|
const textContent = currentNode.textContent || ""
|
||||||
|
if (runningLength + textContent.length >= atMatch.index!) {
|
||||||
|
const localStart = atMatch.index! - runningLength
|
||||||
|
const localEnd = cursorPosition - runningLength
|
||||||
|
if (currentNode === range.startContainer || runningLength + textContent.length >= cursorPosition) {
|
||||||
|
range.setStart(currentNode, localStart)
|
||||||
|
range.setEnd(currentNode, Math.min(localEnd, textContent.length))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runningLength += textContent.length
|
||||||
|
currentNode = walker.nextNode()
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
acc.parts.push({ type: "text", content: value, start: acc.runningIndex, end: acc.runningIndex + value.length })
|
|
||||||
|
range.deleteContents()
|
||||||
|
range.insertNode(gap)
|
||||||
|
range.insertNode(pill)
|
||||||
|
range.setStartAfter(gap)
|
||||||
|
range.collapse(true)
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(range)
|
||||||
|
} else if (part.type === "text") {
|
||||||
|
const textNode = document.createTextNode(part.content)
|
||||||
|
const range = selection.getRangeAt(0)
|
||||||
|
range.deleteContents()
|
||||||
|
range.insertNode(textNode)
|
||||||
|
range.setStartAfter(textNode)
|
||||||
|
range.collapse(true)
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(range)
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
handleInput()
|
||||||
parts: nextParts,
|
|
||||||
inserted,
|
|
||||||
cursorPositionAfter,
|
|
||||||
} = prompt.reduce(
|
|
||||||
(acc, item) => {
|
|
||||||
if (acc.inserted) {
|
|
||||||
acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length })
|
|
||||||
acc.runningIndex += item.content.length
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextIndex = acc.runningIndex + item.content.length
|
|
||||||
if (nextIndex <= startIndex) {
|
|
||||||
acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length })
|
|
||||||
acc.runningIndex = nextIndex
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.type !== "text") {
|
|
||||||
acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length })
|
|
||||||
acc.runningIndex = nextIndex
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
const headLength = Math.max(0, startIndex - acc.runningIndex)
|
|
||||||
const tailLength = Math.max(0, endIndex - acc.runningIndex)
|
|
||||||
const head = item.content.slice(0, headLength)
|
|
||||||
const tail = item.content.slice(tailLength)
|
|
||||||
|
|
||||||
pushText(acc, head)
|
|
||||||
acc.runningIndex += head.length
|
|
||||||
|
|
||||||
if (part.type === "text") {
|
|
||||||
pushText(acc, part.content)
|
|
||||||
acc.runningIndex += part.content.length
|
|
||||||
}
|
|
||||||
if (part.type !== "text") {
|
|
||||||
acc.parts.push({ ...part, start: acc.runningIndex, end: acc.runningIndex + part.content.length })
|
|
||||||
acc.runningIndex += part.content.length
|
|
||||||
}
|
|
||||||
|
|
||||||
const needsGap = Boolean(atMatch)
|
|
||||||
const rest = needsGap ? (tail ? (/^\s/.test(tail) ? tail : ` ${tail}`) : " ") : tail
|
|
||||||
pushText(acc, rest)
|
|
||||||
acc.runningIndex += rest.length
|
|
||||||
|
|
||||||
const baseCursor = startIndex + part.content.length
|
|
||||||
const cursorAddition = needsGap && rest.length > 0 ? 1 : 0
|
|
||||||
acc.cursorPositionAfter = baseCursor + cursorAddition
|
|
||||||
|
|
||||||
acc.inserted = true
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parts: [] as ContentPart[],
|
|
||||||
runningIndex: 0,
|
|
||||||
inserted: false,
|
|
||||||
cursorPositionAfter: cursorPosition + part.content.length,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!inserted) {
|
|
||||||
const baseParts = prompt.filter((item) => !(item.type === "text" && item.content === ""))
|
|
||||||
const runningIndex = baseParts.reduce((sum, p) => sum + p.content.length, 0)
|
|
||||||
const appendedAcc = { parts: [...baseParts] as ContentPart[], runningIndex }
|
|
||||||
if (part.type === "text") {
|
|
||||||
pushText(appendedAcc, part.content)
|
|
||||||
}
|
|
||||||
if (part.type !== "text") {
|
|
||||||
appendedAcc.parts.push({
|
|
||||||
...part,
|
|
||||||
start: appendedAcc.runningIndex,
|
|
||||||
end: appendedAcc.runningIndex + part.content.length,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const next = appendedAcc.parts.length > 0 ? appendedAcc.parts : DEFAULT_PROMPT
|
|
||||||
const nextCursor = rawText.length + part.content.length
|
|
||||||
session.prompt.set(next, nextCursor)
|
|
||||||
setStore("popoverIsOpen", false)
|
|
||||||
queueMicrotask(() => setCursorPosition(editorRef, nextCursor))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
session.prompt.set(nextParts, cursorPositionAfter)
|
|
||||||
setStore("popoverIsOpen", false)
|
setStore("popoverIsOpen", false)
|
||||||
|
|
||||||
queueMicrotask(() => setCursorPosition(editorRef, cursorPositionAfter))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const abort = () =>
|
const abort = () =>
|
||||||
@@ -325,7 +266,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
if (!existing) {
|
if (!existing) {
|
||||||
const created = await sdk.client.session.create()
|
const created = await sdk.client.session.create()
|
||||||
existing = created.data ?? undefined
|
existing = created.data ?? undefined
|
||||||
if (existing) navigate(`/session/${existing.id}`)
|
if (existing) navigate(existing.id)
|
||||||
}
|
}
|
||||||
if (!existing) return
|
if (!existing) return
|
||||||
|
|
||||||
@@ -378,7 +319,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
|
|
||||||
session.layout.setActiveTab(undefined)
|
session.layout.setActiveTab(undefined)
|
||||||
session.messages.setActive(undefined)
|
session.messages.setActive(undefined)
|
||||||
session.prompt.set(DEFAULT_PROMPT, 0)
|
// Clear the editor DOM directly to ensure it's empty
|
||||||
|
editorRef.innerHTML = ""
|
||||||
|
session.prompt.set([{ type: "text", content: "", start: 0, end: 0 }], 0)
|
||||||
|
|
||||||
sdk.client.session.prompt({
|
sdk.client.session.prompt({
|
||||||
path: { id: existing.id },
|
path: { id: existing.id },
|
||||||
@@ -402,7 +345,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div class="relative size-full _max-h-[320px] flex flex-col gap-3">
|
<div class="relative size-full _max-h-[320px] flex flex-col gap-3">
|
||||||
<Show when={store.popoverIsOpen}>
|
<Show when={store.popoverIsOpen}>
|
||||||
<div class="absolute inset-x-0 -top-3 -translate-y-full origin-bottom-left max-h-[252px] min-h-10 overflow-y-auto flex flex-col p-2 pb-0 rounded-2xl border border-border-base bg-surface-raised-stronger-non-alpha shadow-md">
|
<div
|
||||||
|
class="absolute inset-x-0 -top-3 -translate-y-full origin-bottom-left max-h-[252px] min-h-10
|
||||||
|
overflow-auto no-scrollbar flex flex-col p-2 pb-0 rounded-md
|
||||||
|
border border-border-base bg-surface-raised-stronger-non-alpha shadow-md"
|
||||||
|
>
|
||||||
<Show when={flat().length > 0} fallback={<div class="text-text-weak px-2">No matching files</div>}>
|
<Show when={flat().length > 0} fallback={<div class="text-text-weak px-2">No matching files</div>}>
|
||||||
<For each={flat()}>
|
<For each={flat()}>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
@@ -419,7 +366,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
|
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
|
||||||
{getDirectory(i)}
|
{getDirectory(i)}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-text-strong whitespace-nowrap">{getFilename(i)}</span>
|
<Show when={!i.endsWith("/")}>
|
||||||
|
<span class="text-text-strong whitespace-nowrap">{getFilename(i)}</span>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-x-1 text-text-muted/40 shrink-0"></div>
|
<div class="flex items-center gap-x-1 text-text-muted/40 shrink-0"></div>
|
||||||
@@ -433,7 +382,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
classList={{
|
classList={{
|
||||||
"bg-surface-raised-stronger-non-alpha border border-border-strong-base": true,
|
"bg-surface-raised-stronger-non-alpha border border-border-strong-base": true,
|
||||||
"rounded-2xl overflow-clip focus-within:border-transparent focus-within:shadow-xs-border-select": true,
|
"rounded-md overflow-clip focus-within:border-transparent focus-within:shadow-xs-border-select": true,
|
||||||
[props.class ?? ""]: !!props.class,
|
[props.class ?? ""]: !!props.class,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -447,17 +396,17 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
onInput={handleInput}
|
onInput={handleInput}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
classList={{
|
classList={{
|
||||||
"w-full p-3 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
|
"w-full px-5 py-3 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
|
||||||
"[&>[data-type=file]]:text-icon-info-active": true,
|
"[&>[data-type=file]]:text-icon-info-active": true,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Show when={!session.prompt.dirty()}>
|
<Show when={!session.prompt.dirty()}>
|
||||||
<div class="absolute top-0 left-0 p-3 text-14-regular text-text-weak pointer-events-none">
|
<div class="absolute top-0 left-0 px-5 py-3 text-14-regular text-text-weak pointer-events-none">
|
||||||
Plan and build anything
|
Plan and build anything
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-3 flex items-center justify-between">
|
<div class="relative p-3 flex items-center justify-between">
|
||||||
<div class="flex items-center justify-start gap-1">
|
<div class="flex items-center justify-start gap-1">
|
||||||
<Select
|
<Select
|
||||||
options={local.agent.list().map((agent) => agent.name)}
|
options={local.agent.list().map((agent) => agent.name)}
|
||||||
@@ -540,7 +489,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
disabled={!session.prompt.dirty() && !session.working()}
|
disabled={!session.prompt.dirty() && !session.working()}
|
||||||
icon={session.working() ? "stop" : "arrow-up"}
|
icon={session.working() ? "stop" : "arrow-up"}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
class="rounded-full"
|
class="h-10 w-8 absolute right-2 bottom-2"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import { useSession } from "@/context/session"
|
||||||
|
import { FileIcon } from "@/ui"
|
||||||
|
import { getDirectory, getFilename } from "@/utils"
|
||||||
|
import { Accordion, Button, Diff, DiffChanges, Icon, IconButton, Tooltip } from "@opencode-ai/ui"
|
||||||
|
import { For, Match, Show, Switch } from "solid-js"
|
||||||
|
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
||||||
|
import { createStore } from "solid-js/store"
|
||||||
|
import { useLayout } from "@/context/layout"
|
||||||
|
|
||||||
|
export const SessionReview = (props: { split?: boolean; class?: string; hideExpand?: boolean }) => {
|
||||||
|
const layout = useLayout()
|
||||||
|
const session = useSession()
|
||||||
|
const [store, setStore] = createStore({
|
||||||
|
open: session.diffs().map((d) => d.file),
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleChange = (open: string[]) => {
|
||||||
|
setStore("open", open)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleExpandOrCollapseAll = () => {
|
||||||
|
if (store.open.length > 0) {
|
||||||
|
setStore("open", [])
|
||||||
|
} else {
|
||||||
|
setStore(
|
||||||
|
"open",
|
||||||
|
session.diffs().map((d) => d.file),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
classList={{
|
||||||
|
"flex flex-col gap-3 h-full overflow-y-auto no-scrollbar": true,
|
||||||
|
[props.class ?? ""]: !!props.class,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="sticky top-0 z-20 bg-background-stronger h-8 shrink-0 flex justify-between items-center self-stretch">
|
||||||
|
<div class="text-14-medium text-text-strong">Session changes</div>
|
||||||
|
<div class="flex items-center gap-x-4 pr-px">
|
||||||
|
<Button size="normal" icon="chevron-grabber-vertical" onClick={handleExpandOrCollapseAll}>
|
||||||
|
<Switch>
|
||||||
|
<Match when={store.open.length > 0}>Collapse all</Match>
|
||||||
|
<Match when={true}>Expand all</Match>
|
||||||
|
</Switch>
|
||||||
|
</Button>
|
||||||
|
<Show when={!props.hideExpand}>
|
||||||
|
<Tooltip value="Open in tab">
|
||||||
|
<IconButton
|
||||||
|
icon="expand"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
layout.review.tab()
|
||||||
|
session.layout.setActiveTab("review")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Accordion multiple value={store.open} onChange={handleChange}>
|
||||||
|
<For each={session.diffs()}>
|
||||||
|
{(diff) => (
|
||||||
|
<Accordion.Item value={diff.file}>
|
||||||
|
<StickyAccordionHeader class="top-11 data-expanded:before:-top-11">
|
||||||
|
<Accordion.Trigger class="bg-background-stronger">
|
||||||
|
<div class="flex items-center justify-between w-full gap-5">
|
||||||
|
<div class="grow flex items-center gap-5 min-w-0">
|
||||||
|
<FileIcon node={{ path: diff.file, type: "file" }} class="shrink-0 size-4" />
|
||||||
|
<div class="flex grow min-w-0">
|
||||||
|
<Show when={diff.file.includes("/")}>
|
||||||
|
<span class="text-text-base truncate-start">{getDirectory(diff.file)}‎</span>
|
||||||
|
</Show>
|
||||||
|
<span class="text-text-strong shrink-0">{getFilename(diff.file)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="shrink-0 flex gap-4 items-center justify-end">
|
||||||
|
<DiffChanges changes={diff} />
|
||||||
|
<Icon name="chevron-grabber-vertical" size="small" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Accordion.Trigger>
|
||||||
|
</StickyAccordionHeader>
|
||||||
|
<Accordion.Content>
|
||||||
|
<Diff
|
||||||
|
diffStyle={props.split ? "split" : "unified"}
|
||||||
|
before={{
|
||||||
|
name: diff.file!,
|
||||||
|
contents: diff.before!,
|
||||||
|
}}
|
||||||
|
after={{
|
||||||
|
name: diff.file!,
|
||||||
|
contents: diff.after!,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Accordion.Content>
|
||||||
|
</Accordion.Item>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Accordion } from "@opencode-ai/ui"
|
||||||
|
import { ParentProps } from "solid-js"
|
||||||
|
|
||||||
|
export function StickyAccordionHeader(props: ParentProps<{ class?: string }>) {
|
||||||
|
return (
|
||||||
|
<Accordion.Header
|
||||||
|
classList={{
|
||||||
|
"sticky top-0 data-expanded:z-10": true,
|
||||||
|
"data-expanded:before:content-[''] data-expanded:before:z-[-10]": true,
|
||||||
|
"data-expanded:before:absolute data-expanded:before:inset-0 data-expanded:before:bg-background-stronger": true,
|
||||||
|
[props.class ?? ""]: !!props.class,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Accordion.Header>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client"
|
||||||
|
import { createSimpleContext } from "./helper"
|
||||||
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
|
import { onCleanup } from "solid-js"
|
||||||
|
|
||||||
|
export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleContext({
|
||||||
|
name: "GlobalSDK",
|
||||||
|
init: (props: { url: string }) => {
|
||||||
|
const abort = new AbortController()
|
||||||
|
const sdk = createOpencodeClient({
|
||||||
|
baseUrl: props.url,
|
||||||
|
signal: abort.signal,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emitter = createGlobalEmitter<{
|
||||||
|
[key: string]: Event
|
||||||
|
}>()
|
||||||
|
|
||||||
|
sdk.global.event().then(async (events) => {
|
||||||
|
for await (const event of events.stream) {
|
||||||
|
// console.log("event", event)
|
||||||
|
emitter.emit(event.directory, event.payload)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
abort.abort()
|
||||||
|
})
|
||||||
|
|
||||||
|
return { url: props.url, client: sdk, event: emitter }
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import type {
|
||||||
|
Message,
|
||||||
|
Agent,
|
||||||
|
Provider,
|
||||||
|
Session,
|
||||||
|
Part,
|
||||||
|
Config,
|
||||||
|
Path,
|
||||||
|
File,
|
||||||
|
FileNode,
|
||||||
|
Project,
|
||||||
|
FileDiff,
|
||||||
|
Todo,
|
||||||
|
SessionStatus,
|
||||||
|
} from "@opencode-ai/sdk"
|
||||||
|
import { createStore, produce, reconcile } from "solid-js/store"
|
||||||
|
import { Binary } from "@/utils/binary"
|
||||||
|
import { createSimpleContext } from "./helper"
|
||||||
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
ready: boolean
|
||||||
|
provider: Provider[]
|
||||||
|
agent: Agent[]
|
||||||
|
project: Project
|
||||||
|
config: Config
|
||||||
|
path: Path
|
||||||
|
session: Session[]
|
||||||
|
session_status: {
|
||||||
|
[sessionID: string]: SessionStatus
|
||||||
|
}
|
||||||
|
session_diff: {
|
||||||
|
[sessionID: string]: FileDiff[]
|
||||||
|
}
|
||||||
|
todo: {
|
||||||
|
[sessionID: string]: Todo[]
|
||||||
|
}
|
||||||
|
limit: number
|
||||||
|
message: {
|
||||||
|
[sessionID: string]: Message[]
|
||||||
|
}
|
||||||
|
part: {
|
||||||
|
[messageID: string]: Part[]
|
||||||
|
}
|
||||||
|
node: FileNode[]
|
||||||
|
changes: File[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const { use: useGlobalSync, provider: GlobalSyncProvider } = createSimpleContext({
|
||||||
|
name: "GlobalSync",
|
||||||
|
init: () => {
|
||||||
|
const [globalStore, setGlobalStore] = createStore<{
|
||||||
|
ready: boolean
|
||||||
|
defaultProject?: Project // TODO: remove this when we can select projects
|
||||||
|
projects: Project[]
|
||||||
|
children: Record<string, State>
|
||||||
|
}>({
|
||||||
|
ready: false,
|
||||||
|
projects: [],
|
||||||
|
children: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
const children: Record<string, ReturnType<typeof createStore<State>>> = {}
|
||||||
|
|
||||||
|
function child(directory: string) {
|
||||||
|
if (!children[directory]) {
|
||||||
|
setGlobalStore("children", directory, {
|
||||||
|
project: { id: "", worktree: "", time: { created: 0, initialized: 0 } },
|
||||||
|
config: {},
|
||||||
|
path: { state: "", config: "", worktree: "", directory: "" },
|
||||||
|
ready: false,
|
||||||
|
agent: [],
|
||||||
|
provider: [],
|
||||||
|
session: [],
|
||||||
|
session_status: {},
|
||||||
|
session_diff: {},
|
||||||
|
todo: {},
|
||||||
|
limit: 10,
|
||||||
|
message: {},
|
||||||
|
part: {},
|
||||||
|
node: [],
|
||||||
|
changes: [],
|
||||||
|
})
|
||||||
|
children[directory] = createStore(globalStore.children[directory])
|
||||||
|
}
|
||||||
|
return children[directory]
|
||||||
|
}
|
||||||
|
|
||||||
|
const sdk = useGlobalSDK()
|
||||||
|
sdk.event.listen((e) => {
|
||||||
|
const directory = e.name
|
||||||
|
const [store, setStore] = child(directory)
|
||||||
|
|
||||||
|
const event = e.details
|
||||||
|
switch (event.type) {
|
||||||
|
case "session.updated": {
|
||||||
|
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
|
||||||
|
if (result.found) {
|
||||||
|
setStore("session", result.index, reconcile(event.properties.info))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
setStore(
|
||||||
|
"session",
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 0, event.properties.info)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "session.diff":
|
||||||
|
setStore("session_diff", event.properties.sessionID, event.properties.diff)
|
||||||
|
break
|
||||||
|
case "todo.updated":
|
||||||
|
setStore("todo", event.properties.sessionID, event.properties.todos)
|
||||||
|
break
|
||||||
|
case "session.status": {
|
||||||
|
setStore("session_status", event.properties.sessionID, event.properties.status)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "message.updated": {
|
||||||
|
const messages = store.message[event.properties.info.sessionID]
|
||||||
|
if (!messages) {
|
||||||
|
setStore("message", event.properties.info.sessionID, [event.properties.info])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
|
||||||
|
if (result.found) {
|
||||||
|
setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
setStore(
|
||||||
|
"message",
|
||||||
|
event.properties.info.sessionID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 0, event.properties.info)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "message.part.updated": {
|
||||||
|
const part = event.properties.part
|
||||||
|
const parts = store.part[part.messageID]
|
||||||
|
if (!parts) {
|
||||||
|
setStore("part", part.messageID, [part])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
const result = Binary.search(parts, part.id, (p) => p.id)
|
||||||
|
if (result.found) {
|
||||||
|
setStore("part", part.messageID, result.index, reconcile(part))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
setStore(
|
||||||
|
"part",
|
||||||
|
part.messageID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 0, part)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
sdk.client.project.list().then((x) =>
|
||||||
|
setGlobalStore(
|
||||||
|
"projects",
|
||||||
|
x.data!.filter((x) => !x.worktree.includes("opencode-test")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// TODO: remove this when we can select projects
|
||||||
|
sdk.client.project.current().then((x) => setGlobalStore("defaultProject", x.data)),
|
||||||
|
]).then(() => setGlobalStore("ready", true))
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: globalStore,
|
||||||
|
get ready() {
|
||||||
|
return globalStore.ready
|
||||||
|
},
|
||||||
|
child,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { createStore } from "solid-js/store"
|
||||||
|
import { createMemo } from "solid-js"
|
||||||
|
import { createSimpleContext } from "./helper"
|
||||||
|
import { makePersisted } from "@solid-primitives/storage"
|
||||||
|
import { useGlobalSync } from "./global-sync"
|
||||||
|
|
||||||
|
export const { use: useLayout, provider: LayoutProvider } = createSimpleContext({
|
||||||
|
name: "Layout",
|
||||||
|
init: () => {
|
||||||
|
const globalSync = useGlobalSync()
|
||||||
|
const [store, setStore] = makePersisted(
|
||||||
|
createStore({
|
||||||
|
projects: [] as { directory: string; expanded: boolean }[],
|
||||||
|
sidebar: {
|
||||||
|
opened: true,
|
||||||
|
width: 280,
|
||||||
|
},
|
||||||
|
review: {
|
||||||
|
state: "pane" as "pane" | "tab",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: "___default-layout",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
projects: {
|
||||||
|
list: createMemo(() =>
|
||||||
|
globalSync.data.defaultProject
|
||||||
|
? [{ directory: globalSync.data.defaultProject!.worktree, expanded: true }, ...store.projects]
|
||||||
|
: store.projects,
|
||||||
|
),
|
||||||
|
open(directory: string) {
|
||||||
|
if (store.projects.find((x) => x.directory === directory)) return
|
||||||
|
setStore("projects", (x) => [...x, { directory, expanded: true }])
|
||||||
|
},
|
||||||
|
close(directory: string) {
|
||||||
|
setStore("projects", (x) => x.filter((x) => x.directory !== directory))
|
||||||
|
},
|
||||||
|
expand(directory: string) {
|
||||||
|
setStore("projects", (x) => x.map((x) => (x.directory === directory ? { ...x, expanded: true } : x)))
|
||||||
|
},
|
||||||
|
collapse(directory: string) {
|
||||||
|
setStore("projects", (x) => x.map((x) => (x.directory === directory ? { ...x, expanded: false } : x)))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
opened: createMemo(() => store.sidebar.opened),
|
||||||
|
open() {
|
||||||
|
setStore("sidebar", "opened", true)
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
setStore("sidebar", "opened", false)
|
||||||
|
},
|
||||||
|
toggle() {
|
||||||
|
setStore("sidebar", "opened", (x) => !x)
|
||||||
|
},
|
||||||
|
width: createMemo(() => store.sidebar.width),
|
||||||
|
resize(width: number) {
|
||||||
|
setStore("sidebar", "width", width)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
review: {
|
||||||
|
state: createMemo(() => store.review?.state ?? "closed"),
|
||||||
|
pane() {
|
||||||
|
setStore("review", "state", "pane")
|
||||||
|
},
|
||||||
|
tab() {
|
||||||
|
setStore("review", "state", "tab")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -5,7 +5,7 @@ import type { FileContent, FileNode, Model, Provider, File as FileStatus } from
|
|||||||
import { createSimpleContext } from "./helper"
|
import { createSimpleContext } from "./helper"
|
||||||
import { useSDK } from "./sdk"
|
import { useSDK } from "./sdk"
|
||||||
import { useSync } from "./sync"
|
import { useSync } from "./sync"
|
||||||
import { makePersisted } from "@solid-primitives/storage"
|
import { base64Encode } from "@/utils"
|
||||||
|
|
||||||
export type LocalFile = FileNode &
|
export type LocalFile = FileNode &
|
||||||
Partial<{
|
Partial<{
|
||||||
@@ -457,60 +457,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
const layout = (() => {
|
|
||||||
const [store, setStore] = makePersisted(
|
|
||||||
createStore({
|
|
||||||
sidebar: {
|
|
||||||
opened: true,
|
|
||||||
width: 240,
|
|
||||||
},
|
|
||||||
review: {
|
|
||||||
state: "closed" as "open" | "closed" | "tab",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: "default-layout",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
sidebar: {
|
|
||||||
opened: createMemo(() => store.sidebar.opened),
|
|
||||||
open() {
|
|
||||||
setStore("sidebar", "opened", true)
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
setStore("sidebar", "opened", false)
|
|
||||||
},
|
|
||||||
toggle() {
|
|
||||||
setStore("sidebar", "opened", (x) => !x)
|
|
||||||
},
|
|
||||||
width: createMemo(() => store.sidebar.width),
|
|
||||||
resize(width: number) {
|
|
||||||
setStore("sidebar", "width", width)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
review: {
|
|
||||||
state: createMemo(() => store.review?.state ?? "closed"),
|
|
||||||
open() {
|
|
||||||
setStore("review", "state", "open")
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
setStore("review", "state", "closed")
|
|
||||||
},
|
|
||||||
tab() {
|
|
||||||
setStore("review", "state", "tab")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
|
slug: createMemo(() => base64Encode(sdk.directory)),
|
||||||
model,
|
model,
|
||||||
agent,
|
agent,
|
||||||
file,
|
file,
|
||||||
context,
|
context,
|
||||||
layout,
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,36 +2,31 @@ import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client"
|
|||||||
import { createSimpleContext } from "./helper"
|
import { createSimpleContext } from "./helper"
|
||||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
import { onCleanup } from "solid-js"
|
import { onCleanup } from "solid-js"
|
||||||
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
|
|
||||||
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||||
name: "SDK",
|
name: "SDK",
|
||||||
init: (props: { url: string }) => {
|
init: (props: { directory: string }) => {
|
||||||
|
const globalSDK = useGlobalSDK()
|
||||||
const abort = new AbortController()
|
const abort = new AbortController()
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: props.url,
|
baseUrl: globalSDK.url,
|
||||||
signal: abort.signal,
|
signal: abort.signal,
|
||||||
fetch: (req) => {
|
directory: props.directory,
|
||||||
// @ts-ignore
|
|
||||||
req.timeout = false
|
|
||||||
return fetch(req)
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const emitter = createGlobalEmitter<{
|
const emitter = createGlobalEmitter<{
|
||||||
[key in Event["type"]]: Extract<Event, { type: key }>
|
[key in Event["type"]]: Extract<Event, { type: key }>
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
sdk.event.subscribe().then(async (events) => {
|
globalSDK.event.on(props.directory, async (event) => {
|
||||||
for await (const event of events.stream) {
|
emitter.emit(event.type, event)
|
||||||
console.log("event", event.type)
|
|
||||||
emitter.emit(event.type, event)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
abort.abort()
|
abort.abort()
|
||||||
})
|
})
|
||||||
|
|
||||||
return { client: sdk, event: emitter }
|
return { directory: props.directory, client: sdk, event: emitter }
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,43 +3,49 @@ import { createSimpleContext } from "./helper"
|
|||||||
import { batch, createEffect, createMemo } from "solid-js"
|
import { batch, createEffect, createMemo } from "solid-js"
|
||||||
import { useSync } from "./sync"
|
import { useSync } from "./sync"
|
||||||
import { makePersisted } from "@solid-primitives/storage"
|
import { makePersisted } from "@solid-primitives/storage"
|
||||||
import { TextSelection, useLocal } from "./local"
|
import { TextSelection } from "./local"
|
||||||
import { pipe, sumBy } from "remeda"
|
import { pipe, sumBy } from "remeda"
|
||||||
import { AssistantMessage } from "@opencode-ai/sdk"
|
import { AssistantMessage } from "@opencode-ai/sdk"
|
||||||
|
import { useParams } from "@solidjs/router"
|
||||||
|
import { base64Encode } from "@/utils"
|
||||||
|
|
||||||
export const { use: useSession, provider: SessionProvider } = createSimpleContext({
|
export const { use: useSession, provider: SessionProvider } = createSimpleContext({
|
||||||
name: "Session",
|
name: "Session",
|
||||||
init: (props: { sessionId?: string }) => {
|
init: () => {
|
||||||
|
const params = useParams()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const local = useLocal()
|
const name = createMemo(
|
||||||
|
() => `___${base64Encode(sync.data.project.worktree)}/session${params.id ? "/" + params.id : ""}`,
|
||||||
|
)
|
||||||
|
|
||||||
const [store, setStore] = makePersisted(
|
const [store, setStore] = makePersisted(
|
||||||
createStore<{
|
createStore<{
|
||||||
prompt: Prompt
|
|
||||||
cursorPosition?: number
|
|
||||||
messageId?: string
|
messageId?: string
|
||||||
tabs: {
|
tabs: {
|
||||||
active?: string
|
active?: string
|
||||||
opened: string[]
|
opened: string[]
|
||||||
}
|
}
|
||||||
|
prompt: Prompt
|
||||||
|
cursor?: number
|
||||||
}>({
|
}>({
|
||||||
prompt: [{ type: "text", content: "", start: 0, end: 0 }],
|
|
||||||
tabs: {
|
tabs: {
|
||||||
opened: [],
|
opened: [],
|
||||||
},
|
},
|
||||||
|
prompt: clonePrompt(DEFAULT_PROMPT),
|
||||||
|
cursor: undefined,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: props.sessionId ?? "new-session",
|
name: name(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!props.sessionId) return
|
if (!params.id) return
|
||||||
sync.session.sync(props.sessionId)
|
sync.session.sync(params.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
const info = createMemo(() => (props.sessionId ? sync.session.get(props.sessionId) : undefined))
|
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
||||||
const messages = createMemo(() => (props.sessionId ? (sync.data.message[props.sessionId] ?? []) : []))
|
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
||||||
const userMessages = createMemo(() =>
|
const userMessages = createMemo(() =>
|
||||||
messages()
|
messages()
|
||||||
.filter((m) => m.role === "user")
|
.filter((m) => m.role === "user")
|
||||||
@@ -52,16 +58,13 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
|
|||||||
if (!store.messageId) return lastUserMessage()
|
if (!store.messageId) return lastUserMessage()
|
||||||
return userMessages()?.find((m) => m.id === store.messageId)
|
return userMessages()?.find((m) => m.id === store.messageId)
|
||||||
})
|
})
|
||||||
const working = createMemo(() => {
|
const status = createMemo(
|
||||||
if (!props.sessionId) return false
|
() =>
|
||||||
const last = lastUserMessage()
|
sync.data.session_status[params.id] ?? {
|
||||||
if (!last) return false
|
type: "idle",
|
||||||
const assistantMessages = sync.data.message[props.sessionId]?.filter(
|
},
|
||||||
(m) => m.role === "assistant" && m.parentID == last?.id,
|
)
|
||||||
) as AssistantMessage[]
|
const working = createMemo(() => status()?.type !== "idle")
|
||||||
const error = assistantMessages?.find((m) => m?.error)?.error
|
|
||||||
return !last?.summary?.body && !error
|
|
||||||
})
|
|
||||||
|
|
||||||
const cost = createMemo(() => {
|
const cost = createMemo(() => {
|
||||||
const total = pipe(
|
const total = pipe(
|
||||||
@@ -80,7 +83,7 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
|
|||||||
const model = createMemo(() =>
|
const model = createMemo(() =>
|
||||||
last() ? sync.data.provider.find((x) => x.id === last().providerID)?.models[last().modelID] : undefined,
|
last() ? sync.data.provider.find((x) => x.id === last().providerID)?.models[last().modelID] : undefined,
|
||||||
)
|
)
|
||||||
const diffs = createMemo(() => (props.sessionId ? (sync.data.session_diff[props.sessionId] ?? []) : []))
|
const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : []))
|
||||||
|
|
||||||
const tokens = createMemo(() => {
|
const tokens = createMemo(() => {
|
||||||
if (!last()) return
|
if (!last()) return
|
||||||
@@ -96,18 +99,22 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: props.sessionId,
|
get id() {
|
||||||
|
return params.id
|
||||||
|
},
|
||||||
info,
|
info,
|
||||||
|
status,
|
||||||
working,
|
working,
|
||||||
diffs,
|
diffs,
|
||||||
prompt: {
|
prompt: {
|
||||||
current: createMemo(() => store.prompt),
|
current: createMemo(() => store.prompt),
|
||||||
cursor: createMemo(() => store.cursorPosition),
|
cursor: createMemo(() => store.cursor),
|
||||||
dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)),
|
dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)),
|
||||||
set(prompt: Prompt, cursorPosition?: number) {
|
set(prompt: Prompt, cursorPosition?: number) {
|
||||||
|
const next = clonePrompt(prompt)
|
||||||
batch(() => {
|
batch(() => {
|
||||||
setStore("prompt", prompt)
|
setStore("prompt", next)
|
||||||
if (cursorPosition !== undefined) setStore("cursorPosition", cursorPosition)
|
if (cursorPosition !== undefined) setStore("cursor", cursorPosition)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -138,9 +145,6 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
|
|||||||
setStore("tabs", "active", undefined)
|
setStore("tabs", "active", undefined)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (tab.startsWith("file://")) {
|
|
||||||
await local.file.open(tab.replace("file://", ""))
|
|
||||||
}
|
|
||||||
if (tab !== "review") {
|
if (tab !== "review") {
|
||||||
if (!store.tabs.opened.includes(tab)) {
|
if (!store.tabs.opened.includes(tab)) {
|
||||||
setStore("tabs", "opened", [...store.tabs.opened, tab])
|
setStore("tabs", "opened", [...store.tabs.opened, tab])
|
||||||
@@ -172,7 +176,6 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
|
|||||||
opened.splice(to, 0, opened.splice(index, 1)[0])
|
opened.splice(to, 0, opened.splice(index, 1)[0])
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
// setStore("node", path, "pinned", true)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -215,3 +218,20 @@ export function isPromptEqual(promptA: Prompt, promptB: Prompt): boolean {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneSelection(selection?: TextSelection) {
|
||||||
|
if (!selection) return undefined
|
||||||
|
return { ...selection }
|
||||||
|
}
|
||||||
|
|
||||||
|
function clonePart(part: ContentPart): ContentPart {
|
||||||
|
if (part.type === "text") return { ...part }
|
||||||
|
return {
|
||||||
|
...part,
|
||||||
|
selection: cloneSelection(part.selection),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clonePrompt(prompt: Prompt): Prompt {
|
||||||
|
return prompt.map(clonePart)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,133 +1,17 @@
|
|||||||
import type {
|
import type { Part } from "@opencode-ai/sdk"
|
||||||
Message,
|
import { produce } from "solid-js/store"
|
||||||
Agent,
|
|
||||||
Provider,
|
|
||||||
Session,
|
|
||||||
Part,
|
|
||||||
Config,
|
|
||||||
Path,
|
|
||||||
File,
|
|
||||||
FileNode,
|
|
||||||
Project,
|
|
||||||
FileDiff,
|
|
||||||
Todo,
|
|
||||||
} from "@opencode-ai/sdk"
|
|
||||||
import { createStore, produce, reconcile } from "solid-js/store"
|
|
||||||
import { createMemo } from "solid-js"
|
import { createMemo } from "solid-js"
|
||||||
import { Binary } from "@/utils/binary"
|
import { Binary } from "@/utils/binary"
|
||||||
import { createSimpleContext } from "./helper"
|
import { createSimpleContext } from "./helper"
|
||||||
|
import { useGlobalSync } from "./global-sync"
|
||||||
import { useSDK } from "./sdk"
|
import { useSDK } from "./sdk"
|
||||||
|
|
||||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
name: "Sync",
|
name: "Sync",
|
||||||
init: () => {
|
init: () => {
|
||||||
const [store, setStore] = createStore<{
|
const globalSync = useGlobalSync()
|
||||||
ready: boolean
|
|
||||||
provider: Provider[]
|
|
||||||
agent: Agent[]
|
|
||||||
project: Project
|
|
||||||
config: Config
|
|
||||||
path: Path
|
|
||||||
session: Session[]
|
|
||||||
session_diff: {
|
|
||||||
[sessionID: string]: FileDiff[]
|
|
||||||
}
|
|
||||||
todo: {
|
|
||||||
[sessionID: string]: Todo[]
|
|
||||||
}
|
|
||||||
limit: number
|
|
||||||
message: {
|
|
||||||
[sessionID: string]: Message[]
|
|
||||||
}
|
|
||||||
part: {
|
|
||||||
[messageID: string]: Part[]
|
|
||||||
}
|
|
||||||
node: FileNode[]
|
|
||||||
changes: File[]
|
|
||||||
}>({
|
|
||||||
project: { id: "", worktree: "", time: { created: 0, initialized: 0 } },
|
|
||||||
config: {},
|
|
||||||
path: { state: "", config: "", worktree: "", directory: "" },
|
|
||||||
ready: false,
|
|
||||||
agent: [],
|
|
||||||
provider: [],
|
|
||||||
session: [],
|
|
||||||
session_diff: {},
|
|
||||||
todo: {},
|
|
||||||
limit: 10,
|
|
||||||
message: {},
|
|
||||||
part: {},
|
|
||||||
node: [],
|
|
||||||
changes: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
sdk.event.listen((e) => {
|
const [store, setStore] = globalSync.child(sdk.directory)
|
||||||
const event = e.details
|
|
||||||
switch (event.type) {
|
|
||||||
case "session.updated": {
|
|
||||||
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
|
|
||||||
if (result.found) {
|
|
||||||
setStore("session", result.index, reconcile(event.properties.info))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
setStore(
|
|
||||||
"session",
|
|
||||||
produce((draft) => {
|
|
||||||
draft.splice(result.index, 0, event.properties.info)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "session.diff":
|
|
||||||
setStore("session_diff", event.properties.sessionID, event.properties.diff)
|
|
||||||
break
|
|
||||||
case "todo.updated":
|
|
||||||
setStore("todo", event.properties.sessionID, event.properties.todos)
|
|
||||||
break
|
|
||||||
case "message.updated": {
|
|
||||||
const messages = store.message[event.properties.info.sessionID]
|
|
||||||
if (!messages) {
|
|
||||||
setStore("message", event.properties.info.sessionID, [event.properties.info])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
|
|
||||||
if (result.found) {
|
|
||||||
setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
setStore(
|
|
||||||
"message",
|
|
||||||
event.properties.info.sessionID,
|
|
||||||
produce((draft) => {
|
|
||||||
draft.splice(result.index, 0, event.properties.info)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "message.part.updated": {
|
|
||||||
const part = sanitizePart(event.properties.part)
|
|
||||||
const parts = store.part[part.messageID]
|
|
||||||
if (!parts) {
|
|
||||||
setStore("part", part.messageID, [part])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
const result = Binary.search(parts, part.id, (p) => p.id)
|
|
||||||
if (result.found) {
|
|
||||||
setStore("part", part.messageID, result.index, reconcile(part))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
setStore(
|
|
||||||
"part",
|
|
||||||
part.messageID,
|
|
||||||
produce((draft) => {
|
|
||||||
draft.splice(result.index, 0, part)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const load = {
|
const load = {
|
||||||
project: () => sdk.client.project.current().then((x) => setStore("project", x.data!)),
|
project: () => sdk.client.project.current().then((x) => setStore("project", x.data!)),
|
||||||
@@ -142,6 +26,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
.slice(0, store.limit)
|
.slice(0, store.limit)
|
||||||
setStore("session", sessions)
|
setStore("session", sessions)
|
||||||
}),
|
}),
|
||||||
|
status: () => sdk.client.session.status().then((x) => setStore("session_status", x.data!)),
|
||||||
config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)),
|
config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)),
|
||||||
changes: () => sdk.client.file.status().then((x) => setStore("changes", x.data!)),
|
changes: () => sdk.client.file.status().then((x) => setStore("changes", x.data!)),
|
||||||
node: () => sdk.client.file.list({ query: { path: "/" } }).then((x) => setStore("node", x.data!)),
|
node: () => sdk.client.file.list({ query: { path: "/" } }).then((x) => setStore("node", x.data!)),
|
||||||
@@ -216,7 +101,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
setStore("limit", (x) => x + count)
|
setStore("limit", (x) => x + count)
|
||||||
await load.session()
|
await load.session()
|
||||||
},
|
},
|
||||||
more: createMemo(() => store.session.length === store.limit),
|
more: createMemo(() => store.session.length >= store.limit),
|
||||||
},
|
},
|
||||||
load,
|
load,
|
||||||
absolute,
|
absolute,
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { createSignal, onCleanup, onMount } from "solid-js"
|
||||||
|
import { isServer } from "solid-js/web"
|
||||||
|
|
||||||
|
export function createSessionSeen(key: string, delay = 1000) {
|
||||||
|
// 1. Initialize state based on storage (default to true on server to avoid flash)
|
||||||
|
const storageKey = `app:seen:${key}`
|
||||||
|
const [hasSeen] = createSignal(!isServer && sessionStorage.getItem(storageKey) === "true")
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// 2. If we haven't seen it, mark it as seen for NEXT time
|
||||||
|
if (!hasSeen()) {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
sessionStorage.setItem(storageKey, "true")
|
||||||
|
}, delay)
|
||||||
|
onCleanup(() => clearTimeout(timer))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return hasSeen
|
||||||
|
}
|
||||||
@@ -1 +1,7 @@
|
|||||||
@import "@opencode-ai/ui/styles/tailwind";
|
@import "@opencode-ai/ui/styles/tailwind";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
a {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
/* @refresh reload */
|
/* @refresh reload */
|
||||||
import "@/index.css"
|
import "@/index.css"
|
||||||
import { render } from "solid-js/web"
|
import { render } from "solid-js/web"
|
||||||
import { Router, Route } from "@solidjs/router"
|
import { Router, Route, Navigate } from "@solidjs/router"
|
||||||
import { MetaProvider } from "@solidjs/meta"
|
import { MetaProvider } from "@solidjs/meta"
|
||||||
import { Fonts, MarkedProvider } from "@opencode-ai/ui"
|
import { Fonts, MarkedProvider } from "@opencode-ai/ui"
|
||||||
import { SDKProvider } from "./context/sdk"
|
import { GlobalSyncProvider, useGlobalSync } from "./context/global-sync"
|
||||||
import { SyncProvider } from "./context/sync"
|
|
||||||
import { LocalProvider } from "./context/local"
|
|
||||||
import Layout from "@/pages/layout"
|
import Layout from "@/pages/layout"
|
||||||
import SessionLayout from "@/pages/session-layout"
|
import DirectoryLayout from "@/pages/directory-layout"
|
||||||
import Session from "@/pages/session"
|
import Session from "@/pages/session"
|
||||||
|
import { LayoutProvider } from "./context/layout"
|
||||||
|
import { GlobalSDKProvider } from "./context/global-sdk"
|
||||||
|
import { SessionProvider } from "./context/session"
|
||||||
|
import { base64Encode } from "./utils"
|
||||||
|
import { createMemo, Show } from "solid-js"
|
||||||
|
|
||||||
const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1"
|
const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1"
|
||||||
const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"
|
const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"
|
||||||
@@ -30,20 +33,38 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
|||||||
render(
|
render(
|
||||||
() => (
|
() => (
|
||||||
<MarkedProvider>
|
<MarkedProvider>
|
||||||
<SDKProvider url={url}>
|
<GlobalSDKProvider url={url}>
|
||||||
<SyncProvider>
|
<GlobalSyncProvider>
|
||||||
<LocalProvider>
|
<LayoutProvider>
|
||||||
<MetaProvider>
|
<MetaProvider>
|
||||||
<Fonts />
|
<Fonts />
|
||||||
<Router root={Layout}>
|
<Router root={Layout}>
|
||||||
<Route path={["/", "/session"]} component={SessionLayout}>
|
<Route
|
||||||
<Route path="/:id?" component={Session} />
|
path="/"
|
||||||
|
component={() => {
|
||||||
|
const globalSync = useGlobalSync()
|
||||||
|
const slug = createMemo(() => base64Encode(globalSync.data.defaultProject!.worktree))
|
||||||
|
return <Navigate href={`${slug()}/session`} />
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Route path="/:dir" component={DirectoryLayout}>
|
||||||
|
<Route path="/" component={() => <Navigate href="session" />} />
|
||||||
|
<Route
|
||||||
|
path="/session/:id?"
|
||||||
|
component={(p) => (
|
||||||
|
<Show when={p.params.id || true} keyed>
|
||||||
|
<SessionProvider>
|
||||||
|
<Session />
|
||||||
|
</SessionProvider>
|
||||||
|
</Show>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
</MetaProvider>
|
</MetaProvider>
|
||||||
</LocalProvider>
|
</LayoutProvider>
|
||||||
</SyncProvider>
|
</GlobalSyncProvider>
|
||||||
</SDKProvider>
|
</GlobalSDKProvider>
|
||||||
</MarkedProvider>
|
</MarkedProvider>
|
||||||
),
|
),
|
||||||
root!,
|
root!,
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { createMemo, type ParentProps } from "solid-js"
|
||||||
|
import { useParams } from "@solidjs/router"
|
||||||
|
import { SDKProvider } from "@/context/sdk"
|
||||||
|
import { SyncProvider } from "@/context/sync"
|
||||||
|
import { LocalProvider } from "@/context/local"
|
||||||
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
|
import { base64Decode } from "@/utils"
|
||||||
|
|
||||||
|
export default function Layout(props: ParentProps) {
|
||||||
|
const params = useParams()
|
||||||
|
const sync = useGlobalSync()
|
||||||
|
const directory = createMemo(() => {
|
||||||
|
const decoded = base64Decode(params.dir)
|
||||||
|
return sync.data.projects.find((x) => x.worktree === decoded)?.worktree ?? "/"
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<SDKProvider directory={directory()}>
|
||||||
|
<SyncProvider>
|
||||||
|
<LocalProvider>{props.children}</LocalProvider>
|
||||||
|
</SyncProvider>
|
||||||
|
</SDKProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
|
import { base64Encode, getFilename } from "@/utils"
|
||||||
|
import { For } from "solid-js"
|
||||||
|
import { A } from "@solidjs/router"
|
||||||
|
import { Button } from "@opencode-ai/ui"
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const sync = useGlobalSync()
|
||||||
|
return (
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<For each={sync.data.projects}>
|
||||||
|
{(project) => (
|
||||||
|
<Button as={A} href={base64Encode(project.worktree)}>
|
||||||
|
{getFilename(project.worktree)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,116 +1,192 @@
|
|||||||
import { Button, Tooltip, DiffChanges, IconButton } from "@opencode-ai/ui"
|
import { Button, Tooltip, DiffChanges, IconButton, Mark, Icon, Collapsible } from "@opencode-ai/ui"
|
||||||
import { createMemo, For, ParentProps, Show } from "solid-js"
|
import { createMemo, For, ParentProps, Show } from "solid-js"
|
||||||
import { DateTime } from "luxon"
|
import { DateTime } from "luxon"
|
||||||
import { useSync } from "@/context/sync"
|
|
||||||
import { A, useParams } from "@solidjs/router"
|
import { A, useParams } from "@solidjs/router"
|
||||||
import { useLocal } from "@/context/local"
|
import { useLayout } from "@/context/layout"
|
||||||
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
|
import { base64Encode, getFilename } from "@/utils"
|
||||||
|
|
||||||
export default function Layout(props: ParentProps) {
|
export default function Layout(props: ParentProps) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const sync = useSync()
|
const globalSync = useGlobalSync()
|
||||||
const local = useLocal()
|
const layout = useLayout()
|
||||||
|
|
||||||
|
const handleOpenProject = async () => {
|
||||||
|
// layout.projects.open(dir.)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative h-screen flex flex-col">
|
<div class="relative h-screen flex flex-col">
|
||||||
<header class="hidden h-12 shrink-0 bg-background-strong border-b border-border-weak-base"></header>
|
<header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base">
|
||||||
<div class="h-[calc(100vh-0rem)] flex">
|
<A
|
||||||
|
href="/"
|
||||||
|
classList={{
|
||||||
|
"w-12 shrink-0 px-4 py-3.5": true,
|
||||||
|
"flex items-center justify-start self-stretch": true,
|
||||||
|
"border-r border-border-weak-base": true,
|
||||||
|
}}
|
||||||
|
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
|
||||||
|
>
|
||||||
|
<Mark class="shrink-0" />
|
||||||
|
</A>
|
||||||
|
</header>
|
||||||
|
<div class="h-[calc(100vh-3rem)] flex">
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
"@container w-14 pb-4 shrink-0 bg-background-weak": true,
|
"@container w-12 pb-5 shrink-0 bg-background-base": true,
|
||||||
"flex flex-col items-start self-stretch justify-between": true,
|
"flex flex-col gap-5.5 items-start self-stretch justify-between": true,
|
||||||
"border-r border-border-weak-base": true,
|
"border-r border-border-weak-base": true,
|
||||||
"w-70": local.layout.sidebar.opened(),
|
|
||||||
}}
|
}}
|
||||||
|
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col justify-center items-start gap-4 self-stretch py-2 overflow-hidden mx-auto @[4rem]:mx-0">
|
<div class="grow flex flex-col items-start self-stretch gap-4 p-2 min-h-0">
|
||||||
<div class="h-8 shrink-0 flex items-center self-stretch px-3">
|
<Tooltip class="shrink-0" placement="right" value="Toggle sidebar" inactive={layout.sidebar.opened()}>
|
||||||
<Tooltip placement="right" value="Collapse sidebar">
|
<Button
|
||||||
<IconButton icon="layout-left" variant="ghost" size="large" onClick={local.layout.sidebar.toggle} />
|
variant="ghost"
|
||||||
</Tooltip>
|
size="large"
|
||||||
</div>
|
class="group/sidebar-toggle shrink-0 w-full text-left justify-start"
|
||||||
<div class="w-full px-3">
|
onClick={layout.sidebar.toggle}
|
||||||
<Button as={A} href="/session" class="hidden @[4rem]:flex w-full" size="large" icon="edit-small-2">
|
>
|
||||||
New Session
|
<div class="relative -ml-px flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
||||||
|
<Icon
|
||||||
|
name={layout.sidebar.opened() ? "layout-left" : "layout-right"}
|
||||||
|
size="small"
|
||||||
|
class="group-hover/sidebar-toggle:hidden"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
name={layout.sidebar.opened() ? "layout-left-partial" : "layout-right-partial"}
|
||||||
|
size="small"
|
||||||
|
class="hidden group-hover/sidebar-toggle:inline-block"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
name={layout.sidebar.opened() ? "layout-left-full" : "layout-right-full"}
|
||||||
|
size="small"
|
||||||
|
class="hidden group-active/sidebar-toggle:inline-block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Show when={layout.sidebar.opened()}>
|
||||||
|
<div class="hidden group-hover/sidebar-toggle:block group-active/sidebar-toggle:block text-text-base">
|
||||||
|
Toggle sidebar
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</Button>
|
</Button>
|
||||||
<Tooltip placement="right" value="New session">
|
</Tooltip>
|
||||||
<IconButton as={A} href="/session" icon="edit-small-2" size="large" class="@[4rem]:hidden" />
|
<div class="flex flex-col justify-center items-start gap-4 self-stretch min-h-0">
|
||||||
</Tooltip>
|
<div class="hidden @[4rem]:flex size-full flex-col grow overflow-y-auto no-scrollbar">
|
||||||
</div>
|
<For each={layout.projects.list()}>
|
||||||
<div class="hidden @[4rem]:flex size-full overflow-y-auto no-scrollbar flex-col flex-1 px-3">
|
{(project) => {
|
||||||
<nav class="w-full">
|
const [store] = globalSync.child(project.directory)
|
||||||
<For each={sync.data.session}>
|
const slug = createMemo(() => base64Encode(project.directory))
|
||||||
{(session) => {
|
|
||||||
const updated = createMemo(() => DateTime.fromMillis(session.time.updated))
|
|
||||||
return (
|
return (
|
||||||
<A
|
<Collapsible variant="ghost" defaultOpen class="gap-2">
|
||||||
data-active={session.id === params.id}
|
<Button
|
||||||
href={`/session/${session.id}`}
|
as={"div"}
|
||||||
class="group/session focus:outline-none cursor-default"
|
variant="ghost"
|
||||||
>
|
class="flex items-center justify-between gap-3 w-full h-8 pl-2 pr-2.25 self-stretch"
|
||||||
<Tooltip placement="right" value={session.title}>
|
>
|
||||||
<div
|
<Collapsible.Trigger class="p-0 text-left text-14-medium text-text-strong grow min-w-0 truncate">
|
||||||
class="w-full mb-1.5 px-3 py-1 rounded-md
|
{getFilename(project.directory)}
|
||||||
group-data-[active=true]/session:bg-surface-raised-base-hover
|
</Collapsible.Trigger>
|
||||||
group-hover/session:bg-surface-raised-base-hover
|
<IconButton as={A} href={`${slug()}/session`} icon="plus-small" size="normal" />
|
||||||
group-focus/session:bg-surface-raised-base-hover"
|
</Button>
|
||||||
>
|
<Collapsible.Content>
|
||||||
<div class="flex items-center self-stretch gap-6 justify-between">
|
<nav class="w-full flex flex-col gap-1.5">
|
||||||
<span class="text-14-regular text-text-strong overflow-hidden text-ellipsis truncate">
|
<For each={store.session}>
|
||||||
{session.title}
|
{(session) => {
|
||||||
</span>
|
const updated = createMemo(() => DateTime.fromMillis(session.time.updated))
|
||||||
<span class="text-12-regular text-text-weak text-right whitespace-nowrap">
|
return (
|
||||||
{Math.abs(updated().diffNow().as("seconds")) < 60
|
<A
|
||||||
? "Now"
|
data-active={session.id === params.id}
|
||||||
: updated()
|
href={`${slug()}/session/${session.id}`}
|
||||||
.toRelative({ style: "short", unit: ["days", "hours", "minutes"] })
|
class="group/session focus:outline-none cursor-default"
|
||||||
?.replace(" ago", "")
|
>
|
||||||
?.replace(/ days?/, "d")
|
<Tooltip placement="right" value={session.title}>
|
||||||
?.replace(" min.", "m")
|
<div
|
||||||
?.replace(" hr.", "h")}
|
class="w-full px-2 py-1 rounded-md
|
||||||
</span>
|
group-data-[active=true]/session:bg-surface-raised-base-hover
|
||||||
</div>
|
group-hover/session:bg-surface-raised-base-hover
|
||||||
<div class="flex justify-between items-center self-stretch">
|
group-focus/session:bg-surface-raised-base-hover"
|
||||||
<span class="text-12-regular text-text-weak">{`${session.summary?.files || "No"} file${session.summary?.files !== 1 ? "s" : ""} changed`}</span>
|
>
|
||||||
<Show when={session.summary}>{(summary) => <DiffChanges changes={summary()} />}</Show>
|
<div class="flex items-center self-stretch gap-6 justify-between">
|
||||||
</div>
|
<span class="text-14-regular text-text-strong overflow-hidden text-ellipsis truncate">
|
||||||
</div>
|
{session.title}
|
||||||
</Tooltip>
|
</span>
|
||||||
</A>
|
<span class="text-12-regular text-text-weak text-right whitespace-nowrap">
|
||||||
|
{Math.abs(updated().diffNow().as("seconds")) < 60
|
||||||
|
? "Now"
|
||||||
|
: updated()
|
||||||
|
.toRelative({ style: "short", unit: ["days", "hours", "minutes"] })
|
||||||
|
?.replace(" ago", "")
|
||||||
|
?.replace(/ days?/, "d")
|
||||||
|
?.replace(" min.", "m")
|
||||||
|
?.replace(" hr.", "h")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="hidden _flex justify-between items-center self-stretch">
|
||||||
|
<span class="text-12-regular text-text-weak">{`${session.summary?.files || "No"} file${session.summary?.files !== 1 ? "s" : ""} changed`}</span>
|
||||||
|
<Show when={session.summary}>
|
||||||
|
{(summary) => <DiffChanges changes={summary()} />}
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</A>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</nav>
|
||||||
|
{/* <Show when={sync.session.more()}> */}
|
||||||
|
{/* <button */}
|
||||||
|
{/* class="shrink-0 self-start p-3 text-12-medium text-text-weak hover:text-text-strong" */}
|
||||||
|
{/* onClick={() => sync.session.fetch()} */}
|
||||||
|
{/* > */}
|
||||||
|
{/* Show more */}
|
||||||
|
{/* </button> */}
|
||||||
|
{/* </Show> */}
|
||||||
|
</Collapsible.Content>
|
||||||
|
</Collapsible>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</For>
|
</For>
|
||||||
</nav>
|
</div>
|
||||||
<Show when={sync.session.more()}>
|
|
||||||
<button
|
|
||||||
class="shrink-0 self-start p-3 text-12-medium text-text-weak hover:text-text-strong"
|
|
||||||
onClick={() => sync.session.fetch()}
|
|
||||||
>
|
|
||||||
Show more
|
|
||||||
</button>
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-start shrink-0 px-3 py-1 mx-auto @[4rem]:mx-0">
|
<div class="flex flex-col gap-1.5 self-stretch items-start shrink-0 px-2 py-3">
|
||||||
<Button
|
<Tooltip placement="right" value="Open project" inactive={layout.sidebar.opened()}>
|
||||||
as={"a"}
|
<Button
|
||||||
href="https://opencode.ai/desktop-feedback"
|
disabled
|
||||||
target="_blank"
|
class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px]"
|
||||||
class="hidden @[4rem]:flex w-full text-12-medium text-text-base stroke-[1.5px]"
|
variant="ghost"
|
||||||
variant="ghost"
|
size="large"
|
||||||
icon="speech-bubble"
|
icon="folder-add-left"
|
||||||
>
|
onClick={handleOpenProject}
|
||||||
Share feedback
|
>
|
||||||
</Button>
|
<Show when={layout.sidebar.opened()}>Open project</Show>
|
||||||
<Tooltip placement="right" value="Share feedback">
|
</Button>
|
||||||
<IconButton
|
</Tooltip>
|
||||||
|
<Tooltip placement="right" value="Settings" inactive={layout.sidebar.opened()}>
|
||||||
|
<Button
|
||||||
|
disabled
|
||||||
|
class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px]"
|
||||||
|
variant="ghost"
|
||||||
|
size="large"
|
||||||
|
icon="settings-gear"
|
||||||
|
>
|
||||||
|
<Show when={layout.sidebar.opened()}>Settings</Show>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip placement="right" value="Share feedback" inactive={layout.sidebar.opened()}>
|
||||||
|
<Button
|
||||||
as={"a"}
|
as={"a"}
|
||||||
href="https://opencode.ai/desktop-feedback"
|
href="https://opencode.ai/desktop-feedback"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
icon="speech-bubble"
|
class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px]"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="large"
|
size="large"
|
||||||
class="@[4rem]:hidden stroke-[1.5px]"
|
icon="bubble-5"
|
||||||
/>
|
>
|
||||||
|
<Show when={layout.sidebar.opened()}>Share feedback</Show>
|
||||||
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Show, type ParentProps } from "solid-js"
|
|
||||||
import { SessionProvider } from "@/context/session"
|
|
||||||
import { useParams } from "@solidjs/router"
|
|
||||||
|
|
||||||
export default function Layout(props: ParentProps) {
|
|
||||||
const params = useParams()
|
|
||||||
return (
|
|
||||||
<Show when={params.id || true} keyed>
|
|
||||||
<SessionProvider sessionId={params.id}>{props.children}</SessionProvider>
|
|
||||||
</Show>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
Code,
|
Code,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
ProgressCircle,
|
ProgressCircle,
|
||||||
Button,
|
|
||||||
} from "@opencode-ai/ui"
|
} from "@opencode-ai/ui"
|
||||||
import { FileIcon } from "@/ui"
|
import { FileIcon } from "@/ui"
|
||||||
import { MessageProgress } from "@/components/message-progress"
|
import { MessageProgress } from "@/components/message-progress"
|
||||||
@@ -50,8 +49,13 @@ import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
|
|||||||
import { Markdown } from "@opencode-ai/ui"
|
import { Markdown } from "@opencode-ai/ui"
|
||||||
import { Spinner } from "@/components/spinner"
|
import { Spinner } from "@/components/spinner"
|
||||||
import { useSession } from "@/context/session"
|
import { useSession } from "@/context/session"
|
||||||
|
import { StickyAccordionHeader } from "@/components/sticky-accordion-header"
|
||||||
|
import { SessionReview } from "@/components/session-review"
|
||||||
|
import { useLayout } from "@/context/layout"
|
||||||
|
import { createSessionSeen } from "@/hooks/create-session-seen"
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const layout = useLayout()
|
||||||
const local = useLocal()
|
const local = useLocal()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const session = useSession()
|
const session = useSession()
|
||||||
@@ -83,6 +87,15 @@ export default function Page() {
|
|||||||
setStore("fileSelectOpen", true)
|
setStore("fileSelectOpen", true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (event.ctrlKey && event.key.toLowerCase() === "t") {
|
||||||
|
event.preventDefault()
|
||||||
|
const currentTheme = localStorage.getItem("theme") ?? "oc-1"
|
||||||
|
const themes = ["oc-1", "oc-2-paper"]
|
||||||
|
const nextTheme = themes[(themes.indexOf(currentTheme) + 1) % themes.length]
|
||||||
|
localStorage.setItem("theme", nextTheme)
|
||||||
|
document.documentElement.setAttribute("data-theme", nextTheme)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const focused = document.activeElement === inputRef
|
const focused = document.activeElement === inputRef
|
||||||
if (focused) {
|
if (focused) {
|
||||||
@@ -165,10 +178,16 @@ export default function Page() {
|
|||||||
setStore("activeDraggable", undefined)
|
setStore("activeDraggable", undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileVisual = (props: { file: LocalFile }): JSX.Element => {
|
const FileVisual = (props: { file: LocalFile; active?: boolean }): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div class="flex items-center gap-x-1.5">
|
<div class="flex items-center gap-x-1.5">
|
||||||
<FileIcon node={props.file} class="grayscale-100 group-data-[selected]/tab:grayscale-0" />
|
<FileIcon
|
||||||
|
node={props.file}
|
||||||
|
classList={{
|
||||||
|
"grayscale-100 group-data-[selected]/tab:grayscale-0": !props.active,
|
||||||
|
"grayscale-0": props.active,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<span
|
<span
|
||||||
classList={{
|
classList={{
|
||||||
"text-14-medium": true,
|
"text-14-medium": true,
|
||||||
@@ -216,18 +235,15 @@ export default function Page() {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<div use:sortable classList={{ "h-full": true, "opacity-0": sortable.isActiveDraggable }}>
|
<div use:sortable classList={{ "h-full": true, "opacity-0": sortable.isActiveDraggable }}>
|
||||||
<div class="relative h-full">
|
<div class="relative h-full">
|
||||||
<Tabs.Trigger value={props.tab} class="group/tab pl-3 pr-1" onClick={() => props.onTabClick(props.tab)}>
|
<Tabs.Trigger
|
||||||
|
value={props.tab}
|
||||||
|
closeButton={<IconButton icon="close" variant="ghost" onClick={() => props.onTabClose(props.tab)} />}
|
||||||
|
hideCloseButton
|
||||||
|
onClick={() => props.onTabClick(props.tab)}
|
||||||
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={file()}>{(f) => <FileVisual file={f()} />}</Match>
|
<Match when={file()}>{(f) => <FileVisual file={f()} />}</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
<IconButton
|
|
||||||
icon="close"
|
|
||||||
class="mt-0.5 opacity-0 group-data-[selected]/tab:opacity-100
|
|
||||||
hover:bg-transparent
|
|
||||||
hover:opacity-100 group-hover/tab:opacity-100"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => props.onTabClose(props.tab)}
|
|
||||||
/>
|
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -277,38 +293,40 @@ export default function Page() {
|
|||||||
<Tabs value={session.layout.tabs.active ?? "chat"} onChange={session.layout.openTab}>
|
<Tabs value={session.layout.tabs.active ?? "chat"} onChange={session.layout.openTab}>
|
||||||
<div class="sticky top-0 shrink-0 flex">
|
<div class="sticky top-0 shrink-0 flex">
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
<Tabs.Trigger value="chat" class="flex gap-x-4 items-center">
|
<Tabs.Trigger value="chat">
|
||||||
<div>Chat</div>
|
<div class="flex gap-x-[17px] items-center">
|
||||||
<Tooltip
|
<div>Session</div>
|
||||||
value={`${new Intl.NumberFormat("en-US", {
|
<Tooltip
|
||||||
notation: "compact",
|
value={`${new Intl.NumberFormat("en-US", {
|
||||||
compactDisplay: "short",
|
notation: "compact",
|
||||||
}).format(session.usage.tokens() ?? 0)} Tokens`}
|
compactDisplay: "short",
|
||||||
class="flex items-center gap-1.5"
|
}).format(session.usage.tokens() ?? 0)} Tokens`}
|
||||||
>
|
class="flex items-center gap-1.5"
|
||||||
<ProgressCircle percentage={session.usage.context() ?? 0} />
|
>
|
||||||
<div class="text-14-regular text-text-weak text-left w-7">{session.usage.context() ?? 0}%</div>
|
<ProgressCircle percentage={session.usage.context() ?? 0} />
|
||||||
</Tooltip>
|
<div class="text-14-regular text-text-weak text-left w-7">{session.usage.context() ?? 0}%</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
<Show when={local.layout.review.state() === "tab" && session.diffs().length}>
|
<Show when={layout.review.state() === "tab" && session.diffs().length}>
|
||||||
<Tabs.Trigger value="review" class="flex gap-3 items-center group/tab pr-1">
|
<Tabs.Trigger
|
||||||
<Show when={session.diffs()}>
|
value="review"
|
||||||
<DiffChanges changes={session.diffs()} variant="bars" />
|
closeButton={
|
||||||
</Show>
|
<IconButton icon="collapse" size="normal" variant="ghost" onClick={layout.review.pane} />
|
||||||
<div class="flex items-center gap-1.5">
|
}
|
||||||
<div>Review</div>
|
>
|
||||||
<Show when={session.info()?.summary?.files}>
|
<div class="flex items-center gap-3">
|
||||||
<div class="text-12-medium text-text-strong h-4 px-2 flex flex-col items-center justify-center rounded-full bg-surface-base">
|
<Show when={session.diffs()}>
|
||||||
{session.info()?.summary?.files ?? 0}
|
<DiffChanges changes={session.diffs()} variant="bars" />
|
||||||
</div>
|
|
||||||
</Show>
|
</Show>
|
||||||
<IconButton
|
<div class="flex items-center gap-1.5">
|
||||||
icon="close"
|
<div>Review</div>
|
||||||
class="mt-0.5 -ml-1 opacity-0 group-data-[selected]/tab:opacity-100
|
<Show when={session.info()?.summary?.files}>
|
||||||
hover:bg-transparent hover:opacity-100 group-hover/tab:opacity-100"
|
<div class="text-12-medium text-text-strong h-4 px-2 flex flex-col items-center justify-center rounded-full bg-surface-base">
|
||||||
variant="ghost"
|
{session.info()?.summary?.files ?? 0}
|
||||||
onClick={local.layout.review.close}
|
</div>
|
||||||
/>
|
</Show>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -332,115 +350,111 @@ export default function Page() {
|
|||||||
<Tabs.Content value="chat" class="@container select-text flex flex-col flex-1 min-h-0 overflow-y-hidden">
|
<Tabs.Content value="chat" class="@container select-text flex flex-col flex-1 min-h-0 overflow-y-hidden">
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
"w-full grid flex-1 min-h-0": true,
|
"w-full flex-1 min-h-0": true,
|
||||||
"grid-cols-2": local.layout.review.state() === "open",
|
grid: layout.review.state() === "tab",
|
||||||
|
flex: layout.review.state() === "pane",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0 max-w-2xl mx-auto">
|
<div class="relative shrink-0 px-6 py-3 flex flex-col gap-6 flex-1 min-h-0 w-full max-w-xl mx-auto">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={session.id}>
|
<Match when={session.id}>
|
||||||
<div class="h-8 flex shrink-0 self-stretch items-center justify-end">
|
|
||||||
<Show when={local.layout.review.state() === "closed" && session.diffs().length}>
|
|
||||||
<Button icon="layout-right" onClick={local.layout.review.open}>
|
|
||||||
Review
|
|
||||||
</Button>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
"flex-1 min-h-0 pb-20": true,
|
"flex-1 min-h-0 pb-20": true,
|
||||||
"flex items-start justify-start": local.layout.review.state() === "open",
|
"flex items-start justify-start": layout.review.state() === "pane",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={session.messages.user().length > 1}>
|
<Show when={session.messages.user().length > 1}>
|
||||||
<ul
|
{(_) => {
|
||||||
role="list"
|
const expanded = createMemo(() => layout.review.state() === "tab" || !session.diffs().length)
|
||||||
classList={{
|
|
||||||
"mr-8 shrink-0 flex flex-col items-start": true,
|
|
||||||
"absolute right-full w-60 @7xl:gap-2": true, // local.layout.review.state() !== "open",
|
|
||||||
"": local.layout.review.state() === "open",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<For each={session.messages.user()}>
|
|
||||||
{(message) => {
|
|
||||||
const assistantMessages = createMemo(() => {
|
|
||||||
if (!session.id) return []
|
|
||||||
return sync.data.message[session.id]?.filter(
|
|
||||||
(m) => m.role === "assistant" && m.parentID == message.id,
|
|
||||||
) as AssistantMessageType[]
|
|
||||||
})
|
|
||||||
const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error)
|
|
||||||
const working = createMemo(() => !message.summary?.body && !error())
|
|
||||||
|
|
||||||
const handleClick = () => session.messages.setActive(message.id)
|
return (
|
||||||
|
<ul
|
||||||
|
role="list"
|
||||||
|
classList={{
|
||||||
|
"mr-8 shrink-0 flex flex-col items-start": true,
|
||||||
|
"absolute right-full w-60 mt-3 @7xl:gap-2 @7xl:mt-1": expanded(),
|
||||||
|
"mt-3": !expanded(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<For each={session.messages.user()}>
|
||||||
|
{(message) => {
|
||||||
|
const working = createMemo(
|
||||||
|
() => message.id === session.messages.last()?.id && session.working(),
|
||||||
|
)
|
||||||
|
const handleClick = () => session.messages.setActive(message.id)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
classList={{
|
|
||||||
"group/li flex items-center self-stretch justify-end": true,
|
|
||||||
"@7xl:justify-start": local.layout.review.state() !== "open",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Tooltip
|
|
||||||
placement="right"
|
|
||||||
gutter={8}
|
|
||||||
value={
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<DiffChanges changes={message.summary?.diffs ?? []} variant="bars" />
|
|
||||||
{message.summary?.title}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
data-active={session.messages.active()?.id === message.id}
|
|
||||||
onClick={handleClick}
|
|
||||||
classList={{
|
classList={{
|
||||||
"group/tick flex items-center justify-start h-2 w-8 -mr-3": true,
|
"group/li flex items-center self-stretch justify-end": true,
|
||||||
"data-[active=true]:[&>div]:bg-icon-strong-base data-[active=true]:[&>div]:w-full": true,
|
"@7xl:justify-start": expanded(),
|
||||||
"@7xl:hidden": local.layout.review.state() !== "open",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="h-px w-5 bg-icon-base group-hover/tick:w-full group-hover/tick:bg-icon-strong-base" />
|
<Tooltip
|
||||||
</button>
|
placement="right"
|
||||||
</Tooltip>
|
gutter={8}
|
||||||
<button
|
value={
|
||||||
classList={{
|
<div class="flex items-center gap-2">
|
||||||
"hidden items-center self-stretch w-full gap-x-2 cursor-default": true,
|
<DiffChanges changes={message.summary?.diffs ?? []} variant="bars" />
|
||||||
"@7xl:flex": local.layout.review.state() !== "open",
|
{message.summary?.title}
|
||||||
}}
|
</div>
|
||||||
onClick={handleClick}
|
}
|
||||||
>
|
>
|
||||||
<Switch>
|
<button
|
||||||
<Match when={working()}>
|
data-active={session.messages.active()?.id === message.id}
|
||||||
<Spinner class="text-text-base shrink-0 w-[18px] aspect-square" />
|
onClick={handleClick}
|
||||||
</Match>
|
classList={{
|
||||||
<Match when={true}>
|
"group/tick flex items-center justify-start h-2 w-8 -mr-3": true,
|
||||||
<DiffChanges changes={message.summary?.diffs ?? []} variant="bars" />
|
"data-[active=true]:[&>div]:bg-icon-strong-base data-[active=true]:[&>div]:w-full": true,
|
||||||
</Match>
|
"@7xl:hidden": expanded(),
|
||||||
</Switch>
|
}}
|
||||||
<div
|
>
|
||||||
data-active={session.messages.active()?.id === message.id}
|
<div class="h-px w-5 bg-icon-base group-hover/tick:w-full group-hover/tick:bg-icon-strong-base" />
|
||||||
classList={{
|
</button>
|
||||||
"text-14-regular text-text-weak whitespace-nowrap truncate min-w-0": true,
|
</Tooltip>
|
||||||
"text-text-weak data-[active=true]:text-text-strong group-hover/li:text-text-base": true,
|
<button
|
||||||
}}
|
classList={{
|
||||||
>
|
"hidden items-center self-stretch w-full gap-x-2 cursor-default": true,
|
||||||
<Show when={message.summary?.title} fallback="New message">
|
"@7xl:flex": expanded(),
|
||||||
{message.summary?.title}
|
}}
|
||||||
</Show>
|
onClick={handleClick}
|
||||||
</div>
|
>
|
||||||
</button>
|
<Switch>
|
||||||
</li>
|
<Match when={working()}>
|
||||||
)
|
<Spinner class="text-text-base shrink-0 w-[18px] aspect-square" />
|
||||||
}}
|
</Match>
|
||||||
</For>
|
<Match when={true}>
|
||||||
</ul>
|
<DiffChanges changes={message.summary?.diffs ?? []} variant="bars" />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
<div
|
||||||
|
data-active={session.messages.active()?.id === message.id}
|
||||||
|
classList={{
|
||||||
|
"text-14-regular text-text-weak whitespace-nowrap truncate min-w-0": true,
|
||||||
|
"text-text-weak data-[active=true]:text-text-strong group-hover/li:text-text-base": true,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Show when={message.summary?.title} fallback="New message">
|
||||||
|
{message.summary?.title}
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}}
|
||||||
</Show>
|
</Show>
|
||||||
<div ref={messageScrollElement} class="grow w-full min-w-0 h-full overflow-y-auto no-scrollbar">
|
<div ref={messageScrollElement} class="grow size-full min-w-0 overflow-y-auto no-scrollbar">
|
||||||
<For each={session.messages.user()}>
|
<For each={session.messages.user()}>
|
||||||
{(message) => {
|
{(message) => {
|
||||||
const isActive = createMemo(() => session.messages.active()?.id === message.id)
|
const isActive = createMemo(() => session.messages.active()?.id === message.id)
|
||||||
const [titled, setTitled] = createSignal(!!message.summary?.title)
|
const titleSeen = createSessionSeen(`message-title-${message.id}`)
|
||||||
|
const contentSeen = createSessionSeen(`message-content-${message.id}`)
|
||||||
|
const [titled, setTitled] = createSignal(titleSeen())
|
||||||
const assistantMessages = createMemo(() => {
|
const assistantMessages = createMemo(() => {
|
||||||
if (!session.id) return []
|
if (!session.id) return []
|
||||||
return sync.data.message[session.id]?.filter(
|
return sync.data.message[session.id]?.filter(
|
||||||
@@ -448,7 +462,6 @@ export default function Page() {
|
|||||||
) as AssistantMessageType[]
|
) as AssistantMessageType[]
|
||||||
})
|
})
|
||||||
const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error)
|
const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error)
|
||||||
const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error())
|
|
||||||
const [detailsExpanded, setDetailsExpanded] = createSignal(false)
|
const [detailsExpanded, setDetailsExpanded] = createSignal(false)
|
||||||
const parts = createMemo(() => sync.data.part[message.id])
|
const parts = createMemo(() => sync.data.part[message.id])
|
||||||
const hasToolPart = createMemo(() =>
|
const hasToolPart = createMemo(() =>
|
||||||
@@ -456,17 +469,21 @@ export default function Page() {
|
|||||||
?.flatMap((m) => sync.data.part[m.id])
|
?.flatMap((m) => sync.data.part[m.id])
|
||||||
.some((p) => p?.type === "tool"),
|
.some((p) => p?.type === "tool"),
|
||||||
)
|
)
|
||||||
const working = createMemo(() => !message.summary?.body && !error())
|
const working = createMemo(
|
||||||
|
() => message.id === session.messages.last()?.id && session.working(),
|
||||||
|
)
|
||||||
|
const initialCompleted = !(message.id === session.messages.last()?.id && session.working())
|
||||||
|
const [completed, setCompleted] = createSignal(initialCompleted)
|
||||||
|
|
||||||
// allowing time for the animations to finish
|
// allowing time for the animations to finish
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
if (titleSeen()) return
|
||||||
const title = message.summary?.title
|
const title = message.summary?.title
|
||||||
setTimeout(() => setTitled(!!title), 10_000)
|
if (title) setTimeout(() => setTitled(true), 10_000)
|
||||||
})
|
})
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const summary = message.summary?.body
|
const completed = !working()
|
||||||
const complete = !!summary || !!error()
|
setTimeout(() => setCompleted(completed), 1200)
|
||||||
setTimeout(() => setCompleted(complete), 1200)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -476,7 +493,7 @@ export default function Page() {
|
|||||||
class="flex flex-col items-start self-stretch gap-8 pb-20"
|
class="flex flex-col items-start self-stretch gap-8 pb-20"
|
||||||
>
|
>
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<div class="flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10 pb-1">
|
<div class="flex items-center gap-2 self-stretch sticky top-0 bg-background-stronger z-20 h-8">
|
||||||
<div class="w-full text-14-medium text-text-strong">
|
<div class="w-full text-14-medium text-text-strong">
|
||||||
<Show
|
<Show
|
||||||
when={titled()}
|
when={titled()}
|
||||||
@@ -494,9 +511,7 @@ export default function Page() {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="-mt-9">
|
<Message message={message} parts={parts()} />
|
||||||
<Message message={message} parts={parts()} />
|
|
||||||
</div>
|
|
||||||
{/* Summary */}
|
{/* Summary */}
|
||||||
<Show when={completed()}>
|
<Show when={completed()}>
|
||||||
<div class="w-full flex flex-col gap-6 items-start self-stretch">
|
<div class="w-full flex flex-col gap-6 items-start self-stretch">
|
||||||
@@ -512,7 +527,7 @@ export default function Page() {
|
|||||||
<Markdown
|
<Markdown
|
||||||
classList={{
|
classList={{
|
||||||
"text-14-regular": !!message.summary?.diffs?.length,
|
"text-14-regular": !!message.summary?.diffs?.length,
|
||||||
"[&>*]:fade-up-text": !message.summary?.diffs?.length,
|
"[&>*]:fade-up-text": !message.summary?.diffs?.length && !contentSeen(),
|
||||||
}}
|
}}
|
||||||
text={summary()}
|
text={summary()}
|
||||||
/>
|
/>
|
||||||
@@ -523,7 +538,7 @@ export default function Page() {
|
|||||||
<For each={message.summary?.diffs ?? []}>
|
<For each={message.summary?.diffs ?? []}>
|
||||||
{(diff) => (
|
{(diff) => (
|
||||||
<Accordion.Item value={diff.file}>
|
<Accordion.Item value={diff.file}>
|
||||||
<Accordion.Header>
|
<StickyAccordionHeader class="top-10 data-expanded:before:-top-10">
|
||||||
<Accordion.Trigger>
|
<Accordion.Trigger>
|
||||||
<div class="flex items-center justify-between w-full gap-5">
|
<div class="flex items-center justify-between w-full gap-5">
|
||||||
<div class="grow flex items-center gap-5 min-w-0">
|
<div class="grow flex items-center gap-5 min-w-0">
|
||||||
@@ -548,8 +563,8 @@ export default function Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Accordion.Trigger>
|
</Accordion.Trigger>
|
||||||
</Accordion.Header>
|
</StickyAccordionHeader>
|
||||||
<Accordion.Content class="max-h-[360px] overflow-y-auto no-scrollbar">
|
<Accordion.Content class="max-h-60 overflow-y-auto no-scrollbar">
|
||||||
<Diff
|
<Diff
|
||||||
before={{
|
before={{
|
||||||
name: diff.file!,
|
name: diff.file!,
|
||||||
@@ -652,130 +667,25 @@ export default function Page() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Show when={local.layout.review.state() === "open"}>
|
<Show when={layout.review.state() === "pane" && session.diffs().length}>
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
"relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0 border-l border-border-weak-base": true,
|
"relative grow px-6 py-3 flex-1 min-h-0 border-l border-border-weak-base": true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="h-8 w-full flex items-center justify-between shrink-0 self-stretch">
|
<SessionReview />
|
||||||
<div class="flex items-center gap-x-3">
|
|
||||||
<Tooltip value="Close">
|
|
||||||
<IconButton icon="align-right" variant="ghost" onClick={local.layout.review.close} />
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip value="Open in tab">
|
|
||||||
<IconButton
|
|
||||||
icon="expand"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
local.layout.review.tab()
|
|
||||||
session.layout.setActiveTab("review")
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-14-medium text-text-strong">All changes</div>
|
|
||||||
<div class="h-full pb-40 overflow-y-auto no-scrollbar">
|
|
||||||
<Accordion class="w-full" multiple>
|
|
||||||
<For each={session.diffs()}>
|
|
||||||
{(diff) => (
|
|
||||||
<Accordion.Item value={diff.file} defaultOpen>
|
|
||||||
<Accordion.Header>
|
|
||||||
<Accordion.Trigger>
|
|
||||||
<div class="flex items-center justify-between w-full gap-5">
|
|
||||||
<div class="grow flex items-center gap-5 min-w-0">
|
|
||||||
<FileIcon node={{ path: diff.file, type: "file" }} class="shrink-0 size-4" />
|
|
||||||
<div class="flex grow min-w-0">
|
|
||||||
<Show when={diff.file.includes("/")}>
|
|
||||||
<span class="text-text-base truncate-start">
|
|
||||||
{getDirectory(diff.file)}‎
|
|
||||||
</span>
|
|
||||||
</Show>
|
|
||||||
<span class="text-text-strong shrink-0">{getFilename(diff.file)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shrink-0 flex gap-4 items-center justify-end">
|
|
||||||
<DiffChanges changes={diff} />
|
|
||||||
<Icon name="chevron-grabber-vertical" size="small" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Accordion.Trigger>
|
|
||||||
</Accordion.Header>
|
|
||||||
<Accordion.Content>
|
|
||||||
<Diff
|
|
||||||
before={{
|
|
||||||
name: diff.file!,
|
|
||||||
contents: diff.before!,
|
|
||||||
}}
|
|
||||||
after={{
|
|
||||||
name: diff.file!,
|
|
||||||
contents: diff.after!,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Accordion.Content>
|
|
||||||
</Accordion.Item>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
<Show when={local.layout.review.state() === "tab" && session.diffs().length}>
|
<Show when={layout.review.state() === "tab" && session.diffs().length}>
|
||||||
<Tabs.Content value="review" class="select-text">
|
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
"relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0": true,
|
"relative px-6 py-3 flex-1 min-h-0 overflow-hidden": true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="h-8 w-full flex items-center justify-between shrink-0 self-stretch sticky top-0 bg-background-stronger z-100">
|
<SessionReview split hideExpand class="pb-40" />
|
||||||
<div class="flex items-center gap-x-3"></div>
|
|
||||||
</div>
|
|
||||||
<div class="text-14-medium text-text-strong">All changes</div>
|
|
||||||
<div class="h-full pb-40 overflow-y-auto no-scrollbar">
|
|
||||||
<Accordion class="w-full" multiple>
|
|
||||||
<For each={session.diffs()}>
|
|
||||||
{(diff) => (
|
|
||||||
<Accordion.Item value={diff.file} defaultOpen>
|
|
||||||
<Accordion.Header>
|
|
||||||
<Accordion.Trigger>
|
|
||||||
<div class="flex items-center justify-between w-full gap-5">
|
|
||||||
<div class="grow flex items-center gap-5 min-w-0">
|
|
||||||
<FileIcon node={{ path: diff.file, type: "file" }} class="shrink-0 size-4" />
|
|
||||||
<div class="flex grow min-w-0">
|
|
||||||
<Show when={diff.file.includes("/")}>
|
|
||||||
<span class="text-text-base truncate-start">{getDirectory(diff.file)}‎</span>
|
|
||||||
</Show>
|
|
||||||
<span class="text-text-strong shrink-0">{getFilename(diff.file)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shrink-0 flex gap-4 items-center justify-end">
|
|
||||||
<DiffChanges changes={diff} />
|
|
||||||
<Icon name="chevron-grabber-vertical" size="small" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Accordion.Trigger>
|
|
||||||
</Accordion.Header>
|
|
||||||
<Accordion.Content>
|
|
||||||
<Diff
|
|
||||||
diffStyle="split"
|
|
||||||
before={{
|
|
||||||
name: diff.file!,
|
|
||||||
contents: diff.before!,
|
|
||||||
}}
|
|
||||||
after={{
|
|
||||||
name: diff.file!,
|
|
||||||
contents: diff.after!,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Accordion.Content>
|
|
||||||
</Accordion.Item>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -821,8 +731,8 @@ export default function Page() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<div class="relative px-3 h-10 flex items-center bg-background-base border-x border-border-weak-base border-b border-b-transparent">
|
<div class="relative px-6 h-12 flex items-center bg-background-stronger border-x border-border-weak-base border-b border-b-transparent">
|
||||||
<Show when={file()}>{(f) => <FileVisual file={f()} />}</Show>
|
<Show when={file()}>{(f) => <FileVisual active file={f()} />}</Show>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -830,7 +740,7 @@ export default function Page() {
|
|||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
</DragDropProvider>
|
</DragDropProvider>
|
||||||
<Show when={session.layout.tabs.active}>
|
<Show when={session.layout.tabs.active}>
|
||||||
<div class="absolute inset-x-0 px-6 max-w-2xl flex flex-col justify-center items-center z-50 mx-auto bottom-8">
|
<div class="absolute inset-x-0 px-6 max-w-2xl flex flex-col justify-center items-center z-50 mx-auto bottom-6">
|
||||||
<PromptInput
|
<PromptInput
|
||||||
ref={(el) => {
|
ref={(el) => {
|
||||||
inputRef = el
|
inputRef = el
|
||||||
@@ -872,7 +782,13 @@ export default function Page() {
|
|||||||
items={local.file.searchFiles}
|
items={local.file.searchFiles}
|
||||||
key={(x) => x}
|
key={(x) => x}
|
||||||
onOpenChange={(open) => setStore("fileSelectOpen", open)}
|
onOpenChange={(open) => setStore("fileSelectOpen", open)}
|
||||||
onSelect={(x) => (x ? session.layout.openTab("file://" + x) : undefined)}
|
onSelect={(x) => {
|
||||||
|
if (x) {
|
||||||
|
local.file.open(x)
|
||||||
|
return session.layout.openTab("file://" + x)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ export type FileIconProps = JSX.GSVGAttributes<SVGSVGElement> & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const FileIcon: Component<FileIconProps> = (props) => {
|
export const FileIcon: Component<FileIconProps> = (props) => {
|
||||||
const [local, rest] = splitProps(props, ["node", "class", "expanded"])
|
const [local, rest] = splitProps(props, ["node", "class", "classList", "expanded"])
|
||||||
const name = createMemo(() => chooseIconName(local.node.path, local.node.type, local.expanded || false))
|
const name = createMemo(() => chooseIconName(local.node.path, local.node.type, local.expanded || false))
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
{...rest}
|
{...rest}
|
||||||
classList={{
|
classList={{
|
||||||
|
...(local.classList ?? {}),
|
||||||
"shrink-0 size-4": true,
|
"shrink-0 size-4": true,
|
||||||
[local.class ?? ""]: !!local.class,
|
[local.class ?? ""]: !!local.class,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export function base64Encode(value: string) {
|
||||||
|
return btoa(value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function base64Decode(value: string) {
|
||||||
|
return atob(value.replace(/-/g, "+").replace(/_/g, "/"))
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./path"
|
export * from "./path"
|
||||||
export * from "./dom"
|
export * from "./dom"
|
||||||
|
export * from "./encode"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
id = "opencode"
|
id = "opencode"
|
||||||
name = "OpenCode"
|
name = "OpenCode"
|
||||||
description = "The AI coding agent built for the terminal"
|
description = "The AI coding agent built for the terminal"
|
||||||
version = "1.0.47"
|
version = "1.0.85"
|
||||||
schema_version = 1
|
schema_version = 1
|
||||||
authors = ["Anomaly"]
|
authors = ["Anomaly"]
|
||||||
repository = "https://github.com/sst/opencode"
|
repository = "https://github.com/sst/opencode"
|
||||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
|||||||
icon = "./icons/opencode.svg"
|
icon = "./icons/opencode.svg"
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-aarch64]
|
[agent_servers.opencode.targets.darwin-aarch64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.47/opencode-darwin-arm64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-darwin-arm64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-x86_64]
|
[agent_servers.opencode.targets.darwin-x86_64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.47/opencode-darwin-x64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-darwin-x64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-aarch64]
|
[agent_servers.opencode.targets.linux-aarch64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.47/opencode-linux-arm64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-linux-arm64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-x86_64]
|
[agent_servers.opencode.targets.linux-x86_64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.47/opencode-linux-x64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-linux-x64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.windows-x86_64]
|
[agent_servers.opencode.targets.windows-x86_64]
|
||||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.47/opencode-windows-x64.zip"
|
archive = "https://github.com/sst/opencode/releases/download/v1.0.85/opencode-windows-x64.zip"
|
||||||
cmd = "./opencode.exe"
|
cmd = "./opencode.exe"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,61 +1,84 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env node
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ -n "$OPENCODE_BIN_PATH" ]; then
|
const childProcess = require("child_process")
|
||||||
resolved="$OPENCODE_BIN_PATH"
|
const fs = require("fs")
|
||||||
else
|
const path = require("path")
|
||||||
# Get the real path of this script, resolving any symlinks
|
const os = require("os")
|
||||||
script_path="$0"
|
|
||||||
while [ -L "$script_path" ]; do
|
|
||||||
link_target="$(readlink "$script_path")"
|
|
||||||
case "$link_target" in
|
|
||||||
/*) script_path="$link_target" ;;
|
|
||||||
*) script_path="$(dirname "$script_path")/$link_target" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
script_dir="$(dirname "$script_path")"
|
|
||||||
script_dir="$(cd "$script_dir" && pwd)"
|
|
||||||
|
|
||||||
# Map platform names
|
|
||||||
case "$(uname -s)" in
|
|
||||||
Darwin) platform="darwin" ;;
|
|
||||||
Linux) platform="linux" ;;
|
|
||||||
MINGW*|CYGWIN*|MSYS*) platform="win32" ;;
|
|
||||||
*) platform="$(uname -s | tr '[:upper:]' '[:lower:]')" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Map architecture names
|
|
||||||
case "$(uname -m)" in
|
|
||||||
x86_64|amd64) arch="x64" ;;
|
|
||||||
aarch64) arch="arm64" ;;
|
|
||||||
armv7l) arch="arm" ;;
|
|
||||||
*) arch="$(uname -m)" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
name="opencode-${platform}-${arch}"
|
|
||||||
binary="opencode"
|
|
||||||
[ "$platform" = "win32" ] && binary="opencode.exe"
|
|
||||||
|
|
||||||
# Search for the binary starting from real script location
|
|
||||||
resolved=""
|
|
||||||
current_dir="$script_dir"
|
|
||||||
while [ "$current_dir" != "/" ]; do
|
|
||||||
candidate="$current_dir/node_modules/$name/bin/$binary"
|
|
||||||
if [ -f "$candidate" ]; then
|
|
||||||
resolved="$candidate"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
current_dir="$(dirname "$current_dir")"
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$resolved" ]; then
|
|
||||||
printf "It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing the \"%s\" package\n" "$name" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle SIGINT gracefully
|
function run(target) {
|
||||||
trap '' INT
|
const result = childProcess.spawnSync(target, process.argv.slice(2), {
|
||||||
|
stdio: "inherit",
|
||||||
|
})
|
||||||
|
if (result.error) {
|
||||||
|
console.error(result.error.message)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const code = typeof result.status === "number" ? result.status : 0
|
||||||
|
process.exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
# Execute the binary with all arguments
|
const envPath = process.env.OPENCODE_BIN_PATH
|
||||||
exec "$resolved" "$@"
|
if (envPath) {
|
||||||
|
run(envPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const scriptPath = fs.realpathSync(__filename)
|
||||||
|
const scriptDir = path.dirname(scriptPath)
|
||||||
|
|
||||||
|
const platformMap = {
|
||||||
|
darwin: "darwin",
|
||||||
|
linux: "linux",
|
||||||
|
win32: "windows",
|
||||||
|
}
|
||||||
|
const archMap = {
|
||||||
|
x64: "x64",
|
||||||
|
arm64: "arm64",
|
||||||
|
arm: "arm",
|
||||||
|
}
|
||||||
|
|
||||||
|
let platform = platformMap[os.platform()]
|
||||||
|
if (!platform) {
|
||||||
|
platform = os.platform()
|
||||||
|
}
|
||||||
|
let arch = archMap[os.arch()]
|
||||||
|
if (!arch) {
|
||||||
|
arch = os.arch()
|
||||||
|
}
|
||||||
|
const base = "opencode-" + platform + "-" + arch
|
||||||
|
const binary = platform === "windows" ? "opencode.exe" : "opencode"
|
||||||
|
|
||||||
|
function findBinary(startDir) {
|
||||||
|
let current = startDir
|
||||||
|
for (;;) {
|
||||||
|
const modules = path.join(current, "node_modules")
|
||||||
|
if (fs.existsSync(modules)) {
|
||||||
|
const entries = fs.readdirSync(modules)
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.startsWith(base)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const candidate = path.join(modules, entry, "bin", binary)
|
||||||
|
if (fs.existsSync(candidate)) {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const parent = path.dirname(current)
|
||||||
|
if (parent === current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
current = parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolved = findBinary(scriptDir)
|
||||||
|
if (!resolved) {
|
||||||
|
console.error(
|
||||||
|
'It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing the "' +
|
||||||
|
base +
|
||||||
|
'" package',
|
||||||
|
)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
run(resolved)
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
if defined OPENCODE_BIN_PATH (
|
|
||||||
set "resolved=%OPENCODE_BIN_PATH%"
|
|
||||||
goto :execute
|
|
||||||
)
|
|
||||||
|
|
||||||
rem Get the directory of this script
|
|
||||||
set "script_dir=%~dp0"
|
|
||||||
set "script_dir=%script_dir:~0,-1%"
|
|
||||||
|
|
||||||
rem Detect platform and architecture
|
|
||||||
set "platform=windows"
|
|
||||||
|
|
||||||
rem Detect architecture
|
|
||||||
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
|
|
||||||
set "arch=x64"
|
|
||||||
) else if "%PROCESSOR_ARCHITECTURE%"=="ARM64" (
|
|
||||||
set "arch=arm64"
|
|
||||||
) else if "%PROCESSOR_ARCHITECTURE%"=="x86" (
|
|
||||||
set "arch=x86"
|
|
||||||
) else (
|
|
||||||
set "arch=x64"
|
|
||||||
)
|
|
||||||
|
|
||||||
set "name=opencode-!platform!-!arch!"
|
|
||||||
set "binary=opencode.exe"
|
|
||||||
|
|
||||||
rem Search for the binary starting from script location
|
|
||||||
set "resolved="
|
|
||||||
set "current_dir=%script_dir%"
|
|
||||||
|
|
||||||
:search_loop
|
|
||||||
set "candidate=%current_dir%\node_modules\%name%\bin\%binary%"
|
|
||||||
if exist "%candidate%" (
|
|
||||||
set "resolved=%candidate%"
|
|
||||||
goto :execute
|
|
||||||
)
|
|
||||||
|
|
||||||
rem Move up one directory
|
|
||||||
for %%i in ("%current_dir%") do set "parent_dir=%%~dpi"
|
|
||||||
set "parent_dir=%parent_dir:~0,-1%"
|
|
||||||
|
|
||||||
rem Check if we've reached the root
|
|
||||||
if "%current_dir%"=="%parent_dir%" goto :not_found
|
|
||||||
set "current_dir=%parent_dir%"
|
|
||||||
goto :search_loop
|
|
||||||
|
|
||||||
:not_found
|
|
||||||
echo It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing the "%name%" package >&2
|
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:execute
|
|
||||||
rem Execute the binary with all arguments in the same console window
|
|
||||||
rem Use start /b /wait to ensure it runs in the current shell context for all shells
|
|
||||||
start /b /wait "" "%resolved%" %*
|
|
||||||
exit /b %ERRORLEVEL%
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.0.47",
|
"version": "1.0.85",
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
"@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/mcp": "0.0.8",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
@@ -54,8 +55,8 @@
|
|||||||
"@opencode-ai/plugin": "workspace:*",
|
"@opencode-ai/plugin": "workspace:*",
|
||||||
"@opencode-ai/script": "workspace:*",
|
"@opencode-ai/script": "workspace:*",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@opentui/core": "0.0.0-20251108-0c7899b1",
|
"@opentui/core": "0.1.47",
|
||||||
"@opentui/solid": "0.0.0-20251108-0c7899b1",
|
"@opentui/solid": "0.1.47",
|
||||||
"@parcel/watcher": "2.5.1",
|
"@parcel/watcher": "2.5.1",
|
||||||
"@pierre/precision-diffs": "catalog:",
|
"@pierre/precision-diffs": "catalog:",
|
||||||
"@solid-primitives/event-bus": "1.1.2",
|
"@solid-primitives/event-bus": "1.1.2",
|
||||||
|
|||||||
@@ -167,6 +167,15 @@ export default {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
filetype: "yaml",
|
||||||
|
wasm: "https://github.com/tree-sitter-grammars/tree-sitter-yaml/releases/download/v0.7.2/tree-sitter-yaml.wasm",
|
||||||
|
queries: {
|
||||||
|
highlights: [
|
||||||
|
"https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/refs/heads/master/queries/yaml/highlights.scm",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
filetype: "haskell",
|
filetype: "haskell",
|
||||||
wasm: "https://github.com/tree-sitter/tree-sitter-haskell/releases/download/v0.23.1/tree-sitter-haskell.wasm",
|
wasm: "https://github.com/tree-sitter/tree-sitter-haskell/releases/download/v0.23.1/tree-sitter-haskell.wasm",
|
||||||
@@ -212,5 +221,19 @@ export default {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
filetype: "swift",
|
||||||
|
wasm: "https://github.com/alex-pinkus/tree-sitter-swift/releases/download/0.7.1/tree-sitter-swift.wasm",
|
||||||
|
queries: {
|
||||||
|
highlights: [
|
||||||
|
// NOTE: Using parser repo queries instead of nvim-treesitter due to incompatible #lua-match? predicates
|
||||||
|
// "https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/refs/heads/master/queries/highlights.scm
|
||||||
|
"https://raw.githubusercontent.com/alex-pinkus/tree-sitter-swift/main/queries/highlights.scm",
|
||||||
|
],
|
||||||
|
locals: [
|
||||||
|
"https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/refs/heads/master/queries/swift/locals.scm",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,38 +17,88 @@ import { Script } from "@opencode-ai/script"
|
|||||||
|
|
||||||
const singleFlag = process.argv.includes("--single")
|
const singleFlag = process.argv.includes("--single")
|
||||||
|
|
||||||
const allTargets = [
|
const allTargets: {
|
||||||
["windows", "x64"],
|
os: string
|
||||||
["linux", "arm64"],
|
arch: "arm64" | "x64"
|
||||||
["linux", "x64"],
|
abi?: "musl"
|
||||||
["linux", "x64-baseline"],
|
avx2?: false
|
||||||
["darwin", "x64"],
|
}[] = [
|
||||||
["darwin", "x64-baseline"],
|
{
|
||||||
["darwin", "arm64"],
|
os: "linux",
|
||||||
|
arch: "arm64",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "x64",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "x64",
|
||||||
|
avx2: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "arm64",
|
||||||
|
abi: "musl",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "x64",
|
||||||
|
abi: "musl",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "x64",
|
||||||
|
abi: "musl",
|
||||||
|
avx2: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "darwin",
|
||||||
|
arch: "arm64",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "darwin",
|
||||||
|
arch: "x64",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "darwin",
|
||||||
|
arch: "x64",
|
||||||
|
avx2: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "win32",
|
||||||
|
arch: "x64",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "win32",
|
||||||
|
arch: "x64",
|
||||||
|
avx2: false,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const targets = singleFlag
|
const targets = singleFlag
|
||||||
? allTargets.filter(([os, arch]) => os === process.platform && arch === process.arch)
|
? allTargets.filter((item) => item.os === process.platform && item.arch === process.arch)
|
||||||
: allTargets
|
: allTargets
|
||||||
|
|
||||||
await $`rm -rf dist`
|
await $`rm -rf dist`
|
||||||
|
|
||||||
const binaries: Record<string, string> = {}
|
const binaries: Record<string, string> = {}
|
||||||
for (const [os, arch] of targets) {
|
await $`bun install --os="*" --cpu="*" @opentui/core@${pkg.dependencies["@opentui/core"]}`
|
||||||
console.log(`building ${os}-${arch}`)
|
await $`bun install --os="*" --cpu="*" @parcel/watcher@${pkg.dependencies["@parcel/watcher"]}`
|
||||||
const name = `${pkg.name}-${os}-${arch}`
|
for (const item of targets) {
|
||||||
|
const name = [
|
||||||
|
pkg.name,
|
||||||
|
// changing to win32 flags npm for some reason
|
||||||
|
item.os === "win32" ? "windows" : item.os,
|
||||||
|
item.arch,
|
||||||
|
item.avx2 === false ? "baseline" : undefined,
|
||||||
|
item.abi === undefined ? undefined : item.abi,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("-")
|
||||||
|
console.log(`building ${name}`)
|
||||||
await $`mkdir -p dist/${name}/bin`
|
await $`mkdir -p dist/${name}/bin`
|
||||||
|
|
||||||
const opentui = `@opentui/core-${os === "windows" ? "win32" : os}-${arch.replace("-baseline", "")}`
|
|
||||||
await $`mkdir -p ../../node_modules/${opentui}`
|
|
||||||
await $`npm pack ${opentui}@${pkg.dependencies["@opentui/core"]}`.cwd(path.join(dir, "../../node_modules"))
|
|
||||||
await $`tar -xf ../../node_modules/${opentui.replace("@opentui/", "opentui-")}-*.tgz -C ../../node_modules/${opentui} --strip-components=1`
|
|
||||||
|
|
||||||
const watcher = `@parcel/watcher-${os === "windows" ? "win32" : os}-${arch.replace("-baseline", "")}${os === "linux" ? "-glibc" : ""}`
|
|
||||||
await $`mkdir -p ../../node_modules/${watcher}`
|
|
||||||
await $`npm pack ${watcher}`.cwd(path.join(dir, "../../node_modules")).quiet()
|
|
||||||
await $`tar -xf ../../node_modules/${watcher.replace("@parcel/", "parcel-")}-*.tgz -C ../../node_modules/${watcher} --strip-components=1`
|
|
||||||
|
|
||||||
const parserWorker = fs.realpathSync(path.resolve(dir, "./node_modules/@opentui/core/parser.worker.js"))
|
const parserWorker = fs.realpathSync(path.resolve(dir, "./node_modules/@opentui/core/parser.worker.js"))
|
||||||
const workerPath = "./src/cli/cmd/tui/worker.ts"
|
const workerPath = "./src/cli/cmd/tui/worker.ts"
|
||||||
|
|
||||||
@@ -58,7 +108,7 @@ for (const [os, arch] of targets) {
|
|||||||
plugins: [solidPlugin],
|
plugins: [solidPlugin],
|
||||||
sourcemap: "external",
|
sourcemap: "external",
|
||||||
compile: {
|
compile: {
|
||||||
target: `bun-${os}-${arch}` as any,
|
target: name.replace(pkg.name, "bun") as any,
|
||||||
outfile: `dist/${name}/bin/opencode`,
|
outfile: `dist/${name}/bin/opencode`,
|
||||||
execArgv: [`--user-agent=opencode/${Script.version}`, `--env-file=""`, `--`],
|
execArgv: [`--user-agent=opencode/${Script.version}`, `--env-file=""`, `--`],
|
||||||
windows: {},
|
windows: {},
|
||||||
@@ -66,7 +116,7 @@ for (const [os, arch] of targets) {
|
|||||||
entrypoints: ["./src/index.ts", parserWorker, workerPath],
|
entrypoints: ["./src/index.ts", parserWorker, workerPath],
|
||||||
define: {
|
define: {
|
||||||
OPENCODE_VERSION: `'${Script.version}'`,
|
OPENCODE_VERSION: `'${Script.version}'`,
|
||||||
OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/" + path.relative(dir, parserWorker),
|
OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/" + path.relative(dir, parserWorker).replaceAll("\\", "/"),
|
||||||
OPENCODE_WORKER_PATH: workerPath,
|
OPENCODE_WORKER_PATH: workerPath,
|
||||||
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
||||||
},
|
},
|
||||||
@@ -78,8 +128,8 @@ for (const [os, arch] of targets) {
|
|||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
version: Script.version,
|
version: Script.version,
|
||||||
os: [os === "windows" ? "win32" : os],
|
os: [item.os],
|
||||||
cpu: [arch],
|
cpu: [item.arch],
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
|
|||||||
@@ -50,79 +50,66 @@ function detectPlatformAndArch() {
|
|||||||
function findBinary() {
|
function findBinary() {
|
||||||
const { platform, arch } = detectPlatformAndArch()
|
const { platform, arch } = detectPlatformAndArch()
|
||||||
const packageName = `opencode-${platform}-${arch}`
|
const packageName = `opencode-${platform}-${arch}`
|
||||||
const binary = platform === "windows" ? "opencode.exe" : "opencode"
|
const binaryName = platform === "windows" ? "opencode.exe" : "opencode"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use require.resolve to find the package
|
// Use require.resolve to find the package
|
||||||
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
const packageJsonPath = require.resolve(`${packageName}/package.json`)
|
||||||
const packageDir = path.dirname(packageJsonPath)
|
const packageDir = path.dirname(packageJsonPath)
|
||||||
const binaryPath = path.join(packageDir, "bin", binary)
|
const binaryPath = path.join(packageDir, "bin", binaryName)
|
||||||
|
|
||||||
if (!fs.existsSync(binaryPath)) {
|
if (!fs.existsSync(binaryPath)) {
|
||||||
throw new Error(`Binary not found at ${binaryPath}`)
|
throw new Error(`Binary not found at ${binaryPath}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return binaryPath
|
return { binaryPath, binaryName }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Could not find package ${packageName}: ${error.message}`)
|
throw new Error(`Could not find package ${packageName}: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function regenerateWindowsCmdWrappers() {
|
function prepareBinDirectory(binaryName) {
|
||||||
console.log("Windows + npm detected: Forcing npm to rebuild bin links")
|
const binDir = path.join(__dirname, "bin")
|
||||||
|
const targetPath = path.join(binDir, binaryName)
|
||||||
|
|
||||||
try {
|
// Ensure bin directory exists
|
||||||
const { execSync } = require("child_process")
|
if (!fs.existsSync(binDir)) {
|
||||||
const pkgPath = path.join(__dirname, "..")
|
fs.mkdirSync(binDir, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
// npm_config_global is string | undefined
|
// Remove existing binary/symlink if it exists
|
||||||
// if it exists, the value is true
|
if (fs.existsSync(targetPath)) {
|
||||||
const isGlobal = process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules"))
|
fs.unlinkSync(targetPath)
|
||||||
|
}
|
||||||
|
|
||||||
// The npm rebuild command does 2 things - Execute lifecycle scripts and rebuild bin links
|
return { binDir, targetPath }
|
||||||
// We want to skip lifecycle scripts to avoid infinite loops, so we use --ignore-scripts
|
}
|
||||||
const cmd = `npm rebuild opencode-ai --ignore-scripts${isGlobal ? " -g" : ""}`
|
|
||||||
const opts = {
|
|
||||||
stdio: "inherit",
|
|
||||||
shell: true,
|
|
||||||
...(isGlobal ? {} : { cwd: path.join(pkgPath, "..", "..") }), // For local, run from project root
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Running: ${cmd}`)
|
function symlinkBinary(sourcePath, binaryName) {
|
||||||
execSync(cmd, opts)
|
const { targetPath } = prepareBinDirectory(binaryName)
|
||||||
console.log("Successfully rebuilt npm bin links")
|
|
||||||
} catch (error) {
|
fs.symlinkSync(sourcePath, targetPath)
|
||||||
console.error("Error rebuilding npm links:", error.message)
|
console.log(`opencode binary symlinked: ${targetPath} -> ${sourcePath}`)
|
||||||
console.error("npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts")
|
|
||||||
|
// Verify the file exists after operation
|
||||||
|
if (!fs.existsSync(targetPath)) {
|
||||||
|
throw new Error(`Failed to symlink binary to ${targetPath}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
if (os.platform() === "win32") {
|
if (os.platform() === "win32") {
|
||||||
// NPM eg format - npm/11.4.2 node/v24.4.1 win32 x64
|
// On Windows, the .exe is already included in the package and bin field points to it
|
||||||
// Bun eg format - bun/1.2.19 npm/? node/v24.3.0 win32 x64
|
// No postinstall setup needed
|
||||||
if (process.env.npm_config_user_agent.startsWith("npm")) {
|
console.log("Windows detected: binary setup not needed (using packaged .exe)")
|
||||||
await regenerateWindowsCmdWrappers()
|
|
||||||
} else {
|
|
||||||
console.log("Windows detected but not npm, skipping postinstall")
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const binaryPath = findBinary()
|
const { binaryPath, binaryName } = findBinary()
|
||||||
const binScript = path.join(__dirname, "bin", "opencode")
|
symlinkBinary(binaryPath, binaryName)
|
||||||
|
|
||||||
// Remove existing bin script if it exists
|
|
||||||
if (fs.existsSync(binScript)) {
|
|
||||||
fs.unlinkSync(binScript)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create symlink to the actual binary
|
|
||||||
fs.symlinkSync(binaryPath, binScript)
|
|
||||||
console.log(`opencode binary symlinked: ${binScript} -> ${binaryPath}`)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to create opencode binary symlink:", error.message)
|
console.error("Failed to setup opencode binary:", error.message)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
import fs from "fs"
|
|
||||||
import path from "path"
|
|
||||||
import os from "os"
|
|
||||||
import { fileURLToPath } from "url"
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
if (os.platform() !== "win32") {
|
|
||||||
console.log("Non-Windows platform detected, skipping preinstall")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Windows detected: Modifying package.json bin entry")
|
|
||||||
|
|
||||||
// Read package.json
|
|
||||||
const packageJsonPath = path.join(__dirname, "package.json")
|
|
||||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"))
|
|
||||||
|
|
||||||
// Modify bin to point to .cmd file on Windows
|
|
||||||
packageJson.bin = {
|
|
||||||
opencode: "./bin/opencode.cmd",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write it back
|
|
||||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
|
|
||||||
console.log("Updated package.json bin to use opencode.cmd")
|
|
||||||
|
|
||||||
// Now you can also remove the Unix script if you want
|
|
||||||
const unixScript = path.join(__dirname, "bin", "opencode")
|
|
||||||
if (fs.existsSync(unixScript)) {
|
|
||||||
console.log("Removing Unix shell script")
|
|
||||||
fs.unlinkSync(unixScript)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
main()
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Preinstall script error:", error.message)
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user