mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-02 16:26:14 -04:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6a3be31de | |||
| 715265de4b | |||
| 2048f32491 | |||
| 2d7ba43a21 | |||
| bd198d8550 | |||
| cbffbcdd3d | |||
| 2be8b2269f | |||
| c1fa257a92 | |||
| 7fd81dd93e | |||
| d554e7aaef |
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
auto-label:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
@@ -11,16 +11,12 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: ./.github/actions/setup-bun
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
- run: bun sst deploy --stage=${{ github.ref_name }}
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
check-duplicates:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Check for duplicate issues
|
||||
env:
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OPENCODE_PERMISSION: |
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
format:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send nicely-formatted embed to Discord
|
||||
uses: SethCohen/github-releases-to-discord@v1
|
||||
|
||||
@@ -3,8 +3,6 @@ name: opencode
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
opencode:
|
||||
@@ -13,7 +11,7 @@ jobs:
|
||||
startsWith(github.event.comment.body, '/oc') ||
|
||||
contains(github.event.comment.body, ' /opencode') ||
|
||||
startsWith(github.event.comment.body, '/opencode')
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
@@ -23,11 +21,9 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Run opencode
|
||||
uses: sst/opencode/github@latest
|
||||
env:
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
with:
|
||||
model: opencode/claude-haiku-4-5
|
||||
model: opencode/glm-4.6
|
||||
|
||||
@@ -14,7 +14,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
@@ -13,7 +13,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
@@ -25,7 +25,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -61,13 +61,6 @@ jobs:
|
||||
run: |
|
||||
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
./script/publish.ts
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
name: snapshot
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- test-bedrock
|
||||
- fix-snapshot-2
|
||||
- v0
|
||||
- otui-diffs
|
||||
- snapshot-*
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
stats:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
zed:
|
||||
name: Release Zed Extension
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
test:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
typecheck:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -18,8 +18,7 @@ on:
|
||||
|
||||
jobs:
|
||||
update:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SYSTEM: x86_64-linux
|
||||
|
||||
@@ -29,8 +28,6 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.head_ref || github.ref_name }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||
|
||||
- name: Setup Nix
|
||||
uses: DeterminateSystems/nix-installer-action@v20
|
||||
@@ -43,16 +40,12 @@ jobs:
|
||||
- name: Update flake.lock
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "📦 Updating flake.lock..."
|
||||
nix flake update
|
||||
echo "✅ flake.lock updated successfully"
|
||||
|
||||
- name: Update node_modules hash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "🔄 Updating node_modules hash..."
|
||||
nix/scripts/update-hashes.sh
|
||||
echo "✅ node_modules hash updated successfully"
|
||||
|
||||
- name: Commit hash changes
|
||||
env:
|
||||
@@ -60,8 +53,6 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "🔍 Checking for changes in tracked Nix files..."
|
||||
|
||||
summarize() {
|
||||
local status="$1"
|
||||
{
|
||||
@@ -79,24 +70,15 @@ jobs:
|
||||
FILES=(flake.lock flake.nix nix/node-modules.nix nix/hashes.json)
|
||||
STATUS="$(git status --short -- "${FILES[@]}" || true)"
|
||||
if [ -z "$STATUS" ]; then
|
||||
echo "✅ No changes detected. Hashes are already up to date."
|
||||
summarize "no changes"
|
||||
echo "No changes to tracked Nix files. Hashes are already up to date."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "📝 Changes detected:"
|
||||
echo "$STATUS"
|
||||
echo "🔗 Staging files..."
|
||||
git add "${FILES[@]}"
|
||||
echo "💾 Committing changes..."
|
||||
git commit -m "Update Nix flake.lock and hashes"
|
||||
echo "✅ Changes committed"
|
||||
|
||||
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||
echo "🌳 Pulling latest from branch: $BRANCH"
|
||||
git pull --rebase origin "$BRANCH"
|
||||
echo "🚀 Pushing changes to branch: $BRANCH"
|
||||
git push origin HEAD:"$BRANCH"
|
||||
echo "✅ Changes pushed successfully"
|
||||
|
||||
summarize "committed $(git rev-parse --short HEAD)"
|
||||
|
||||
@@ -1,9 +1,2 @@
|
||||
#!/bin/sh
|
||||
# Check if bun version matches package.json
|
||||
EXPECTED_VERSION=$(grep '"packageManager"' package.json | sed 's/.*"bun@\([^"]*\)".*/\1/')
|
||||
CURRENT_VERSION=$(bun --version)
|
||||
if [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; then
|
||||
echo "Error: Bun version $CURRENT_VERSION does not match expected version $EXPECTED_VERSION from package.json"
|
||||
exit 1
|
||||
fi
|
||||
bun typecheck
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
description: Git commit and push
|
||||
subtask: true
|
||||
---
|
||||
|
||||
commit and push
|
||||
@@ -21,6 +22,3 @@ WHAT was done.
|
||||
|
||||
do not do generic messages like "improved agent experience" be very specific
|
||||
about what user facing changes were made
|
||||
|
||||
if there are changes do a git pull --rebase
|
||||
if there are conflicts DO NOT FIX THEM. notify me and I will fix them
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"plugin": ["opencode-openai-codex-auth"],
|
||||
// "enterprise": {
|
||||
// "url": "https://enterprise.dev.opencode.ai",
|
||||
// },
|
||||
"provider": {
|
||||
"opencode": {
|
||||
"options": {
|
||||
@@ -11,17 +8,4 @@
|
||||
},
|
||||
},
|
||||
},
|
||||
"mcp": {
|
||||
"exa": {
|
||||
"type": "remote",
|
||||
"url": "https://mcp.exa.ai/mcp",
|
||||
},
|
||||
"morph": {
|
||||
"type": "local",
|
||||
"command": ["bunx", "@morphllm/morphmcp"],
|
||||
"environment": {
|
||||
"ENABLED_TOOLS": "warp_grep",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -146,15 +146,3 @@
|
||||
| 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) |
|
||||
| 2025-11-21 | 826,309 (+11,689) | 769,307 (+11,400) | 1,595,616 (+23,089) |
|
||||
| 2025-11-22 | 837,269 (+10,960) | 780,996 (+11,689) | 1,618,265 (+22,649) |
|
||||
| 2025-11-23 | 846,609 (+9,340) | 795,069 (+14,073) | 1,641,678 (+23,413) |
|
||||
| 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) |
|
||||
| 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) |
|
||||
| 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) |
|
||||
| 2025-11-27 | 893,960 (+12,546) | 846,180 (+13,662) | 1,740,140 (+26,208) |
|
||||
| 2025-11-28 | 901,741 (+7,781) | 856,482 (+10,302) | 1,758,223 (+18,083) |
|
||||
| 2025-11-29 | 908,689 (+6,948) | 863,361 (+6,879) | 1,772,050 (+13,827) |
|
||||
| 2025-11-30 | 916,116 (+7,427) | 870,194 (+6,833) | 1,786,310 (+14,260) |
|
||||
| 2025-12-01 | 925,898 (+9,782) | 876,500 (+6,306) | 1,802,398 (+16,088) |
|
||||
| 2025-12-02 | 939,250 (+13,352) | 890,919 (+14,419) | 1,830,169 (+27,771) |
|
||||
|
||||
Generated
+3
-3
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764611609,
|
||||
"narHash": "sha256-yU9BNcP0oadUKupw0UKmO9BKDOVIg9NStdJosEbXf8U=",
|
||||
"lastModified": 1763618868,
|
||||
"narHash": "sha256-v5afmLjn/uyD9EQuPBn7nZuaZVV9r+JerayK/4wvdWA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8c29968b3a942f2903f90797f9623737c215737c",
|
||||
"rev": "a8d610af3f1a5fb71e23e08434d8d61a466fc942",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -30,24 +30,6 @@ Leave the following comment on a GitHub PR. opencode will implement the requeste
|
||||
Delete the attachment from S3 when the note is removed /oc
|
||||
```
|
||||
|
||||
#### Review specific code lines
|
||||
|
||||
Leave a comment directly on code lines in the PR's "Files" tab. opencode will automatically detect the file, line numbers, and diff context to provide precise responses.
|
||||
|
||||
```
|
||||
[Comment on specific lines in Files tab]
|
||||
/oc add error handling here
|
||||
```
|
||||
|
||||
When commenting on specific lines, opencode receives:
|
||||
|
||||
- The exact file being reviewed
|
||||
- The specific lines of code
|
||||
- The surrounding diff context
|
||||
- Line number information
|
||||
|
||||
This allows for more targeted requests without needing to specify file paths or line numbers manually.
|
||||
|
||||
## Installation
|
||||
|
||||
Run the following command in the terminal from your GitHub repo:
|
||||
@@ -69,8 +51,6 @@ This will walk you through installing the GitHub app, creating the workflow, and
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
opencode:
|
||||
@@ -155,9 +135,3 @@ Replace the image URL `https://github.com/user-attachments/assets/xxxxxxxx` with
|
||||
```
|
||||
MOCK_EVENT='{"eventName":"issue_comment","repo":{"owner":"sst","repo":"hello-world"},"actor":"fwang","payload":{"issue":{"number":4,"pull_request":{}},"comment":{"id":1,"body":"hey opencode, summarize thread"}}}'
|
||||
```
|
||||
|
||||
### PR review comment event
|
||||
|
||||
```
|
||||
MOCK_EVENT='{"eventName":"pull_request_review_comment","repo":{"owner":"sst","repo":"hello-world"},"actor":"fwang","payload":{"pull_request":{"number":7},"comment":{"id":1,"body":"hey opencode, add error handling","path":"src/components/Button.tsx","diff_hunk":"@@ -45,8 +45,11 @@\n- const handleClick = () => {\n- console.log('clicked')\n+ const handleClick = useCallback(() => {\n+ console.log('clicked')\n+ doSomething()\n+ }, [doSomething])","line":47,"original_line":45,"position":10,"commit_id":"abc123","original_commit_id":"def456"}}}'
|
||||
```
|
||||
|
||||
+7
-44
@@ -5,7 +5,7 @@ import { graphql } from "@octokit/graphql"
|
||||
import * as core from "@actions/core"
|
||||
import * as github from "@actions/github"
|
||||
import type { Context as GitHubContext } from "@actions/github/lib/context"
|
||||
import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types"
|
||||
import type { IssueCommentEvent } from "@octokit/webhooks-types"
|
||||
import { createOpencodeClient } from "@opencode-ai/sdk"
|
||||
import { spawn } from "node:child_process"
|
||||
|
||||
@@ -124,7 +124,7 @@ let exitCode = 0
|
||||
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
|
||||
|
||||
try {
|
||||
assertContextEvent("issue_comment", "pull_request_review_comment")
|
||||
assertContextEvent("issue_comment")
|
||||
assertPayloadKeyword()
|
||||
await assertOpencodeConnected()
|
||||
|
||||
@@ -241,43 +241,19 @@ function createOpencode() {
|
||||
}
|
||||
|
||||
function assertPayloadKeyword() {
|
||||
const payload = useContext().payload as IssueCommentEvent | PullRequestReviewCommentEvent
|
||||
const payload = useContext().payload as IssueCommentEvent
|
||||
const body = payload.comment.body.trim()
|
||||
if (!body.match(/(?:^|\s)(?:\/opencode|\/oc)(?=$|\s)/)) {
|
||||
throw new Error("Comments must mention `/opencode` or `/oc`")
|
||||
}
|
||||
}
|
||||
|
||||
function getReviewCommentContext() {
|
||||
const context = useContext()
|
||||
if (context.eventName !== "pull_request_review_comment") {
|
||||
return null
|
||||
}
|
||||
|
||||
const payload = context.payload as PullRequestReviewCommentEvent
|
||||
return {
|
||||
file: payload.comment.path,
|
||||
diffHunk: payload.comment.diff_hunk,
|
||||
line: payload.comment.line,
|
||||
originalLine: payload.comment.original_line,
|
||||
position: payload.comment.position,
|
||||
commitId: payload.comment.commit_id,
|
||||
originalCommitId: payload.comment.original_commit_id,
|
||||
}
|
||||
}
|
||||
|
||||
async function assertOpencodeConnected() {
|
||||
let retry = 0
|
||||
let connected = false
|
||||
do {
|
||||
try {
|
||||
await client.app.log<true>({
|
||||
body: {
|
||||
service: "github-workflow",
|
||||
level: "info",
|
||||
message: "Prepare to react to Github Workflow event",
|
||||
},
|
||||
})
|
||||
await client.app.get<true>()
|
||||
connected = true
|
||||
break
|
||||
} catch (e) {}
|
||||
@@ -407,24 +383,11 @@ async function createComment() {
|
||||
}
|
||||
|
||||
async function getUserPrompt() {
|
||||
const context = useContext()
|
||||
const payload = context.payload as IssueCommentEvent | PullRequestReviewCommentEvent
|
||||
const reviewContext = getReviewCommentContext()
|
||||
|
||||
let prompt = (() => {
|
||||
const payload = useContext().payload as IssueCommentEvent
|
||||
const body = payload.comment.body.trim()
|
||||
if (body === "/opencode" || body === "/oc") {
|
||||
if (reviewContext) {
|
||||
return `Review this code change and suggest improvements for the commented lines:\n\nFile: ${reviewContext.file}\nLines: ${reviewContext.line}\n\n${reviewContext.diffHunk}`
|
||||
}
|
||||
return "Summarize this thread"
|
||||
}
|
||||
if (body.includes("/opencode") || body.includes("/oc")) {
|
||||
if (reviewContext) {
|
||||
return `${body}\n\nContext: You are reviewing a comment on file "${reviewContext.file}" at line ${reviewContext.line}.\n\nDiff context:\n${reviewContext.diffHunk}`
|
||||
}
|
||||
return body
|
||||
}
|
||||
if (body === "/opencode" || body === "/oc") return "Summarize this thread"
|
||||
if (body.includes("/opencode") || body.includes("/oc")) return body
|
||||
throw new Error("Comments must mention `/opencode` or `/oc`")
|
||||
})()
|
||||
|
||||
|
||||
+4
-10
@@ -97,12 +97,8 @@ export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint",
|
||||
],
|
||||
})
|
||||
|
||||
const ZEN_MODELS = [
|
||||
new sst.Secret("ZEN_MODELS1"),
|
||||
new sst.Secret("ZEN_MODELS2"),
|
||||
new sst.Secret("ZEN_MODELS3"),
|
||||
new sst.Secret("ZEN_MODELS4"),
|
||||
]
|
||||
const ZEN_MODELS1 = new sst.Secret("ZEN_MODELS1")
|
||||
const ZEN_MODELS2 = new sst.Secret("ZEN_MODELS2")
|
||||
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
|
||||
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||
properties: { value: auth.url.apply((url) => url!) },
|
||||
@@ -116,8 +112,6 @@ const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
|
||||
// CONSOLE
|
||||
////////////////
|
||||
|
||||
const bucket = new sst.cloudflare.Bucket("ConsoleData")
|
||||
|
||||
const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID")
|
||||
const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY")
|
||||
|
||||
@@ -134,15 +128,15 @@ new sst.cloudflare.x.SolidStart("Console", {
|
||||
domain,
|
||||
path: "packages/console/app",
|
||||
link: [
|
||||
bucket,
|
||||
database,
|
||||
AUTH_API_URL,
|
||||
STRIPE_WEBHOOK_SECRET,
|
||||
STRIPE_SECRET_KEY,
|
||||
ZEN_MODELS1,
|
||||
ZEN_MODELS2,
|
||||
EMAILOCTOPUS_API_KEY,
|
||||
AWS_SES_ACCESS_KEY_ID,
|
||||
AWS_SES_SECRET_ACCESS_KEY,
|
||||
...ZEN_MODELS,
|
||||
...($dev
|
||||
? [
|
||||
new sst.Secret("CLOUDFLARE_DEFAULT_ACCOUNT_ID", process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID!),
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { SECRET } from "./secret"
|
||||
import { domain } from "./stage"
|
||||
|
||||
const storage = new sst.cloudflare.Bucket("EnterpriseStorage")
|
||||
|
||||
const enterprise = new sst.cloudflare.x.SolidStart("Enterprise", {
|
||||
domain: "enterprise." + domain,
|
||||
path: "packages/enterprise",
|
||||
buildCommand: "bun run build:cloudflare",
|
||||
environment: {
|
||||
OPENCODE_STORAGE_ADAPTER: "r2",
|
||||
OPENCODE_STORAGE_ACCOUNT_ID: sst.cloudflare.DEFAULT_ACCOUNT_ID,
|
||||
OPENCODE_STORAGE_ACCESS_KEY_ID: SECRET.R2AccessKey.value,
|
||||
OPENCODE_STORAGE_SECRET_ACCESS_KEY: SECRET.R2SecretKey.value,
|
||||
OPENCODE_STORAGE_BUCKET: storage.name,
|
||||
},
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
export const SECRET = {
|
||||
R2AccessKey: new sst.Secret("R2AccessKey", "unknown"),
|
||||
R2SecretKey: new sst.Secret("R2SecretKey", "unknown"),
|
||||
}
|
||||
@@ -2,103 +2,49 @@
|
||||
set -euo pipefail
|
||||
APP=opencode
|
||||
|
||||
MUTED='\033[0;2m'
|
||||
RED='\033[0;31m'
|
||||
ORANGE='\033[38;5;214m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
ORANGE='\033[38;2;255;140;0m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
requested_version=${VERSION:-}
|
||||
|
||||
raw_os=$(uname -s)
|
||||
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
|
||||
# Normalize various Unix-like identifiers
|
||||
case "$raw_os" in
|
||||
Darwin*) os="darwin" ;;
|
||||
Linux*) os="linux" ;;
|
||||
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
|
||||
esac
|
||||
|
||||
esac
|
||||
arch=$(uname -m)
|
||||
|
||||
if [[ "$arch" == "aarch64" ]]; then
|
||||
arch="arm64"
|
||||
fi
|
||||
if [[ "$arch" == "x86_64" ]]; then
|
||||
elif [[ "$arch" == "x86_64" ]]; then
|
||||
arch="x64"
|
||||
fi
|
||||
|
||||
if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then
|
||||
rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
|
||||
if [ "$rosetta_flag" = "1" ]; then
|
||||
arch="arm64"
|
||||
fi
|
||||
fi
|
||||
filename="$APP-$os-$arch.zip"
|
||||
|
||||
combo="$os-$arch"
|
||||
case "$combo" in
|
||||
linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64)
|
||||
|
||||
case "$filename" in
|
||||
*"-linux-"*)
|
||||
[[ "$arch" == "x64" || "$arch" == "arm64" ]] || exit 1
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
|
||||
exit 1
|
||||
*"-darwin-"*)
|
||||
[[ "$arch" == "x64" || "$arch" == "arm64" ]] || exit 1
|
||||
;;
|
||||
*"-windows-"*)
|
||||
[[ "$arch" == "x64" ]] || exit 1
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
archive_ext=".zip"
|
||||
if [ "$os" = "linux" ]; then
|
||||
archive_ext=".tar.gz"
|
||||
fi
|
||||
|
||||
is_musl=false
|
||||
if [ "$os" = "linux" ]; then
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
is_musl=true
|
||||
fi
|
||||
|
||||
if command -v ldd >/dev/null 2>&1; then
|
||||
if ldd --version 2>&1 | grep -qi musl; then
|
||||
is_musl=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
needs_baseline=false
|
||||
if [ "$arch" = "x64" ]; then
|
||||
if [ "$os" = "linux" ]; then
|
||||
if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then
|
||||
needs_baseline=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$os" = "darwin" ]; then
|
||||
avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0)
|
||||
if [ "$avx2" != "1" ]; then
|
||||
needs_baseline=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
target="$os-$arch"
|
||||
if [ "$needs_baseline" = "true" ]; then
|
||||
target="$target-baseline"
|
||||
fi
|
||||
if [ "$is_musl" = "true" ]; then
|
||||
target="$target-musl"
|
||||
fi
|
||||
|
||||
filename="$APP-$target$archive_ext"
|
||||
|
||||
|
||||
if [ "$os" = "linux" ]; then
|
||||
if ! command -v tar >/dev/null 2>&1; then
|
||||
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! command -v unzip >/dev/null 2>&1; then
|
||||
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
INSTALL_DIR=$HOME/.opencode/bin
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
@@ -121,8 +67,8 @@ print_message() {
|
||||
local color=""
|
||||
|
||||
case $level in
|
||||
info) color="${NC}" ;;
|
||||
warning) color="${NC}" ;;
|
||||
info) color="${GREEN}" ;;
|
||||
warning) color="${YELLOW}" ;;
|
||||
error) color="${RED}" ;;
|
||||
esac
|
||||
|
||||
@@ -140,119 +86,19 @@ check_version() {
|
||||
installed_version=$(echo $installed_version | awk '{print $2}')
|
||||
|
||||
if [[ "$installed_version" != "$specific_version" ]]; then
|
||||
print_message info "${MUTED}Installed version: ${NC}$installed_version."
|
||||
print_message info "Installed version: ${YELLOW}$installed_version."
|
||||
else
|
||||
print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
|
||||
print_message info "Version ${YELLOW}$specific_version${GREEN} already installed"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
unbuffered_sed() {
|
||||
if echo | sed -u -e "" >/dev/null 2>&1; then
|
||||
sed -nu "$@"
|
||||
elif echo | sed -l -e "" >/dev/null 2>&1; then
|
||||
sed -nl "$@"
|
||||
else
|
||||
local pad="$(printf "\n%512s" "")"
|
||||
sed -ne "s/$/\\${pad}/" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
print_progress() {
|
||||
local bytes="$1"
|
||||
local length="$2"
|
||||
[ "$length" -gt 0 ] || return 0
|
||||
|
||||
local width=50
|
||||
local percent=$(( bytes * 100 / length ))
|
||||
[ "$percent" -gt 100 ] && percent=100
|
||||
local on=$(( percent * width / 100 ))
|
||||
local off=$(( width - on ))
|
||||
|
||||
local filled=$(printf "%*s" "$on" "")
|
||||
filled=${filled// /■}
|
||||
local empty=$(printf "%*s" "$off" "")
|
||||
empty=${empty// /・}
|
||||
|
||||
printf "\r${ORANGE}%s%s %3d%%${NC}" "$filled" "$empty" "$percent" >&4
|
||||
}
|
||||
|
||||
download_with_progress() {
|
||||
local url="$1"
|
||||
local output="$2"
|
||||
|
||||
if [ -t 2 ]; then
|
||||
exec 4>&2
|
||||
else
|
||||
exec 4>/dev/null
|
||||
fi
|
||||
|
||||
local tmp_dir=${TMPDIR:-/tmp}
|
||||
local basename="${tmp_dir}/opencode_install_$$"
|
||||
local tracefile="${basename}.trace"
|
||||
|
||||
rm -f "$tracefile"
|
||||
mkfifo "$tracefile"
|
||||
|
||||
# Hide cursor
|
||||
printf "\033[?25l" >&4
|
||||
|
||||
trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN
|
||||
|
||||
(
|
||||
curl --trace-ascii "$tracefile" -s -L -o "$output" "$url"
|
||||
) &
|
||||
local curl_pid=$!
|
||||
|
||||
unbuffered_sed \
|
||||
-e 'y/ACDEGHLNORTV/acdeghlnortv/' \
|
||||
-e '/^0000: content-length:/p' \
|
||||
-e '/^<= recv data/p' \
|
||||
"$tracefile" | \
|
||||
{
|
||||
local length=0
|
||||
local bytes=0
|
||||
|
||||
while IFS=" " read -r -a line; do
|
||||
[ "${#line[@]}" -lt 2 ] && continue
|
||||
local tag="${line[0]} ${line[1]}"
|
||||
|
||||
if [ "$tag" = "0000: content-length:" ]; then
|
||||
length="${line[2]}"
|
||||
length=$(echo "$length" | tr -d '\r')
|
||||
bytes=0
|
||||
elif [ "$tag" = "<= recv" ]; then
|
||||
local size="${line[3]}"
|
||||
bytes=$(( bytes + size ))
|
||||
if [ "$length" -gt 0 ]; then
|
||||
print_progress "$bytes" "$length"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
wait $curl_pid
|
||||
local ret=$?
|
||||
echo "" >&4
|
||||
return $ret
|
||||
}
|
||||
|
||||
download_and_install() {
|
||||
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version"
|
||||
print_message info "Downloading ${ORANGE}opencode ${GREEN}version: ${YELLOW}$specific_version ${GREEN}..."
|
||||
mkdir -p opencodetmp && cd opencodetmp
|
||||
|
||||
if [[ "$os" == "windows" ]] || ! download_with_progress "$url" "$filename"; then
|
||||
# Fallback to standard curl on Windows or if custom progress fails
|
||||
curl -# -L -o "$filename" "$url"
|
||||
fi
|
||||
|
||||
if [ "$os" = "linux" ]; then
|
||||
tar -xzf "$filename"
|
||||
else
|
||||
unzip -q "$filename"
|
||||
fi
|
||||
|
||||
curl -# -L -o "$filename" "$url"
|
||||
unzip -q "$filename"
|
||||
mv opencode "$INSTALL_DIR"
|
||||
chmod 755 "${INSTALL_DIR}/opencode"
|
||||
cd .. && rm -rf opencodetmp
|
||||
@@ -271,7 +117,7 @@ add_to_path() {
|
||||
elif [[ -w $config_file ]]; then
|
||||
echo -e "\n# opencode" >> "$config_file"
|
||||
echo "$command" >> "$config_file"
|
||||
print_message info "${MUTED}Successfully added ${NC}opencode ${MUTED}to \$PATH in ${NC}$config_file"
|
||||
print_message info "Successfully added ${ORANGE}opencode ${GREEN}to \$PATH in $config_file"
|
||||
else
|
||||
print_message warning "Manually add the directory to $config_file (or similar):"
|
||||
print_message info " $command"
|
||||
@@ -345,19 +191,3 @@ if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||
echo "$INSTALL_DIR" >> $GITHUB_PATH
|
||||
print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
echo -e "${MUTED} ${NC} ▄ "
|
||||
echo -e "${MUTED}█▀▀█ █▀▀█ █▀▀█ █▀▀▄ ${NC}█▀▀▀ █▀▀█ █▀▀█ █▀▀█"
|
||||
echo -e "${MUTED}█░░█ █░░█ █▀▀▀ █░░█ ${NC}█░░░ █░░█ █░░█ █▀▀▀"
|
||||
echo -e "${MUTED}▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ${NC}▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀"
|
||||
echo -e ""
|
||||
echo -e ""
|
||||
echo -e "${MUTED}OpenCode includes free models, to start:${NC}"
|
||||
echo -e "cd <project> ${MUTED}# Open directory${NC}"
|
||||
echo -e "opencode ${MUTED}# Run command${NC}"
|
||||
echo -e ""
|
||||
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs"
|
||||
echo -e ""
|
||||
echo -e ""
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import solidPlugin from "./node_modules/@opentui/solid/scripts/solid-plugin"
|
||||
import path from "path"
|
||||
import fs from "fs"
|
||||
|
||||
const dir = process.cwd()
|
||||
const parser = fs.realpathSync(path.join(dir, "node_modules/@opentui/core/parser.worker.js"))
|
||||
const worker = "./src/cli/cmd/tui/worker.ts"
|
||||
const version = process.env.OPENCODE_VERSION ?? "local"
|
||||
const channel = process.env.OPENCODE_CHANNEL ?? "local"
|
||||
|
||||
fs.rmSync(path.join(dir, "dist"), { recursive: true, force: true })
|
||||
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./src/index.ts", worker, parser],
|
||||
outdir: "./dist",
|
||||
target: "bun",
|
||||
sourcemap: "none",
|
||||
tsconfig: "./tsconfig.json",
|
||||
plugins: [solidPlugin],
|
||||
external: ["@opentui/core"],
|
||||
define: {
|
||||
OPENCODE_VERSION: `'${version}'`,
|
||||
OPENCODE_CHANNEL: `'${channel}'`,
|
||||
// Leave undefined so runtime picks bundled/dist worker or fallback in code.
|
||||
OPENCODE_WORKER_PATH: "undefined",
|
||||
OTUI_TREE_SITTER_WORKER_PATH: 'new URL("./cli/cmd/tui/parser.worker.js", import.meta.url).href',
|
||||
},
|
||||
})
|
||||
|
||||
if (!result.success) {
|
||||
console.error("bundle failed")
|
||||
for (const log of result.logs) console.error(log)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const parserOut = path.join(dir, "dist/src/cli/cmd/tui/parser.worker.js")
|
||||
fs.mkdirSync(path.dirname(parserOut), { recursive: true })
|
||||
await Bun.write(parserOut, Bun.file(parser))
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"nodeModules": "sha256-HyH219sZn4gOPyVg/bij7K3mfZ0MnBSM/7NmsOyrD4o="
|
||||
"nodeModules": "sha256-bPiUpHGtgwVxHQHXBprpc6fFeJqW6/x7dwtQZBq29oU="
|
||||
}
|
||||
|
||||
+35
-62
@@ -1,4 +1,4 @@
|
||||
{ lib, stdenvNoCC, bun, fzf, ripgrep, makeBinaryWrapper }:
|
||||
{ lib, stdenv, stdenvNoCC, bun, fzf, ripgrep, makeBinaryWrapper }:
|
||||
args:
|
||||
let
|
||||
scripts = args.scripts;
|
||||
@@ -28,93 +28,66 @@ stdenvNoCC.mkDerivation (finalAttrs: {
|
||||
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";
|
||||
dontConfigure = true;
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
cp -r ${finalAttrs.node_modules}/node_modules .
|
||||
cp -r ${finalAttrs.node_modules}/packages .
|
||||
cp ${scripts + "/bun-build.ts"} bun-build.ts
|
||||
|
||||
(
|
||||
cd packages/opencode
|
||||
substituteInPlace bun-build.ts \
|
||||
--replace '@VERSION@' "${finalAttrs.version}"
|
||||
|
||||
chmod -R u+w ./node_modules
|
||||
mkdir -p ./node_modules/@opencode-ai
|
||||
rm -f ./node_modules/@opencode-ai/{script,sdk,plugin}
|
||||
ln -s $(pwd)/../../packages/script ./node_modules/@opencode-ai/script
|
||||
ln -s $(pwd)/../../packages/sdk/js ./node_modules/@opencode-ai/sdk
|
||||
ln -s $(pwd)/../../packages/plugin ./node_modules/@opencode-ai/plugin
|
||||
|
||||
cp ${./bundle.ts} ./bundle.ts
|
||||
chmod +x ./bundle.ts
|
||||
bun run ./bundle.ts
|
||||
)
|
||||
export BUN_COMPILE_TARGET=${args.target}
|
||||
bun --bun bun-build.ts
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
dontStrip = true;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
cd packages/opencode
|
||||
if [ ! -d dist ]; then
|
||||
echo "ERROR: dist directory missing after bundle step"
|
||||
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
|
||||
|
||||
mkdir -p $out/lib/opencode
|
||||
cp -r dist $out/lib/opencode/
|
||||
chmod -R u+w $out/lib/opencode/dist
|
||||
|
||||
# Select bundled worker assets deterministically (sorted find output)
|
||||
worker_file=$(find "$out/lib/opencode/dist" -type f \( -path '*/tui/worker.*' -o -name 'worker.*' \) | sort | head -n1)
|
||||
parser_worker_file=$(find "$out/lib/opencode/dist" -type f -name 'parser.worker.*' | sort | head -n1)
|
||||
if [ -z "$worker_file" ]; then
|
||||
echo "ERROR: bundled worker not found"
|
||||
exit 1
|
||||
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
|
||||
|
||||
main_wasm=$(printf '%s\n' "$out"/lib/opencode/dist/tree-sitter-*.wasm | sort | head -n1)
|
||||
wasm_list=$(find "$out/lib/opencode/dist" -maxdepth 1 -name 'tree-sitter-*.wasm' -print)
|
||||
for patch_file in "$worker_file" "$parser_worker_file"; do
|
||||
[ -z "$patch_file" ] && continue
|
||||
[ ! -f "$patch_file" ] && continue
|
||||
if [ -n "$wasm_list" ] && grep -q 'tree-sitter' "$patch_file"; then
|
||||
# Rewrite wasm references to absolute store paths to avoid runtime resolve failures.
|
||||
bun --bun ${scripts + "/patch-wasm.ts"} "$patch_file" "$main_wasm" $wasm_list
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p $out/lib/opencode/node_modules
|
||||
cp -r ../../node_modules/.bun $out/lib/opencode/node_modules/
|
||||
mkdir -p $out/lib/opencode/node_modules/@opentui
|
||||
|
||||
mkdir -p $out/bin
|
||||
makeWrapper ${bun}/bin/bun $out/bin/opencode \
|
||||
--add-flags "run" \
|
||||
--add-flags "$out/lib/opencode/dist/src/index.js" \
|
||||
--prefix PATH : ${lib.makeBinPath [ fzf ripgrep ]} \
|
||||
--argv0 opencode
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
postInstall = ''
|
||||
for pkg in $out/lib/opencode/node_modules/.bun/@opentui+core-* $out/lib/opencode/node_modules/.bun/@opentui+solid-* $out/lib/opencode/node_modules/.bun/@opentui+core@* $out/lib/opencode/node_modules/.bun/@opentui+solid@*; do
|
||||
if [ -d "$pkg" ]; then
|
||||
pkgName=$(basename "$pkg" | sed 's/@opentui+\([^@]*\)@.*/\1/')
|
||||
ln -sf ../.bun/$(basename "$pkg")/node_modules/@opentui/$pkgName \
|
||||
$out/lib/opencode/node_modules/@opentui/$pkgName
|
||||
fi
|
||||
done
|
||||
postFixup = ''
|
||||
wrapProgram "$out/bin/opencode" --prefix PATH : ${lib.makeBinPath [ fzf ripgrep ]}
|
||||
'';
|
||||
|
||||
dontFixup = true;
|
||||
|
||||
meta = {
|
||||
description = "AI coding agent built for the terminal";
|
||||
longDescription = ''
|
||||
|
||||
@@ -24,13 +24,15 @@ for (const entry of directories) {
|
||||
if (!info.isDirectory()) {
|
||||
continue
|
||||
}
|
||||
const parsed = parseEntry(entry)
|
||||
if (!parsed) {
|
||||
const marker = entry.lastIndexOf("@")
|
||||
if (marker <= 0) {
|
||||
continue
|
||||
}
|
||||
const list = versions.get(parsed.name) ?? []
|
||||
list.push({ dir: full, version: parsed.version, label: entry })
|
||||
versions.set(parsed.name, list)
|
||||
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
|
||||
@@ -77,12 +79,6 @@ for (const [slug, entry] of Array.from(selections.entries()).sort((a, b) => a[0]
|
||||
await mkdir(parent, { recursive: true })
|
||||
const linkPath = join(parent, leaf)
|
||||
const desired = join(entry.dir, "node_modules", slug)
|
||||
const exists = await lstat(desired)
|
||||
.then((info) => info.isDirectory())
|
||||
.catch(() => false)
|
||||
if (!exists) {
|
||||
continue
|
||||
}
|
||||
const relativeTarget = relative(parent, desired)
|
||||
const resolved = relativeTarget.length === 0 ? "." : relativeTarget
|
||||
await rm(linkPath, { recursive: true, force: true })
|
||||
@@ -98,16 +94,3 @@ for (const line of rewrites.slice(0, 20)) {
|
||||
if (rewrites.length > 20) {
|
||||
console.log(" ...")
|
||||
}
|
||||
|
||||
function parseEntry(label: string) {
|
||||
const marker = label.startsWith("@") ? label.indexOf("@", 1) : label.indexOf("@")
|
||||
if (marker <= 0) {
|
||||
return null
|
||||
}
|
||||
const name = label.slice(0, marker).replace(/\+/g, "/")
|
||||
const version = label.slice(marker + 1)
|
||||
if (!name || !version) {
|
||||
return null
|
||||
}
|
||||
return { name, version }
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
/**
|
||||
* Rewrite tree-sitter wasm references inside a JS file to absolute paths.
|
||||
* argv: [node, script, file, mainWasm, ...wasmPaths]
|
||||
*/
|
||||
const [, , file, mainWasm, ...wasmPaths] = process.argv
|
||||
|
||||
if (!file || !mainWasm) {
|
||||
console.error("usage: patch-wasm <file> <mainWasm> [wasmPaths...]")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(file, "utf8")
|
||||
const byName = new Map<string, string>()
|
||||
|
||||
for (const wasm of wasmPaths) {
|
||||
const name = path.basename(wasm)
|
||||
byName.set(name, wasm)
|
||||
}
|
||||
|
||||
let next = content
|
||||
|
||||
for (const [name, wasmPath] of byName) {
|
||||
next = next.replaceAll(name, wasmPath)
|
||||
}
|
||||
|
||||
next = next.replaceAll("tree-sitter.wasm", mainWasm).replaceAll("web-tree-sitter/tree-sitter.wasm", mainWasm)
|
||||
|
||||
// Collapse any relative prefixes before absolute store paths (e.g., "../../../..//nix/store/...")
|
||||
next = next.replace(/(\.\/)+/g, "./")
|
||||
next = next.replace(/(\.\.\/)+\/?(\/nix\/store[^"']+)/g, "/$2")
|
||||
next = next.replace(/(["'])\/{2,}(\/nix\/store[^"']+)(["'])/g, "$1/$2$3")
|
||||
next = next.replace(/(["'])\/\/(nix\/store[^"']+)(["'])/g, "$1/$2$3")
|
||||
|
||||
if (next !== content) fs.writeFileSync(file, next)
|
||||
+11
-15
@@ -4,13 +4,12 @@
|
||||
"description": "AI-powered development tool",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.3.3",
|
||||
"packageManager": "bun@1.3.2",
|
||||
"scripts": {
|
||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
||||
"typecheck": "bun turbo typecheck",
|
||||
"prepare": "husky",
|
||||
"random": "echo 'Random script'",
|
||||
"hello": "echo 'Hello World!'"
|
||||
"random": "echo 'Random script'"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
@@ -20,37 +19,33 @@
|
||||
"packages/slack"
|
||||
],
|
||||
"catalog": {
|
||||
"@types/bun": "1.3.3",
|
||||
"@types/bun": "1.3.0",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"ulid": "3.0.1",
|
||||
"@kobalte/core": "0.13.11",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/node": "22.13.9",
|
||||
"@tsconfig/node22": "22.0.2",
|
||||
"@tsconfig/bun": "1.0.9",
|
||||
"@cloudflare/workers-types": "4.20251008.0",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@pierre/precision-diffs": "0.6.0-beta.3",
|
||||
"@pierre/precision-diffs": "0.4.4",
|
||||
"@solidjs/meta": "0.29.4",
|
||||
"@tailwindcss/vite": "4.1.11",
|
||||
"diff": "8.0.2",
|
||||
"ai": "5.0.97",
|
||||
"hono": "4.7.10",
|
||||
"hono-openapi": "1.1.1",
|
||||
"fuzzysort": "3.1.0",
|
||||
"luxon": "3.6.1",
|
||||
"typescript": "5.8.2",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251014.1",
|
||||
"zod": "4.1.8",
|
||||
"remeda": "2.26.0",
|
||||
"solid-js": "1.9.9",
|
||||
"solid-list": "0.3.0",
|
||||
"tailwindcss": "4.1.11",
|
||||
"virtua": "0.42.3",
|
||||
"vite": "7.1.4",
|
||||
"@solidjs/meta": "0.29.4",
|
||||
"@solidjs/router": "0.15.4",
|
||||
"@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",
|
||||
"solid-js": "1.9.10",
|
||||
"vite-plugin-solid": "2.11.10"
|
||||
"vite-plugin-solid": "2.11.8"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -61,10 +56,8 @@
|
||||
"turbo": "2.5.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.933.0",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"typescript": "catalog:"
|
||||
"@opencode-ai/sdk": "workspace:*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -83,6 +76,9 @@
|
||||
"tree-sitter-bash",
|
||||
"web-tree-sitter"
|
||||
],
|
||||
"patchedDependencies": {
|
||||
"@solidjs/start@1.1.7": "patches/@solidjs%2Fstart@1.1.7.patch"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node": "catalog:"
|
||||
|
||||
@@ -3,6 +3,7 @@ dist
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.vinxi
|
||||
app.config.timestamp_*.js
|
||||
|
||||
# Environment
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { defineConfig } from "@solidjs/start/config"
|
||||
|
||||
export default defineConfig({
|
||||
middleware: "./src/middleware.ts",
|
||||
vite: {
|
||||
server: {
|
||||
allowedHosts: true,
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
external: ["cloudflare:workers"],
|
||||
},
|
||||
minify: false,
|
||||
},
|
||||
},
|
||||
server: {
|
||||
compatibilityDate: "2024-09-19",
|
||||
preset: "cloudflare_module",
|
||||
cloudflare: {
|
||||
nodeCompat: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.0.127",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"dev": "vite dev --host 0.0.0.0",
|
||||
"dev": "vinxi dev --host 0.0.0.0",
|
||||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
||||
"build": "./script/generate-sitemap.ts && vite build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||
"start": "vite start"
|
||||
"build": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||
"start": "vinxi start",
|
||||
"version": "1.0.85"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -18,20 +17,17 @@
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
"@opencode-ai/console-mail": "workspace:*",
|
||||
"@opencode-ai/console-resource": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@solidjs/meta": "catalog:",
|
||||
"@solidjs/router": "catalog:",
|
||||
"@solidjs/start": "catalog:",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
"@solidjs/router": "^0.15.0",
|
||||
"@solidjs/start": "^1.1.0",
|
||||
"chart.js": "4.5.1",
|
||||
"nitro": "3.0.1-alpha.1",
|
||||
"solid-js": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vinxi": "^0.5.7",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"wrangler": "4.50.0"
|
||||
"@typescript/native-preview": "catalog:"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../../ui/src/assets/favicon/apple-touch-icon.png
|
||||
@@ -1 +0,0 @@
|
||||
../../../ui/src/assets/favicon/favicon-96x96.png
|
||||
@@ -0,0 +1,23 @@
|
||||
<svg width="400" height="400" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="400" height="400" fill="#FDFCFC"/>
|
||||
<path d="M96 122.001V70.001H148V122.001H96Z" fill="#17181C"/>
|
||||
<path d="M148.004 122.001V70.001H200.004V122.001H148.004Z" fill="#17181C"/>
|
||||
<path d="M200.008 122.001V70.001H252.008V122.001H200.008Z" fill="#17181C"/>
|
||||
<path d="M251.996 122.001V70.001H303.996V122.001H251.996Z" fill="#17181C"/>
|
||||
<path d="M251.996 173.988V121.988H303.996V173.988H251.996Z" fill="#17181C"/>
|
||||
<path d="M96 225.998V173.998H148V225.998H96Z" fill="#CFCECD"/>
|
||||
<rect width="52" height="52" transform="translate(148.004 173.998)" fill="#17181C"/>
|
||||
<path d="M148.004 225.998V173.998H200.004V225.998H148.004Z" fill="#17181C" fill-opacity="0.1"/>
|
||||
<path d="M200.008 225.998V173.998H252.008V225.998H200.008Z" fill="#17181C"/>
|
||||
<path d="M252.016 225.998V173.998H304.016V225.998H252.016Z" fill="#CFCECD"/>
|
||||
<rect width="52" height="52" transform="translate(96 226.002)" fill="#17181C"/>
|
||||
<path d="M96 278.002V226.002H148V278.002H96Z" fill="#17181C" fill-opacity="0.1"/>
|
||||
<rect width="52" height="52" transform="translate(148.004 226.002)" fill="white"/>
|
||||
<path d="M148.004 278.002V226.002H200.004V278.002H148.004Z" fill="#CFCECD"/>
|
||||
<path d="M200.008 278.002V226.002H252.008V278.002H200.008Z" fill="#CFCECD"/>
|
||||
<path d="M252.016 278.002V226.002H304.016V278.002H252.016Z" fill="#CFCECD"/>
|
||||
<path d="M96 330.012V278.012H148V330.012H96Z" fill="#17181C"/>
|
||||
<path d="M148.004 330.012V278.012H200.004V330.012H148.004Z" fill="#17181C"/>
|
||||
<path d="M200.008 329.99V277.99H252.008V329.99H200.008Z" fill="#17181C"/>
|
||||
<path d="M251.996 330.012V278.012H303.996V330.012H251.996Z" fill="#17181C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1 +0,0 @@
|
||||
../../../ui/src/assets/favicon/favicon.ico
|
||||
@@ -1 +0,0 @@
|
||||
../../../ui/src/assets/favicon/favicon.svg
|
||||
|
Before Width: | Height: | Size: 42 B After Width: | Height: | Size: 269 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="400" height="400" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="400" height="400" fill="#0E0E0E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M312 340H88V60H312V340ZM256 116H144V284H256V116Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 42 B After Width: | Height: | Size: 269 B |
@@ -2,5 +2,4 @@ User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Disallow shared content pages
|
||||
Disallow: /s/
|
||||
Disallow: /share/
|
||||
Disallow: /s/
|
||||
@@ -1 +0,0 @@
|
||||
../../../ui/src/assets/favicon/site.webmanifest
|
||||
@@ -1 +0,0 @@
|
||||
../../../ui/src/assets/favicon/web-app-manifest-192x192.png
|
||||
@@ -1 +0,0 @@
|
||||
../../../ui/src/assets/favicon/web-app-manifest-512x512.png
|
||||
@@ -1,8 +1,7 @@
|
||||
import { MetaProvider, Title, Meta } from "@solidjs/meta"
|
||||
import { Router } from "@solidjs/router"
|
||||
import { FileRoutes } from "@solidjs/start/router"
|
||||
import { Suspense } from "solid-js"
|
||||
import { Favicon } from "@opencode-ai/ui/favicon"
|
||||
import { ErrorBoundary, Suspense } from "solid-js"
|
||||
import "@ibm/plex/css/ibm-plex.css"
|
||||
import "./app.css"
|
||||
|
||||
@@ -14,7 +13,6 @@ export default function App() {
|
||||
<MetaProvider>
|
||||
<Title>opencode</Title>
|
||||
<Meta name="description" content="OpenCode - The AI coding agent built for the terminal." />
|
||||
<Favicon />
|
||||
<Suspense>{props.children}</Suspense>
|
||||
</MetaProvider>
|
||||
)}
|
||||
|
||||
@@ -202,7 +202,7 @@ export function IconZai(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function IconGemini(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>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useSession } from "@solidjs/start/http"
|
||||
import { useSession } from "vinxi/http"
|
||||
|
||||
export interface AuthSession {
|
||||
account?: Record<
|
||||
|
||||
@@ -9,6 +9,7 @@ export default createHandler(
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<meta property="og:image" content="/social-share.png" />
|
||||
<meta property="twitter:image" content="/social-share.png" />
|
||||
{assets}
|
||||
|
||||
Vendored
-4
@@ -1,5 +1 @@
|
||||
/// <reference types="@solidjs/start/env" />
|
||||
|
||||
export declare module "@solidjs/start/server" {
|
||||
export type APIEvent = { request: Request }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createMiddleware } from "@solidjs/start/middleware"
|
||||
import { defineMiddleware } from "vinxi/http"
|
||||
|
||||
export default createMiddleware({
|
||||
export default defineMiddleware({
|
||||
onBeforeResponse() {},
|
||||
})
|
||||
|
||||
@@ -36,7 +36,6 @@ ${body.email}`.trim()
|
||||
to: "contact@anoma.ly",
|
||||
subject: `Enterprise Inquiry from ${body.name}`,
|
||||
body: emailContent,
|
||||
replyTo: body.email,
|
||||
})
|
||||
|
||||
return Response.json({ success: true, message: "Form submitted successfully" }, { status: 200 })
|
||||
|
||||
@@ -19,7 +19,6 @@ export async function GET(input: APIEvent) {
|
||||
return {
|
||||
...value,
|
||||
account: {
|
||||
...value.account,
|
||||
[id]: {
|
||||
id,
|
||||
email: decoded.subject.properties.email,
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { redirect } from "@solidjs/router"
|
||||
import { APIEvent } from "@solidjs/start"
|
||||
import { useAuthSession } from "~/context/auth.session"
|
||||
|
||||
export async function GET(event: APIEvent) {
|
||||
const auth = await useAuthSession()
|
||||
const current = auth.data.current
|
||||
if (current)
|
||||
await auth.update((val) => {
|
||||
delete val.account?.[current]
|
||||
const first = Object.keys(val.account ?? {})[0]
|
||||
val.current = first
|
||||
event!.locals.actor = undefined
|
||||
return val
|
||||
})
|
||||
return redirect("/zen")
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { APIEvent } from "@solidjs/start"
|
||||
import { useAuthSession } from "~/context/auth.session"
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
const session = await useAuthSession()
|
||||
return Response.json(session.data)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import "./index.css"
|
||||
import { Title, Meta, Link } from "@solidjs/meta"
|
||||
// import { HttpHeader } from "@solidjs/start"
|
||||
import { HttpHeader } from "@solidjs/start"
|
||||
import video from "../asset/lander/opencode-min.mp4"
|
||||
import videoPoster from "../asset/lander/opencode-poster.png"
|
||||
import { IconCopy, IconCheck } from "../component/icon"
|
||||
@@ -42,9 +42,10 @@ export default function Home() {
|
||||
|
||||
return (
|
||||
<main data-page="opencode">
|
||||
{/*<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />*/}
|
||||
<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />
|
||||
<Title>OpenCode | The AI coding agent built for the terminal</Title>
|
||||
<Link rel="canonical" href={config.baseUrl} />
|
||||
<Link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<Meta property="og:image" content="/social-share.png" />
|
||||
<Meta name="twitter:image" content="/social-share.png" />
|
||||
<div data-component="container">
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
[data-slot="item"] {
|
||||
color: var(--color-danger);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { action } from "@solidjs/router"
|
||||
import { action, redirect } from "@solidjs/router"
|
||||
import { getRequestEvent } from "solid-js/web"
|
||||
import { useAuthSession } from "~/context/auth.session"
|
||||
import { Dropdown } from "~/component/dropdown"
|
||||
@@ -17,15 +17,18 @@ const logout = action(async () => {
|
||||
event!.locals.actor = undefined
|
||||
return val
|
||||
})
|
||||
}, "auth.logout")
|
||||
throw redirect("/zen")
|
||||
})
|
||||
|
||||
export function UserMenu(props: { email: string | null | undefined }) {
|
||||
return (
|
||||
<div data-component="user-menu">
|
||||
<Dropdown trigger={props.email ?? ""} align="right">
|
||||
<a href="/auth/logout" data-slot="item">
|
||||
Logout
|
||||
</a>
|
||||
<form action={logout} method="post">
|
||||
<button type="submit" formaction={logout} data-slot="item">
|
||||
Logout
|
||||
</button>
|
||||
</form>
|
||||
</Dropdown>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ import "./workspace-picker.css"
|
||||
const getWorkspaces = query(async () => {
|
||||
"use server"
|
||||
return withActor(async () => {
|
||||
return Database.use((tx) =>
|
||||
return Database.transaction((tx) =>
|
||||
tx
|
||||
.select({
|
||||
id: WorkspaceTable.id,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { UserMenu } from "./user-menu"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { User } from "@opencode-ai/console-core/user.js"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { Link } from "@solidjs/meta"
|
||||
|
||||
const getUserEmail = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
@@ -21,6 +22,7 @@ export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||
const userEmail = createAsync(() => getUserEmail(params.id!))
|
||||
return (
|
||||
<main data-page="workspace">
|
||||
<Link rel="icon" type="image/svg+xml" href="/favicon-zen.svg" />
|
||||
<header data-component="workspace-header">
|
||||
<div data-slot="header-brand">
|
||||
<A href="/" data-component="site-title">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { querySessionInfo } from "../common"
|
||||
import {
|
||||
IconAlibaba,
|
||||
IconAnthropic,
|
||||
IconGemini,
|
||||
IconGoogle,
|
||||
IconMoonshotAI,
|
||||
IconOpenAI,
|
||||
IconStealth,
|
||||
@@ -117,7 +117,7 @@ export function ModelSection() {
|
||||
case "Anthropic":
|
||||
return <IconAnthropic width={16} height={16} />
|
||||
case "Google":
|
||||
return <IconGemini width={16} height={16} />
|
||||
return <IconGoogle width={16} height={16} />
|
||||
case "Moonshot AI":
|
||||
return <IconMoonshotAI width={16} height={16} />
|
||||
case "Z.ai":
|
||||
|
||||
@@ -8,7 +8,6 @@ import styles from "./provider-section.module.css"
|
||||
const PROVIDERS = [
|
||||
{ name: "OpenAI", key: "openai", prefix: "sk-" },
|
||||
{ name: "Anthropic", key: "anthropic", prefix: "sk-ant-" },
|
||||
{ name: "Google Gemini", key: "google", prefix: "AI" },
|
||||
] as const
|
||||
|
||||
type Provider = (typeof PROVIDERS)[number]
|
||||
|
||||
@@ -50,10 +50,6 @@ export function UsageSection() {
|
||||
return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWrite5mTokens ?? 0) + (u.cacheWrite1hTokens ?? 0)
|
||||
}
|
||||
|
||||
const calculateTotalOutputTokens = (u: Awaited<ReturnType<typeof getUsageInfo>>[0]) => {
|
||||
return u.outputTokens + (u.reasoningTokens ?? 0)
|
||||
}
|
||||
|
||||
const goPrev = async () => {
|
||||
const usage = await getUsageInfo(params.id!, store.page - 1)
|
||||
setStore({
|
||||
@@ -99,11 +95,8 @@ export function UsageSection() {
|
||||
{(usage, index) => {
|
||||
const date = createMemo(() => new Date(usage.timeCreated))
|
||||
const totalInputTokens = createMemo(() => calculateTotalInputTokens(usage))
|
||||
const totalOutputTokens = createMemo(() => calculateTotalOutputTokens(usage))
|
||||
const inputBreakdownId = `input-breakdown-${index()}`
|
||||
const outputBreakdownId = `output-breakdown-${index()}`
|
||||
const isInputOpen = createMemo(() => openBreakdownId() === inputBreakdownId)
|
||||
const isOutputOpen = createMemo(() => openBreakdownId() === outputBreakdownId)
|
||||
const breakdownId = `breakdown-${index()}`
|
||||
const isOpen = createMemo(() => openBreakdownId() === breakdownId)
|
||||
const isClaude = usage.model.toLowerCase().includes("claude")
|
||||
return (
|
||||
<tr>
|
||||
@@ -117,13 +110,13 @@ export function UsageSection() {
|
||||
data-slot="breakdown-button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setOpenBreakdownId(isInputOpen() ? null : inputBreakdownId)
|
||||
setOpenBreakdownId(isOpen() ? null : breakdownId)
|
||||
}}
|
||||
>
|
||||
<IconBreakdown />
|
||||
</button>
|
||||
<span onClick={() => setOpenBreakdownId(null)}>{totalInputTokens()}</span>
|
||||
<Show when={isInputOpen()}>
|
||||
<Show when={isOpen()}>
|
||||
<div data-slot="breakdown-popup" onClick={(e) => e.stopPropagation()}>
|
||||
<div data-slot="breakdown-row">
|
||||
<span data-slot="breakdown-label">Input</span>
|
||||
@@ -143,32 +136,7 @@ export function UsageSection() {
|
||||
</Show>
|
||||
</div>
|
||||
</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(isOutputOpen() ? null : outputBreakdownId)
|
||||
}}
|
||||
>
|
||||
<IconBreakdown />
|
||||
</button>
|
||||
<span onClick={() => setOpenBreakdownId(null)}>{totalOutputTokens()}</span>
|
||||
<Show when={isOutputOpen()}>
|
||||
<div data-slot="breakdown-popup" onClick={(e) => e.stopPropagation()}>
|
||||
<div data-slot="breakdown-row">
|
||||
<span data-slot="breakdown-label">Output</span>
|
||||
<span data-slot="breakdown-value">{usage.outputTokens}</span>
|
||||
</div>
|
||||
<div data-slot="breakdown-row">
|
||||
<span data-slot="breakdown-label">Reasoning</span>
|
||||
<span data-slot="breakdown-value">{usage.reasoningTokens ?? 0}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</td>
|
||||
<td data-slot="usage-tokens">{usage.outputTokens}</td>
|
||||
<td data-slot="usage-cost">${((usage.cost ?? 0) / 100000000).toFixed(4)}</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "./index.css"
|
||||
import { createAsync, query, redirect } from "@solidjs/router"
|
||||
import { Title, Meta, Link } from "@solidjs/meta"
|
||||
// import { HttpHeader } from "@solidjs/start"
|
||||
import { HttpHeader } from "@solidjs/start"
|
||||
import zenLogoLight from "../../asset/zen-ornate-light.svg"
|
||||
import { config } from "~/config"
|
||||
import zenLogoDark from "../../asset/zen-ornate-dark.svg"
|
||||
@@ -18,24 +18,23 @@ import { Legal } from "~/component/legal"
|
||||
import { Footer } from "~/component/footer"
|
||||
import { Header } from "~/component/header"
|
||||
import { getLastSeenWorkspaceID } from "../workspace/common"
|
||||
import { IconGemini, IconZai } from "~/component/icon"
|
||||
|
||||
const checkLoggedIn = query(async () => {
|
||||
"use server"
|
||||
const workspaceID = await getLastSeenWorkspaceID().catch(() => {})
|
||||
const workspaceID = await getLastSeenWorkspaceID()
|
||||
if (workspaceID) throw redirect(`/workspace/${workspaceID}`)
|
||||
}, "checkLoggedIn.get")
|
||||
|
||||
export default function Home() {
|
||||
const loggedin = createAsync(() => checkLoggedIn())
|
||||
createAsync(() => checkLoggedIn())
|
||||
return (
|
||||
<main data-page="zen">
|
||||
{/*<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />*/}
|
||||
<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />
|
||||
<Title>OpenCode Zen | A curated set of reliable optimized models for coding agents</Title>
|
||||
<Link rel="canonical" href={`${config.baseUrl}/zen`} />
|
||||
<Link rel="icon" type="image/svg+xml" href="/favicon-zen.svg" />
|
||||
<Meta property="og:image" content="/social-share-zen.png" />
|
||||
<Meta name="twitter:image" content="/social-share-zen.png" />
|
||||
<Meta name="opencode:auth" content={loggedin() ? "true" : "false"} />
|
||||
|
||||
<div data-component="container">
|
||||
<Header zen />
|
||||
@@ -82,9 +81,6 @@ export default function Home() {
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<IconGemini width="24" height="24" />
|
||||
</div>
|
||||
<div>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
@@ -115,9 +111,6 @@ export default function Home() {
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<IconZai width="24" height="24" />
|
||||
</div>
|
||||
</div>
|
||||
<a href="/auth">
|
||||
<span>Get started with Zen </span>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Resource, waitUntil } from "@opencode-ai/console-resource"
|
||||
|
||||
export function createDataDumper(sessionId: string, requestId: string) {
|
||||
if (Resource.App.stage !== "production") return
|
||||
|
||||
let data: Record<string, any> = {}
|
||||
let modelName: string | undefined
|
||||
|
||||
return {
|
||||
provideModel: (model?: string) => (modelName = model),
|
||||
provideRequest: (request: string) => (data.request = request),
|
||||
provideResponse: (response: string) => (data.response = response),
|
||||
provideStream: (chunk: string) => (data.response = (data.response ?? "") + chunk),
|
||||
flush: () => {
|
||||
if (!modelName) return
|
||||
|
||||
const str = new Date().toISOString().replace(/[^0-9]/g, "")
|
||||
const yyyymmdd = str.substring(0, 8)
|
||||
const hh = str.substring(8, 10)
|
||||
|
||||
waitUntil(
|
||||
Resource.ConsoleData.put(`${yyyymmdd}/${hh}/${modelName}/${sessionId}/${requestId}.json`, JSON.stringify(data)),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -13,20 +13,12 @@ import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
|
||||
import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js"
|
||||
import { logger } from "./logger"
|
||||
import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError, RateLimitError } from "./error"
|
||||
import {
|
||||
createBodyConverter,
|
||||
createStreamPartConverter,
|
||||
createResponseConverter,
|
||||
ProviderHelper,
|
||||
UsageInfo,
|
||||
} from "./provider/provider"
|
||||
import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider"
|
||||
import { anthropicHelper } from "./provider/anthropic"
|
||||
import { googleHelper } from "./provider/google"
|
||||
import { openaiHelper } from "./provider/openai"
|
||||
import { oaCompatHelper } from "./provider/openai-compatible"
|
||||
import { createRateLimiter } from "./rateLimiter"
|
||||
import { createDataDumper } from "./dataDumper"
|
||||
import { createTrialLimiter } from "./trialLimiter"
|
||||
|
||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||
type RetryOptions = {
|
||||
@@ -56,26 +48,21 @@ export async function handler(
|
||||
try {
|
||||
const url = input.request.url
|
||||
const body = await input.request.json()
|
||||
const ip = input.request.headers.get("x-real-ip") ?? ""
|
||||
const model = opts.parseModel(url, body)
|
||||
const isStream = opts.parseIsStream(url, body)
|
||||
const ip = input.request.headers.get("x-real-ip") ?? ""
|
||||
const sessionId = input.request.headers.get("x-opencode-session") ?? ""
|
||||
const requestId = input.request.headers.get("x-opencode-request") ?? ""
|
||||
logger.metric({
|
||||
is_tream: isStream,
|
||||
session: sessionId,
|
||||
request: requestId,
|
||||
session: input.request.headers.get("x-opencode-session"),
|
||||
request: input.request.headers.get("x-opencode-request"),
|
||||
})
|
||||
const zenData = ZenData.list()
|
||||
const modelInfo = validateModel(zenData, model)
|
||||
const dataDumper = createDataDumper(sessionId, requestId)
|
||||
const trialLimiter = createTrialLimiter(modelInfo.trial?.limit, ip)
|
||||
const isTrial = await trialLimiter?.isTrial()
|
||||
const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
|
||||
await rateLimiter?.check()
|
||||
|
||||
const retriableRequest = async (retry: RetryOptions = { excludeProviders: [], retryCount: 0 }) => {
|
||||
const providerInfo = selectProvider(zenData, modelInfo, sessionId, isTrial ?? false, retry)
|
||||
const providerInfo = selectProvider(zenData, modelInfo, ip, retry)
|
||||
const authInfo = await authenticate(modelInfo, providerInfo)
|
||||
validateBilling(authInfo, modelInfo)
|
||||
validateModelSettings(authInfo)
|
||||
@@ -117,14 +104,10 @@ export async function handler(
|
||||
})
|
||||
}
|
||||
|
||||
return { providerInfo, authInfo, reqBody, res, startTimestamp }
|
||||
return { providerInfo, authInfo, res, startTimestamp }
|
||||
}
|
||||
|
||||
const { providerInfo, authInfo, reqBody, res, startTimestamp } = await retriableRequest()
|
||||
|
||||
// Store model request
|
||||
dataDumper?.provideModel(providerInfo.storeModel)
|
||||
dataDumper?.provideRequest(reqBody)
|
||||
const { providerInfo, authInfo, res, startTimestamp } = await retriableRequest()
|
||||
|
||||
// Scrub response headers
|
||||
const resHeaders = new Headers()
|
||||
@@ -143,12 +126,8 @@ export async function handler(
|
||||
const body = JSON.stringify(responseConverter(json))
|
||||
logger.metric({ response_length: body.length })
|
||||
logger.debug("RESPONSE: " + body)
|
||||
dataDumper?.provideResponse(body)
|
||||
dataDumper?.flush()
|
||||
const tokensInfo = providerInfo.normalizeUsage(json.usage)
|
||||
await trialLimiter?.track(tokensInfo)
|
||||
await rateLimiter?.track()
|
||||
await trackUsage(authInfo, modelInfo, providerInfo, tokensInfo)
|
||||
await trackUsage(authInfo, modelInfo, providerInfo, json.usage)
|
||||
await reload(authInfo)
|
||||
return new Response(body, {
|
||||
status: res.status,
|
||||
@@ -176,13 +155,10 @@ export async function handler(
|
||||
response_length: responseLength,
|
||||
"timestamp.last_byte": Date.now(),
|
||||
})
|
||||
dataDumper?.flush()
|
||||
await rateLimiter?.track()
|
||||
const usage = usageParser.retrieve()
|
||||
if (usage) {
|
||||
const tokensInfo = providerInfo.normalizeUsage(usage)
|
||||
await trialLimiter?.track(tokensInfo)
|
||||
await trackUsage(authInfo, modelInfo, providerInfo, tokensInfo)
|
||||
await trackUsage(authInfo, modelInfo, providerInfo, usage)
|
||||
await reload(authInfo)
|
||||
}
|
||||
c.close()
|
||||
@@ -198,7 +174,6 @@ export async function handler(
|
||||
}
|
||||
responseLength += value.length
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
dataDumper?.provideStream(buffer)
|
||||
|
||||
const parts = buffer.split(providerInfo.streamSeparator)
|
||||
buffer = parts.pop() ?? ""
|
||||
@@ -288,18 +263,8 @@ export async function handler(
|
||||
return { id: modelId, ...modelData }
|
||||
}
|
||||
|
||||
function selectProvider(
|
||||
zenData: ZenData,
|
||||
modelInfo: ModelInfo,
|
||||
sessionId: string,
|
||||
isTrial: boolean,
|
||||
retry: RetryOptions,
|
||||
) {
|
||||
function selectProvider(zenData: ZenData, modelInfo: ModelInfo, ip: string, retry: RetryOptions) {
|
||||
const provider = (() => {
|
||||
if (isTrial) {
|
||||
return modelInfo.providers.find((provider) => provider.id === modelInfo.trial!.provider)
|
||||
}
|
||||
|
||||
if (retry.retryCount === MAX_RETRIES) {
|
||||
return modelInfo.providers.find((provider) => provider.id === modelInfo.fallbackProvider)
|
||||
}
|
||||
@@ -309,13 +274,9 @@ export async function handler(
|
||||
.filter((provider) => !retry.excludeProviders.includes(provider.id))
|
||||
.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
|
||||
|
||||
// Use the last 4 characters of session ID to select a provider
|
||||
let h = 0
|
||||
const l = sessionId.length
|
||||
for (let i = l - 4; i < l; i++) {
|
||||
h = (h * 31 + sessionId.charCodeAt(i)) | 0 // 32-bit int
|
||||
}
|
||||
const index = (h >>> 0) % providers.length // make unsigned + range 0..length-1
|
||||
// Use the last 2 characters of IP address to select a provider
|
||||
const lastChars = ip.slice(-2)
|
||||
const index = parseInt(lastChars, 16) % providers.length
|
||||
return providers[index || 0]
|
||||
})()
|
||||
|
||||
@@ -455,14 +416,9 @@ export async function handler(
|
||||
providerInfo.apiKey = authInfo.provider.credentials
|
||||
}
|
||||
|
||||
async function trackUsage(
|
||||
authInfo: AuthInfo,
|
||||
modelInfo: ModelInfo,
|
||||
providerInfo: ProviderInfo,
|
||||
usageInfo: UsageInfo,
|
||||
) {
|
||||
async function trackUsage(authInfo: AuthInfo, modelInfo: ModelInfo, providerInfo: ProviderInfo, usage: any) {
|
||||
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
|
||||
usageInfo
|
||||
providerInfo.normalizeUsage(usage)
|
||||
|
||||
const modelCost =
|
||||
modelInfo.cost200K &&
|
||||
|
||||
@@ -24,15 +24,6 @@ import {
|
||||
toOaCompatibleResponse,
|
||||
} from "./openai-compatible"
|
||||
|
||||
export type UsageInfo = {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
reasoningTokens?: number
|
||||
cacheReadTokens?: number
|
||||
cacheWrite5mTokens?: number
|
||||
cacheWrite1hTokens?: number
|
||||
}
|
||||
|
||||
export type ProviderHelper = {
|
||||
format: ZenData.Format
|
||||
modifyUrl: (providerApi: string, model?: string, isStream?: boolean) => string
|
||||
@@ -43,7 +34,14 @@ export type ProviderHelper = {
|
||||
parse: (chunk: string) => void
|
||||
retrieve: () => any
|
||||
}
|
||||
normalizeUsage: (usage: any) => UsageInfo
|
||||
normalizeUsage: (usage: any) => {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
reasoningTokens?: number
|
||||
cacheReadTokens?: number
|
||||
cacheWrite5mTokens?: number
|
||||
cacheWrite1hTokens?: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommonMessage {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Database, eq, sql } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { IpTable } from "@opencode-ai/console-core/schema/ip.sql.js"
|
||||
import { UsageInfo } from "./provider/provider"
|
||||
|
||||
export function createTrialLimiter(limit: number | undefined, ip: string) {
|
||||
if (!limit) return
|
||||
if (!ip) return
|
||||
|
||||
let trial: boolean
|
||||
|
||||
return {
|
||||
isTrial: async () => {
|
||||
const data = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
usage: IpTable.usage,
|
||||
})
|
||||
.from(IpTable)
|
||||
.where(eq(IpTable.ip, ip))
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
|
||||
trial = (data?.usage ?? 0) < limit
|
||||
return trial
|
||||
},
|
||||
track: async (usageInfo: UsageInfo) => {
|
||||
if (!trial) return
|
||||
const usage =
|
||||
usageInfo.inputTokens +
|
||||
usageInfo.outputTokens +
|
||||
(usageInfo.reasoningTokens ?? 0) +
|
||||
(usageInfo.cacheReadTokens ?? 0) +
|
||||
(usageInfo.cacheWrite5mTokens ?? 0) +
|
||||
(usageInfo.cacheWrite1hTokens ?? 0)
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.insert(IpTable)
|
||||
.values({ ip, usage })
|
||||
.onDuplicateKeyUpdate({ set: { usage: sql`${IpTable.usage} + ${usage}` } }),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"types": ["vite/client"],
|
||||
"types": ["vinxi/types/client"],
|
||||
"isolatedModules": true,
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { defineConfig, PluginOption } from "vite"
|
||||
import { solidStart } from "@solidjs/start/config"
|
||||
import { nitro } from "nitro/vite"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
solidStart() as PluginOption,
|
||||
nitro({
|
||||
compatibilityDate: "2024-09-19",
|
||||
preset: "cloudflare_module",
|
||||
cloudflare: {
|
||||
nodeCompat: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
allowedHosts: true,
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
external: ["cloudflare:workers"],
|
||||
},
|
||||
minify: false,
|
||||
},
|
||||
})
|
||||
@@ -1,8 +0,0 @@
|
||||
CREATE TABLE `ip` (
|
||||
`ip` varchar(45) NOT NULL,
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
`usage` int,
|
||||
CONSTRAINT `ip_ip_pk` PRIMARY KEY(`ip`)
|
||||
);
|
||||
@@ -1,981 +0,0 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "9d5d9885-7ec5-45f6-ac53-45a8e25dede7",
|
||||
"prevId": "8b7fa839-a088-408e-84a4-1a07325c0290",
|
||||
"tables": {
|
||||
"account": {
|
||||
"name": "account",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"account_id_pk": {
|
||||
"name": "account_id_pk",
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"auth": {
|
||||
"name": "auth",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "enum('email','github','google')",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"subject": {
|
||||
"name": "subject",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"columns": ["provider", "subject"],
|
||||
"isUnique": true
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"auth_id_pk": {
|
||||
"name": "auth_id_pk",
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"billing": {
|
||||
"name": "billing",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_method_id": {
|
||||
"name": "payment_method_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_method_type": {
|
||||
"name": "payment_method_type",
|
||||
"type": "varchar(32)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_method_last4": {
|
||||
"name": "payment_method_last4",
|
||||
"type": "varchar(4)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"balance": {
|
||||
"name": "balance",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_limit": {
|
||||
"name": "monthly_limit",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_usage": {
|
||||
"name": "monthly_usage",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_monthly_usage_updated": {
|
||||
"name": "time_monthly_usage_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload": {
|
||||
"name": "reload",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload_trigger": {
|
||||
"name": "reload_trigger",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload_amount": {
|
||||
"name": "reload_amount",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload_error": {
|
||||
"name": "reload_error",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_reload_error": {
|
||||
"name": "time_reload_error",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_reload_locked_till": {
|
||||
"name": "time_reload_locked_till",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"payment": {
|
||||
"name": "payment",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"invoice_id": {
|
||||
"name": "invoice_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_refunded": {
|
||||
"name": "time_refunded",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"usage": {
|
||||
"name": "usage",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reasoning_tokens": {
|
||||
"name": "reasoning_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_read_tokens": {
|
||||
"name": "cache_read_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_5m_tokens": {
|
||||
"name": "cache_write_5m_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_1h_tokens": {
|
||||
"name": "cache_write_1h_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cost": {
|
||||
"name": "cost",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key_id": {
|
||||
"name": "key_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"ip": {
|
||||
"name": "ip",
|
||||
"columns": {
|
||||
"ip": {
|
||||
"name": "ip",
|
||||
"type": "varchar(45)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"usage": {
|
||||
"name": "usage",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"ip_ip_pk": {
|
||||
"name": "ip_ip_pk",
|
||||
"columns": ["ip"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_used": {
|
||||
"name": "time_used",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"credentials": {
|
||||
"name": "credentials",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "enum('admin','member')",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_limit": {
|
||||
"name": "monthly_limit",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_usage": {
|
||||
"name": "monthly_usage",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_monthly_usage_updated": {
|
||||
"name": "time_monthly_usage_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"workspace": {
|
||||
"name": "workspace",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -267,13 +267,6 @@
|
||||
"when": 1761928273807,
|
||||
"tag": "0037_messy_jackal",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 38,
|
||||
"version": "5",
|
||||
"when": 1764110043942,
|
||||
"tag": "0038_famous_magik",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.127",
|
||||
"version": "1.0.85",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
@@ -30,7 +30,6 @@
|
||||
"update-models": "script/update-models.ts",
|
||||
"promote-models-to-dev": "script/promote-models.ts dev",
|
||||
"promote-models-to-prod": "script/promote-models.ts production",
|
||||
"pull-models-from-dev": "script/pull-models.ts dev",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -11,21 +11,20 @@ const root = path.resolve(process.cwd(), "..", "..", "..")
|
||||
|
||||
// read the secret
|
||||
const ret = await $`bun sst secret list`.cwd(root).text()
|
||||
const lines = ret.split("\n")
|
||||
const value1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[1]
|
||||
const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
|
||||
const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
|
||||
const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
|
||||
const value1 = ret
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS1"))
|
||||
?.split("=")[1]
|
||||
const value2 = ret
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS2"))
|
||||
?.split("=")[1]
|
||||
if (!value1) throw new Error("ZEN_MODELS1 not found")
|
||||
if (!value2) throw new Error("ZEN_MODELS2 not found")
|
||||
if (!value3) throw new Error("ZEN_MODELS3 not found")
|
||||
if (!value4) throw new Error("ZEN_MODELS4 not found")
|
||||
|
||||
// validate value
|
||||
ZenData.validate(JSON.parse(value1 + value2 + value3 + value4))
|
||||
ZenData.validate(JSON.parse(value1 + value2))
|
||||
|
||||
// update the secret
|
||||
await $`bun sst secret set ZEN_MODELS1 ${value1} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${value2} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS3 ${value3} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS4 ${value4} --stage ${stage}`
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { $ } from "bun"
|
||||
import path from "path"
|
||||
import { ZenData } from "../src/model"
|
||||
|
||||
const stage = process.argv[2]
|
||||
if (!stage) throw new Error("Stage is required")
|
||||
|
||||
const root = path.resolve(process.cwd(), "..", "..", "..")
|
||||
|
||||
// read the secret
|
||||
const ret = await $`bun sst secret list --stage ${stage}`.cwd(root).text()
|
||||
const lines = ret.split("\n")
|
||||
const value1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[1]
|
||||
const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
|
||||
const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
|
||||
const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
|
||||
if (!value1) throw new Error("ZEN_MODELS1 not found")
|
||||
if (!value2) throw new Error("ZEN_MODELS2 not found")
|
||||
if (!value3) throw new Error("ZEN_MODELS3 not found")
|
||||
if (!value4) throw new Error("ZEN_MODELS4 not found")
|
||||
|
||||
// validate value
|
||||
ZenData.validate(JSON.parse(value1 + value2 + value3 + value4))
|
||||
|
||||
// update the secret
|
||||
await $`bun sst secret set ZEN_MODELS1 ${value1}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${value2}`
|
||||
await $`bun sst secret set ZEN_MODELS3 ${value3}`
|
||||
await $`bun sst secret set ZEN_MODELS4 ${value4}`
|
||||
@@ -9,20 +9,21 @@ const root = path.resolve(process.cwd(), "..", "..", "..")
|
||||
const models = await $`bun sst secret list`.cwd(root).text()
|
||||
|
||||
// read the line starting with "ZEN_MODELS"
|
||||
const lines = models.split("\n")
|
||||
const oldValue1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[1]
|
||||
const oldValue2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
|
||||
const oldValue3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
|
||||
const oldValue4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
|
||||
const oldValue1 = models
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS1"))
|
||||
?.split("=")[1]
|
||||
const oldValue2 = models
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS2"))
|
||||
?.split("=")[1]
|
||||
if (!oldValue1) throw new Error("ZEN_MODELS1 not found")
|
||||
if (!oldValue2) throw new Error("ZEN_MODELS2 not found")
|
||||
if (!oldValue3) throw new Error("ZEN_MODELS3 not found")
|
||||
if (!oldValue4) throw new Error("ZEN_MODELS4 not found")
|
||||
|
||||
// store the prettified json to a temp file
|
||||
const filename = `models-${Date.now()}.json`
|
||||
const tempFile = Bun.file(path.join(os.tmpdir(), filename))
|
||||
await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4), null, 2))
|
||||
await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2), null, 2))
|
||||
console.log("tempFile", tempFile.name)
|
||||
|
||||
// open temp file in vim and read the file on close
|
||||
@@ -31,12 +32,6 @@ const newValue = JSON.stringify(JSON.parse(await tempFile.text()))
|
||||
ZenData.validate(JSON.parse(newValue))
|
||||
|
||||
// update the secret
|
||||
const chunk = Math.ceil(newValue.length / 4)
|
||||
const newValue1 = newValue.slice(0, chunk)
|
||||
const newValue2 = newValue.slice(chunk, chunk * 2)
|
||||
const newValue3 = newValue.slice(chunk * 2, chunk * 3)
|
||||
const newValue4 = newValue.slice(chunk * 3)
|
||||
await $`bun sst secret set ZEN_MODELS1 ${newValue1}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${newValue2}`
|
||||
await $`bun sst secret set ZEN_MODELS3 ${newValue3}`
|
||||
await $`bun sst secret set ZEN_MODELS4 ${newValue4}`
|
||||
const mid = Math.floor(newValue.length / 2)
|
||||
await $`bun sst secret set ZEN_MODELS1 ${newValue.slice(0, mid)}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${newValue.slice(mid)}`
|
||||
|
||||
@@ -11,7 +11,7 @@ export namespace Account {
|
||||
id: z.string().optional(),
|
||||
}),
|
||||
async (input) =>
|
||||
Database.use(async (tx) => {
|
||||
Database.transaction(async (tx) => {
|
||||
const id = input.id ?? Identifier.create("account")
|
||||
await tx.insert(AccountTable).values({
|
||||
id,
|
||||
@@ -21,12 +21,13 @@ export namespace Account {
|
||||
)
|
||||
|
||||
export const fromID = fn(z.string(), async (id) =>
|
||||
Database.use((tx) =>
|
||||
tx
|
||||
Database.transaction(async (tx) => {
|
||||
return tx
|
||||
.select()
|
||||
.from(AccountTable)
|
||||
.where(eq(AccountTable.id, id))
|
||||
.then((rows) => rows[0]),
|
||||
),
|
||||
.execute()
|
||||
.then((rows) => rows[0])
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ export namespace AWS {
|
||||
to: z.string(),
|
||||
subject: z.string(),
|
||||
body: z.string(),
|
||||
replyTo: z.string().optional(),
|
||||
}),
|
||||
async (input) => {
|
||||
const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", {
|
||||
@@ -36,7 +35,6 @@ export namespace AWS {
|
||||
Destination: {
|
||||
ToAddresses: [input.to],
|
||||
},
|
||||
...(input.replyTo && { ReplyToAddresses: [input.replyTo] }),
|
||||
Content: {
|
||||
Simple: {
|
||||
Subject: {
|
||||
|
||||
@@ -24,12 +24,6 @@ export namespace ZenData {
|
||||
cost: ModelCostSchema,
|
||||
cost200K: ModelCostSchema.optional(),
|
||||
allowAnonymous: z.boolean().optional(),
|
||||
trial: z
|
||||
.object({
|
||||
limit: z.number(),
|
||||
provider: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
rateLimit: z.number().optional(),
|
||||
fallbackProvider: z.string().optional(),
|
||||
providers: z.array(
|
||||
@@ -38,7 +32,6 @@ export namespace ZenData {
|
||||
model: z.string(),
|
||||
weight: z.number().optional(),
|
||||
disabled: z.boolean().optional(),
|
||||
storeModel: z.string().optional(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
@@ -60,9 +53,7 @@ export namespace ZenData {
|
||||
})
|
||||
|
||||
export const list = fn(z.void(), () => {
|
||||
const json = JSON.parse(
|
||||
Resource.ZEN_MODELS1.value + Resource.ZEN_MODELS2.value + Resource.ZEN_MODELS3.value + Resource.ZEN_MODELS4.value,
|
||||
)
|
||||
const json = JSON.parse(Resource.ZEN_MODELS1.value + Resource.ZEN_MODELS2.value)
|
||||
return ModelsSchema.parse(json)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export namespace Provider {
|
||||
}),
|
||||
async ({ provider }) => {
|
||||
Actor.assertAdmin()
|
||||
Database.use((tx) =>
|
||||
return Database.transaction((tx) =>
|
||||
tx
|
||||
.delete(ProviderTable)
|
||||
.where(and(eq(ProviderTable.provider, provider), eq(ProviderTable.workspaceID, Actor.workspace()))),
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { mysqlTable, int, primaryKey, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps } from "../drizzle/types"
|
||||
|
||||
export const IpTable = mysqlTable(
|
||||
"ip",
|
||||
{
|
||||
ip: varchar("ip", { length: 45 }).notNull(),
|
||||
...timestamps,
|
||||
usage: int("usage"),
|
||||
},
|
||||
(table) => [primaryKey({ columns: [table.ip] })],
|
||||
)
|
||||
Vendored
-18
@@ -74,14 +74,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
R2AccessKey: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
R2SecretKey: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
STRIPE_SECRET_KEY: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
@@ -102,14 +94,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
// cloudflare
|
||||
@@ -120,8 +104,6 @@ declare module "sst" {
|
||||
AuthApi: cloudflare.Service
|
||||
AuthStorage: cloudflare.KVNamespace
|
||||
Bucket: cloudflare.R2Bucket
|
||||
ConsoleData: cloudflare.R2Bucket
|
||||
EnterpriseStorage: cloudflare.R2Bucket
|
||||
GatewayKv: cloudflare.KVNamespace
|
||||
LogProcessor: cloudflare.Service
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.127",
|
||||
"version": "1.0.85",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -194,7 +194,7 @@ export default {
|
||||
// Get workspace
|
||||
await Actor.provide("account", { accountID, email }, async () => {
|
||||
await User.joinInvitedWorkspaces()
|
||||
const workspaces = await Database.use((tx) =>
|
||||
const workspaces = await Database.transaction(async (tx) =>
|
||||
tx
|
||||
.select({ id: WorkspaceTable.id })
|
||||
.from(WorkspaceTable)
|
||||
|
||||
-18
@@ -74,14 +74,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
R2AccessKey: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
R2SecretKey: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
STRIPE_SECRET_KEY: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
@@ -102,14 +94,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
// cloudflare
|
||||
@@ -120,8 +104,6 @@ declare module "sst" {
|
||||
AuthApi: cloudflare.Service
|
||||
AuthStorage: cloudflare.KVNamespace
|
||||
Bucket: cloudflare.R2Bucket
|
||||
ConsoleData: cloudflare.R2Bucket
|
||||
EnterpriseStorage: cloudflare.R2Bucket
|
||||
GatewayKv: cloudflare.KVNamespace
|
||||
LogProcessor: cloudflare.Service
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.127",
|
||||
"version": "1.0.85",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { env } from "cloudflare:workers"
|
||||
export { waitUntil } from "cloudflare:workers"
|
||||
|
||||
export const Resource = new Proxy(
|
||||
{},
|
||||
|
||||
@@ -2,66 +2,54 @@ import type { KVNamespaceListOptions, KVNamespaceListResult, KVNamespacePutOptio
|
||||
import { Resource as ResourceBase } from "sst"
|
||||
import Cloudflare from "cloudflare"
|
||||
|
||||
export const waitUntil = async (fn: () => Promise<void>) => {
|
||||
await fn()
|
||||
}
|
||||
|
||||
export const Resource = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_target, prop: keyof typeof ResourceBase) {
|
||||
const value = ResourceBase[prop]
|
||||
if ("type" in value) {
|
||||
// @ts-ignore
|
||||
if ("type" in value && value.type === "sst.cloudflare.Kv") {
|
||||
const client = new Cloudflare({
|
||||
apiToken: ResourceBase.CLOUDFLARE_API_TOKEN.value,
|
||||
})
|
||||
// @ts-ignore
|
||||
if (value.type === "sst.cloudflare.Bucket") {
|
||||
return {
|
||||
put: async () => {},
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
if (value.type === "sst.cloudflare.Kv") {
|
||||
const client = new Cloudflare({
|
||||
apiToken: ResourceBase.CLOUDFLARE_API_TOKEN.value,
|
||||
})
|
||||
// @ts-ignore
|
||||
const namespaceId = value.namespaceId
|
||||
const accountId = ResourceBase.CLOUDFLARE_DEFAULT_ACCOUNT_ID.value
|
||||
return {
|
||||
get: (k: string | string[]) => {
|
||||
const isMulti = Array.isArray(k)
|
||||
return client.kv.namespaces
|
||||
.bulkGet(namespaceId, {
|
||||
keys: Array.isArray(k) ? k : [k],
|
||||
account_id: accountId,
|
||||
})
|
||||
.then((result) => (isMulti ? new Map(Object.entries(result?.values ?? {})) : result?.values?.[k]))
|
||||
},
|
||||
put: (k: string, v: string, opts?: KVNamespacePutOptions) =>
|
||||
client.kv.namespaces.values.update(namespaceId, k, {
|
||||
const namespaceId = value.namespaceId
|
||||
const accountId = ResourceBase.CLOUDFLARE_DEFAULT_ACCOUNT_ID.value
|
||||
return {
|
||||
get: (k: string | string[]) => {
|
||||
const isMulti = Array.isArray(k)
|
||||
return client.kv.namespaces
|
||||
.bulkGet(namespaceId, {
|
||||
keys: Array.isArray(k) ? k : [k],
|
||||
account_id: accountId,
|
||||
value: v,
|
||||
expiration: opts?.expiration,
|
||||
expiration_ttl: opts?.expirationTtl,
|
||||
metadata: opts?.metadata,
|
||||
}),
|
||||
delete: (k: string) =>
|
||||
client.kv.namespaces.values.delete(namespaceId, k, {
|
||||
})
|
||||
.then((result) => (isMulti ? new Map(Object.entries(result?.values ?? {})) : result?.values?.[k]))
|
||||
},
|
||||
put: (k: string, v: string, opts?: KVNamespacePutOptions) =>
|
||||
client.kv.namespaces.values.update(namespaceId, k, {
|
||||
account_id: accountId,
|
||||
value: v,
|
||||
expiration: opts?.expiration,
|
||||
expiration_ttl: opts?.expirationTtl,
|
||||
metadata: opts?.metadata,
|
||||
}),
|
||||
delete: (k: string) =>
|
||||
client.kv.namespaces.values.delete(namespaceId, k, {
|
||||
account_id: accountId,
|
||||
}),
|
||||
list: (opts?: KVNamespaceListOptions): Promise<KVNamespaceListResult<unknown, string>> =>
|
||||
client.kv.namespaces.keys
|
||||
.list(namespaceId, {
|
||||
account_id: accountId,
|
||||
prefix: opts?.prefix ?? undefined,
|
||||
})
|
||||
.then((result) => {
|
||||
return {
|
||||
keys: result.result,
|
||||
list_complete: true,
|
||||
cacheStatus: null,
|
||||
}
|
||||
}),
|
||||
list: (opts?: KVNamespaceListOptions): Promise<KVNamespaceListResult<unknown, string>> =>
|
||||
client.kv.namespaces.keys
|
||||
.list(namespaceId, {
|
||||
account_id: accountId,
|
||||
prefix: opts?.prefix ?? undefined,
|
||||
})
|
||||
.then((result) => {
|
||||
return {
|
||||
keys: result.result,
|
||||
list_complete: true,
|
||||
cacheStatus: null,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
|
||||
-18
@@ -74,14 +74,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
R2AccessKey: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
R2SecretKey: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
STRIPE_SECRET_KEY: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
@@ -102,14 +94,6 @@ declare module "sst" {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS3: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
ZEN_MODELS4: {
|
||||
type: "sst.sst.Secret"
|
||||
value: string
|
||||
}
|
||||
}
|
||||
}
|
||||
// cloudflare
|
||||
@@ -120,8 +104,6 @@ declare module "sst" {
|
||||
AuthApi: cloudflare.Service
|
||||
AuthStorage: cloudflare.KVNamespace
|
||||
Bucket: cloudflare.R2Bucket
|
||||
ConsoleData: cloudflare.R2Bucket
|
||||
EnterpriseStorage: cloudflare.R2Bucket
|
||||
GatewayKv: cloudflare.KVNamespace
|
||||
LogProcessor: cloudflare.Service
|
||||
}
|
||||
|
||||
@@ -3,16 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="shortcut icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>OpenCode</title>
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<meta name="theme-color" content="#F8F7F7" />
|
||||
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
|
||||
<meta property="og:image" content="/social-share.png" />
|
||||
<meta property="twitter:image" content="/social-share.png" />
|
||||
</head>
|
||||
<body class="antialiased overscroll-none select-none text-12-regular">
|
||||
<script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.127",
|
||||
"version": "1.0.85",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -14,7 +14,7 @@
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
"@tsconfig/bun": "1.0.9",
|
||||
"@types/luxon": "catalog:",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/node": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -26,7 +26,6 @@
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"@shikijs/transformers": "3.9.2",
|
||||
"@solid-primitives/active-element": "2.1.3",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
@@ -34,7 +33,7 @@
|
||||
"@solid-primitives/scroll": "2.1.3",
|
||||
"@solid-primitives/storage": "4.3.3",
|
||||
"@solidjs/meta": "catalog:",
|
||||
"@solidjs/router": "catalog:",
|
||||
"@solidjs/router": "0.15.3",
|
||||
"@thisbeyond/solid-dnd": "0.7.5",
|
||||
"diff": "catalog:",
|
||||
"fuzzysort": "catalog:",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/apple-touch-icon.png
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon-96x96.png
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/favicon.ico
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/site.webmanifest
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.7 KiB |
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/web-app-manifest-192x192.png
|
||||
@@ -1 +0,0 @@
|
||||
../../ui/src/assets/favicon/web-app-manifest-512x512.png
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user