mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-03 08:46:15 -04:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5199141369 | |||
| d86d3e7ea1 | |||
| fe8d29cb2b | |||
| edd6198999 | |||
| c3b2c27997 | |||
| 679aeb29f0 | |||
| 190413580f | |||
| 8c9fbc7717 | |||
| 9d3fdda674 | |||
| 449994f120 | |||
| d772fff776 | |||
| 71b43fd02e | |||
| f40b91ab7a | |||
| 6404bd006d | |||
| 75157e515c | |||
| 5a96ee8e1b | |||
| ee6ceb4c64 | |||
| 9d53628e19 | |||
| 869b476145 | |||
| 223d487787 | |||
| 5ead6d7dd5 | |||
| a98454217f | |||
| cbb75d8577 | |||
| 4ab992a9a9 | |||
| 80b0a93d64 | |||
| e749d48534 | |||
| d7e873f807 | |||
| c23510346b | |||
| f9c5df05a1 | |||
| 02b4d1e2fc | |||
| 0b36eb8760 | |||
| 36bec9948c | |||
| 2db73c39df | |||
| 6107666d04 | |||
| cc2bd7141f | |||
| ee442975df | |||
| 9b1a508657 | |||
| 288c977596 | |||
| 6b799b304c | |||
| 92c126d875 | |||
| 7123fbeb47 | |||
| 84bb692193 | |||
| 079095d7a9 | |||
| 28e1d67ea4 | |||
| c1940d1d2c | |||
| 869f629c14 | |||
| a55943e469 | |||
| 84d95a0d2a | |||
| 7dfed8ca35 | |||
| 38ea0fc051 | |||
| 9223b6ed8f | |||
| f8528c52d9 | |||
| d63ce40af2 | |||
| 5acdd70587 |
@@ -0,0 +1,50 @@
|
||||
name: snapshot
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- opentui
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- run: git fetch --force --tags
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ">=1.24.0"
|
||||
cache: true
|
||||
cache-dependency-path: go.sum
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.2.21
|
||||
|
||||
- name: Cache ~/.bun
|
||||
id: cache-bun
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.bun
|
||||
key: ${{ runner.os }}-bun-1-2-21-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-1-2-21-
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
./packages/opencode/script/publish.ts
|
||||
env:
|
||||
OPENCODE_SNAPSHOT: true
|
||||
OPENCODE_TAG: ${{ github.ref_name }}
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { tool } from "@opencode-ai/plugin"
|
||||
|
||||
export default tool({
|
||||
description: "call this tool when you want to give up",
|
||||
args: {
|
||||
message: tool.schema.string().describe("give up message"),
|
||||
},
|
||||
async execute(args) {
|
||||
return "Hey fuck you!"
|
||||
},
|
||||
})
|
||||
@@ -84,3 +84,7 @@
|
||||
| 2025-09-17 | 351,117 (+8,508) | 260,970 (+5,706) | 612,087 (+14,214) |
|
||||
| 2025-09-18 | 358,717 (+7,600) | 266,922 (+5,952) | 625,639 (+13,552) |
|
||||
| 2025-09-19 | 365,401 (+6,684) | 271,859 (+4,937) | 637,260 (+11,621) |
|
||||
| 2025-09-20 | 372,092 (+6,691) | 276,917 (+5,058) | 649,009 (+11,749) |
|
||||
| 2025-09-21 | 377,079 (+4,987) | 280,261 (+3,344) | 657,340 (+8,331) |
|
||||
| 2025-09-22 | 382,492 (+5,413) | 284,009 (+3,748) | 666,501 (+9,161) |
|
||||
| 2025-09-23 | 387,008 (+4,516) | 289,129 (+5,120) | 676,137 (+9,636) |
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode/app",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "0.13.11",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@shikijs/transformers": "3.9.2",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
"@solid-primitives/resize-observer": "2.1.3",
|
||||
"@solid-primitives/scroll": "2.1.3",
|
||||
"@solidjs/router": "0.15.3",
|
||||
@@ -61,7 +62,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode/console-core",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@opencode/console-resource": "workspace:*",
|
||||
@@ -78,7 +79,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode/console-function",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -104,7 +105,7 @@
|
||||
},
|
||||
"packages/console/scripts": {
|
||||
"name": "@opencode/console-scripts",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@opencode/console-core": "workspace:*",
|
||||
"tsx": "4.20.5",
|
||||
@@ -116,7 +117,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode/function",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
@@ -131,7 +132,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -182,7 +183,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -194,7 +195,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
},
|
||||
@@ -206,7 +207,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode/web",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -996,6 +997,8 @@
|
||||
|
||||
"@smithy/util-utf8": ["@smithy/util-utf8@4.1.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ=="],
|
||||
|
||||
"@solid-primitives/event-bus": ["@solid-primitives/event-bus@1.1.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-l+n10/51neGcMaP3ypYt21bXfoeWh8IaC8k7fYuY3ww2a8S1Zv2N2a7FF5Qn+waTu86l0V8/nRHjkyqVIZBYwA=="],
|
||||
|
||||
"@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.3", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg=="],
|
||||
|
||||
"@solid-primitives/keyed": ["@solid-primitives/keyed@1.5.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-BgoEdqPw48URnI+L5sZIHdF4ua4Las1eWEBBPaoSFs42kkhnHue+rwCBPL2Z9ebOyQ75sUhUfOETdJfmv0D6Kg=="],
|
||||
|
||||
+1
-3
@@ -75,6 +75,7 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
|
||||
"checkout.session.async_payment_succeeded",
|
||||
"checkout.session.completed",
|
||||
"checkout.session.expired",
|
||||
"charge.refunded",
|
||||
"customer.created",
|
||||
"customer.deleted",
|
||||
"customer.updated",
|
||||
@@ -93,9 +94,6 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
|
||||
"customer.subscription.resumed",
|
||||
"customer.subscription.trial_will_end",
|
||||
"customer.subscription.updated",
|
||||
"customer.tax_id.created",
|
||||
"customer.tax_id.deleted",
|
||||
"customer.tax_id.updated",
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ mkdir -p "$INSTALL_DIR"
|
||||
|
||||
if [ -z "$requested_version" ]; then
|
||||
url="https://github.com/sst/opencode/releases/latest/download/$filename"
|
||||
specific_version=$(curl -s https://api.github.com/repos/sst/opencode/releases/latest | awk -F'"' '/"tag_name": "/ {gsub(/^v/, "", $4); print $4}')
|
||||
specific_version=$(curl -s https://api.github.com/repos/sst/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
|
||||
|
||||
if [[ $? -ne 0 || -z "$specific_version" ]]; then
|
||||
echo -e "${RED}Failed to fetch version information${NC}"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
src/assets/theme.css
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/app",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -24,6 +24,7 @@
|
||||
"@kobalte/core": "0.13.11",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@shikijs/transformers": "3.9.2",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
"@solid-primitives/resize-observer": "2.1.3",
|
||||
"@solid-primitives/scroll": "2.1.3",
|
||||
"@solidjs/router": "0.15.3",
|
||||
|
||||
@@ -37,7 +37,7 @@ class ColorResolver {
|
||||
if (typeof value === "string") {
|
||||
if (value === "none") return { dark: value, light: value }
|
||||
if (value.startsWith("#")) {
|
||||
return { dark: value.toUpperCase(), light: value.toUpperCase() }
|
||||
return { dark: value.toLowerCase(), light: value.toLowerCase() }
|
||||
}
|
||||
const resolved = this.resolveReference(value)
|
||||
return { dark: resolved, light: resolved }
|
||||
@@ -57,7 +57,7 @@ class ColorResolver {
|
||||
if (typeof value === "string") {
|
||||
if (value === "none") return value
|
||||
if (value.startsWith("#")) {
|
||||
return value.toUpperCase()
|
||||
return value.toLowerCase()
|
||||
}
|
||||
return this.resolveReference(value)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class ColorResolver {
|
||||
if (typeof colorValue === "string") {
|
||||
if (colorValue === "none") return colorValue
|
||||
if (colorValue.startsWith("#")) {
|
||||
return colorValue.toUpperCase()
|
||||
return colorValue.toLowerCase()
|
||||
}
|
||||
return this.resolveReference(colorValue)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,584 +1,6 @@
|
||||
import { transformerNotationDiff } from "@shikijs/transformers"
|
||||
import { marked } from "marked"
|
||||
import markedShiki from "marked-shiki"
|
||||
import { codeToHtml } from "shiki"
|
||||
import { useMarked } from "@/context"
|
||||
import { createResource } from "solid-js"
|
||||
|
||||
const markedWithShiki = marked.use(
|
||||
markedShiki({
|
||||
highlight(code, lang) {
|
||||
return codeToHtml(code, {
|
||||
// structure: "inline",
|
||||
lang: lang || "text",
|
||||
tabindex: false,
|
||||
theme: {
|
||||
colors: {
|
||||
"actionBar.toggledBackground": "var(--theme-background-element)",
|
||||
"activityBarBadge.background": "var(--theme-accent)",
|
||||
"checkbox.border": "var(--theme-border)",
|
||||
"editor.background": "transparent",
|
||||
"editor.foreground": "var(--theme-text)",
|
||||
"editor.inactiveSelectionBackground": "var(--theme-background-element)",
|
||||
"editor.selectionHighlightBackground": "var(--theme-border-active)",
|
||||
"editorIndentGuide.activeBackground1": "var(--theme-border-subtle)",
|
||||
"editorIndentGuide.background1": "var(--theme-border-subtle)",
|
||||
"input.placeholderForeground": "var(--theme-text-muted)",
|
||||
"list.activeSelectionIconForeground": "var(--theme-text)",
|
||||
"list.dropBackground": "var(--theme-background-element)",
|
||||
"menu.background": "var(--theme-background-panel)",
|
||||
"menu.border": "var(--theme-border)",
|
||||
"menu.foreground": "var(--theme-text)",
|
||||
"menu.selectionBackground": "var(--theme-primary)",
|
||||
"menu.separatorBackground": "var(--theme-border)",
|
||||
"ports.iconRunningProcessForeground": "var(--theme-success)",
|
||||
"sideBarSectionHeader.background": "transparent",
|
||||
"sideBarSectionHeader.border": "var(--theme-border-subtle)",
|
||||
"sideBarTitle.foreground": "var(--theme-text-muted)",
|
||||
"statusBarItem.remoteBackground": "var(--theme-success)",
|
||||
"statusBarItem.remoteForeground": "var(--theme-text)",
|
||||
"tab.lastPinnedBorder": "var(--theme-border-subtle)",
|
||||
"tab.selectedBackground": "var(--theme-background-element)",
|
||||
"tab.selectedForeground": "var(--theme-text-muted)",
|
||||
"terminal.inactiveSelectionBackground": "var(--theme-background-element)",
|
||||
"widget.border": "var(--theme-border)",
|
||||
},
|
||||
displayName: "opencode",
|
||||
name: "opencode",
|
||||
semanticHighlighting: true,
|
||||
semanticTokenColors: {
|
||||
customLiteral: "var(--theme-syntax-function)",
|
||||
newOperator: "var(--theme-syntax-operator)",
|
||||
numberLiteral: "var(--theme-syntax-number)",
|
||||
stringLiteral: "var(--theme-syntax-string)",
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
"meta.embedded",
|
||||
"source.groovy.embedded",
|
||||
"string meta.image.inline.markdown",
|
||||
"variable.legacy.builtin.python",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-text)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "emphasis",
|
||||
settings: {
|
||||
fontStyle: "italic",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "strong",
|
||||
settings: {
|
||||
fontStyle: "bold",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "header",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-heading)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "comment",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-comment)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.language",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"constant.numeric",
|
||||
"variable.other.enummember",
|
||||
"keyword.operator.plus.exponent",
|
||||
"keyword.operator.minus.exponent",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.regexp",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "entity.name.tag",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["entity.name.tag.css", "entity.name.tag.less"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "entity.other.attribute-name",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"entity.other.attribute-name.class.css",
|
||||
"source.css entity.other.attribute-name.class",
|
||||
"entity.other.attribute-name.id.css",
|
||||
"entity.other.attribute-name.parent-selector.css",
|
||||
"entity.other.attribute-name.parent.less",
|
||||
"source.css entity.other.attribute-name.pseudo-class",
|
||||
"entity.other.attribute-name.pseudo-element.css",
|
||||
"source.css.less entity.other.attribute-name.id",
|
||||
"entity.other.attribute-name.scss",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "invalid",
|
||||
settings: {
|
||||
foreground: "var(--theme-error)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.underline",
|
||||
settings: {
|
||||
fontStyle: "underline",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.bold",
|
||||
settings: {
|
||||
fontStyle: "bold",
|
||||
foreground: "var(--theme-markdown-strong)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.heading",
|
||||
settings: {
|
||||
fontStyle: "bold",
|
||||
foreground: "var(--theme-markdown-heading)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.italic",
|
||||
settings: {
|
||||
fontStyle: "italic",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.strikethrough",
|
||||
settings: {
|
||||
fontStyle: "strikethrough",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.inserted",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-added)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.deleted",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-removed)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.changed",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-context)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "punctuation.definition.quote.begin.markdown",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-block-quote)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "punctuation.definition.list.begin.markdown",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-list-enumeration)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.inline.raw",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-code)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "punctuation.definition.tag",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-punctuation)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["meta.preprocessor", "entity.name.function.preprocessor"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.preprocessor.string",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.preprocessor.numeric",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.structure.dictionary.key.python",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.diff.header",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-hunk-header)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "storage",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "storage.type",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["storage.modifier", "keyword.operator.noexcept"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["string", "meta.embedded.assembly"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "string.tag",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "string.value",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "string.regexp",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"punctuation.definition.template-expression.begin",
|
||||
"punctuation.definition.template-expression.end",
|
||||
"punctuation.section.embedded",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["meta.template.expression"],
|
||||
settings: {
|
||||
foreground: "var(--theme-text)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"support.type.vendored.property-name",
|
||||
"support.type.property-name",
|
||||
"source.css variable",
|
||||
"source.coffee.embedded",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.control",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.operator",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"keyword.operator.new",
|
||||
"keyword.operator.expression",
|
||||
"keyword.operator.cast",
|
||||
"keyword.operator.sizeof",
|
||||
"keyword.operator.alignof",
|
||||
"keyword.operator.typeid",
|
||||
"keyword.operator.alignas",
|
||||
"keyword.operator.instanceof",
|
||||
"keyword.operator.logical.python",
|
||||
"keyword.operator.wordlike",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.other.unit",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "support.function.git-rebase",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.sha.git-rebase",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"storage.modifier.import.java",
|
||||
"variable.language.wildcard.java",
|
||||
"storage.modifier.package.java",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-text)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "variable.language",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"entity.name.function",
|
||||
"support.function",
|
||||
"support.constant.handlebars",
|
||||
"source.powershell variable.other.member",
|
||||
"entity.name.operator.custom-literal",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-function)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"support.class",
|
||||
"support.type",
|
||||
"entity.name.type",
|
||||
"entity.name.namespace",
|
||||
"entity.other.attribute",
|
||||
"entity.name.scope-resolution",
|
||||
"entity.name.class",
|
||||
"storage.type.numeric.go",
|
||||
"storage.type.byte.go",
|
||||
"storage.type.boolean.go",
|
||||
"storage.type.string.go",
|
||||
"storage.type.uintptr.go",
|
||||
"storage.type.error.go",
|
||||
"storage.type.rune.go",
|
||||
"storage.type.cs",
|
||||
"storage.type.generic.cs",
|
||||
"storage.type.modifier.cs",
|
||||
"storage.type.variable.cs",
|
||||
"storage.type.annotation.java",
|
||||
"storage.type.generic.java",
|
||||
"storage.type.java",
|
||||
"storage.type.object.array.java",
|
||||
"storage.type.primitive.array.java",
|
||||
"storage.type.primitive.java",
|
||||
"storage.type.token.java",
|
||||
"storage.type.groovy",
|
||||
"storage.type.annotation.groovy",
|
||||
"storage.type.parameters.groovy",
|
||||
"storage.type.generic.groovy",
|
||||
"storage.type.object.array.groovy",
|
||||
"storage.type.primitive.array.groovy",
|
||||
"storage.type.primitive.groovy",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-type)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"meta.type.cast.expr",
|
||||
"meta.type.new.expr",
|
||||
"support.constant.math",
|
||||
"support.constant.dom",
|
||||
"support.constant.json",
|
||||
"entity.other.inherited-class",
|
||||
"punctuation.separator.namespace.ruby",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-type)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"keyword.control",
|
||||
"source.cpp keyword.operator.new",
|
||||
"keyword.operator.delete",
|
||||
"keyword.other.using",
|
||||
"keyword.other.directive.using",
|
||||
"keyword.other.operator",
|
||||
"entity.name.operator",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"variable",
|
||||
"meta.definition.variable.name",
|
||||
"support.variable",
|
||||
"entity.name.variable",
|
||||
"constant.other.placeholder",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["variable.other.constant", "variable.other.enummember"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["meta.object-literal.key"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"support.constant.property-value",
|
||||
"support.constant.font-name",
|
||||
"support.constant.media-type",
|
||||
"support.constant.media",
|
||||
"constant.other.color.rgb-value",
|
||||
"constant.other.rgb-value",
|
||||
"support.constant.color",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"punctuation.definition.group.regexp",
|
||||
"punctuation.definition.group.assertion.regexp",
|
||||
"punctuation.definition.character-class.regexp",
|
||||
"punctuation.character.set.begin.regexp",
|
||||
"punctuation.character.set.end.regexp",
|
||||
"keyword.operator.negation.regexp",
|
||||
"support.other.parenthesis.regexp",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"constant.character.character-class.regexp",
|
||||
"constant.other.character-class.set.regexp",
|
||||
"constant.other.character-class.regexp",
|
||||
"constant.character.set.regexp",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.operator.quantifier.regexp",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["constant.character", "constant.other.option"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.character.escape",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "entity.name.label",
|
||||
settings: {
|
||||
foreground: "var(--theme-text-muted)",
|
||||
},
|
||||
},
|
||||
],
|
||||
type: "dark",
|
||||
},
|
||||
transformers: [transformerNotationDiff()],
|
||||
})
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
function strip(text: string): string {
|
||||
const wrappedRe = /^\s*<([A-Za-z]\w*)>\s*([\s\S]*?)\s*<\/\1>\s*$/
|
||||
const match = text.match(wrappedRe)
|
||||
@@ -586,10 +8,11 @@ function strip(text: string): string {
|
||||
}
|
||||
|
||||
export default function Markdown(props: { text: string; class?: string }) {
|
||||
const marked = useMarked()
|
||||
const [html] = createResource(
|
||||
() => strip(props.text),
|
||||
async (markdown) => {
|
||||
return markedWithShiki.parse(markdown)
|
||||
return marked.parse(markdown)
|
||||
},
|
||||
)
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createContext, useContext, type ParentProps } from "solid-js"
|
||||
import { createEventBus } from "@solid-primitives/event-bus"
|
||||
import type { Event as SDKEvent } from "@opencode-ai/sdk"
|
||||
import { useSDK } from "@/context"
|
||||
|
||||
export type Event = SDKEvent // can extend with custom events later
|
||||
|
||||
function init() {
|
||||
const sdk = useSDK()
|
||||
const bus = createEventBus<Event>()
|
||||
sdk.event.subscribe().then(async (events) => {
|
||||
for await (const event of events.stream) {
|
||||
bus.emit(event)
|
||||
}
|
||||
})
|
||||
return bus
|
||||
}
|
||||
|
||||
type EventContext = ReturnType<typeof init>
|
||||
|
||||
const ctx = createContext<EventContext>()
|
||||
|
||||
export function EventProvider(props: ParentProps) {
|
||||
const value = init()
|
||||
return <ctx.Provider value={value}>{props.children}</ctx.Provider>
|
||||
}
|
||||
|
||||
export function useEvent() {
|
||||
const value = useContext(ctx)
|
||||
if (!value) {
|
||||
throw new Error("useEvent must be used within a EventProvider")
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
export { EventProvider, useEvent } from "./event"
|
||||
export { LocalProvider, useLocal } from "./local"
|
||||
export { MarkedProvider, useMarked } from "./marked"
|
||||
export { SDKProvider, useSDK } from "./sdk"
|
||||
export { ShikiProvider, useShiki } from "./shiki"
|
||||
export { SyncProvider, useSync } from "./sync"
|
||||
export { ThemeProvider, useTheme } from "./theme"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { createStore, produce, reconcile } from "solid-js/store"
|
||||
import { batch, createContext, createEffect, createMemo, useContext, type ParentProps } from "solid-js"
|
||||
import { useSync } from "./sync"
|
||||
import { uniqueBy } from "remeda"
|
||||
import type { FileContent, FileNode } from "@opencode-ai/sdk"
|
||||
import { useSDK } from "./sdk"
|
||||
import { useSDK, useEvent, useSync } from "@/context"
|
||||
|
||||
export type LocalFile = FileNode &
|
||||
Partial<{
|
||||
@@ -165,17 +164,19 @@ function init() {
|
||||
})
|
||||
}
|
||||
|
||||
const load = async (path: string) =>
|
||||
sdk.file.read({ query: { path } }).then((x) => {
|
||||
const load = async (path: string) => {
|
||||
const relative = path.replace(sync.data.path.directory + "/", "")
|
||||
sdk.file.read({ query: { path: relative } }).then((x) => {
|
||||
setStore(
|
||||
"node",
|
||||
path,
|
||||
relative,
|
||||
produce((draft) => {
|
||||
draft.loaded = true
|
||||
draft.content = x.data
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const open = async (path: string) => {
|
||||
const relative = path.replace(sync.data.path.directory + "/", "")
|
||||
@@ -213,27 +214,27 @@ function init() {
|
||||
})
|
||||
}
|
||||
|
||||
sdk.event.subscribe().then(async (events) => {
|
||||
for await (const event of events.stream) {
|
||||
switch (event.type) {
|
||||
case "message.part.updated":
|
||||
const part = event.properties.part
|
||||
if (part.type === "tool" && part.state.status === "completed") {
|
||||
switch (part.tool) {
|
||||
case "read":
|
||||
console.log("read", part.state.input)
|
||||
break
|
||||
case "edit":
|
||||
const absolute = part.state.input["filePath"] as string
|
||||
const path = absolute.replace(sync.data.path.directory + "/", "")
|
||||
load(path)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
const bus = useEvent()
|
||||
bus.listen((event) => {
|
||||
switch (event.type) {
|
||||
case "message.part.updated":
|
||||
const part = event.properties.part
|
||||
if (part.type === "tool" && part.state.status === "completed") {
|
||||
switch (part.tool) {
|
||||
case "read":
|
||||
console.log("read", part.state.input)
|
||||
break
|
||||
case "edit":
|
||||
load(part.state.input["filePath"] as string)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
case "file.watcher.updated":
|
||||
load(event.properties.file)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createContext, useContext, type ParentProps } from "solid-js"
|
||||
import { useShiki } from "@/context"
|
||||
import { marked } from "marked"
|
||||
import markedShiki from "marked-shiki"
|
||||
import { bundledLanguages, type BundledLanguage } from "shiki"
|
||||
|
||||
function init(highlighter: ReturnType<typeof useShiki>) {
|
||||
return marked.use(
|
||||
markedShiki({
|
||||
async highlight(code, lang) {
|
||||
if (!(lang in bundledLanguages)) {
|
||||
lang = "text"
|
||||
}
|
||||
if (!highlighter.getLoadedLanguages().includes(lang)) {
|
||||
await highlighter.loadLanguage(lang as BundledLanguage)
|
||||
}
|
||||
return highlighter.codeToHtml(code, {
|
||||
lang: lang || "text",
|
||||
theme: "opencode",
|
||||
tabindex: false,
|
||||
})
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
type MarkedContext = ReturnType<typeof init>
|
||||
|
||||
const ctx = createContext<MarkedContext>()
|
||||
|
||||
export function MarkedProvider(props: ParentProps) {
|
||||
const highlighter = useShiki()
|
||||
const value = init(highlighter)
|
||||
return <ctx.Provider value={value}>{props.children}</ctx.Provider>
|
||||
}
|
||||
|
||||
export function useMarked() {
|
||||
const value = useContext(ctx)
|
||||
if (!value) {
|
||||
throw new Error("useMarked must be used within a MarkedProvider")
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
import { createHighlighter, type ThemeInput } from "shiki"
|
||||
import { createContext, useContext, type ParentProps } from "solid-js"
|
||||
|
||||
const theme: ThemeInput = {
|
||||
colors: {
|
||||
"actionBar.toggledBackground": "var(--theme-background-element)",
|
||||
"activityBarBadge.background": "var(--theme-accent)",
|
||||
"checkbox.border": "var(--theme-border)",
|
||||
"editor.background": "transparent",
|
||||
"editor.foreground": "var(--theme-text)",
|
||||
"editor.inactiveSelectionBackground": "var(--theme-background-element)",
|
||||
"editor.selectionHighlightBackground": "var(--theme-border-active)",
|
||||
"editorIndentGuide.activeBackground1": "var(--theme-border-subtle)",
|
||||
"editorIndentGuide.background1": "var(--theme-border-subtle)",
|
||||
"input.placeholderForeground": "var(--theme-text-muted)",
|
||||
"list.activeSelectionIconForeground": "var(--theme-text)",
|
||||
"list.dropBackground": "var(--theme-background-element)",
|
||||
"menu.background": "var(--theme-background-panel)",
|
||||
"menu.border": "var(--theme-border)",
|
||||
"menu.foreground": "var(--theme-text)",
|
||||
"menu.selectionBackground": "var(--theme-primary)",
|
||||
"menu.separatorBackground": "var(--theme-border)",
|
||||
"ports.iconRunningProcessForeground": "var(--theme-success)",
|
||||
"sideBarSectionHeader.background": "transparent",
|
||||
"sideBarSectionHeader.border": "var(--theme-border-subtle)",
|
||||
"sideBarTitle.foreground": "var(--theme-text-muted)",
|
||||
"statusBarItem.remoteBackground": "var(--theme-success)",
|
||||
"statusBarItem.remoteForeground": "var(--theme-text)",
|
||||
"tab.lastPinnedBorder": "var(--theme-border-subtle)",
|
||||
"tab.selectedBackground": "var(--theme-background-element)",
|
||||
"tab.selectedForeground": "var(--theme-text-muted)",
|
||||
"terminal.inactiveSelectionBackground": "var(--theme-background-element)",
|
||||
"widget.border": "var(--theme-border)",
|
||||
},
|
||||
displayName: "opencode",
|
||||
name: "opencode",
|
||||
semanticHighlighting: true,
|
||||
semanticTokenColors: {
|
||||
customLiteral: "var(--theme-syntax-function)",
|
||||
newOperator: "var(--theme-syntax-operator)",
|
||||
numberLiteral: "var(--theme-syntax-number)",
|
||||
stringLiteral: "var(--theme-syntax-string)",
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
"meta.embedded",
|
||||
"source.groovy.embedded",
|
||||
"string meta.image.inline.markdown",
|
||||
"variable.legacy.builtin.python",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-text)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "emphasis",
|
||||
settings: {
|
||||
fontStyle: "italic",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "strong",
|
||||
settings: {
|
||||
fontStyle: "bold",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "header",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-heading)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "comment",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-comment)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.language",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"constant.numeric",
|
||||
"variable.other.enummember",
|
||||
"keyword.operator.plus.exponent",
|
||||
"keyword.operator.minus.exponent",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.regexp",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "entity.name.tag",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["entity.name.tag.css", "entity.name.tag.less"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "entity.other.attribute-name",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"entity.other.attribute-name.class.css",
|
||||
"source.css entity.other.attribute-name.class",
|
||||
"entity.other.attribute-name.id.css",
|
||||
"entity.other.attribute-name.parent-selector.css",
|
||||
"entity.other.attribute-name.parent.less",
|
||||
"source.css entity.other.attribute-name.pseudo-class",
|
||||
"entity.other.attribute-name.pseudo-element.css",
|
||||
"source.css.less entity.other.attribute-name.id",
|
||||
"entity.other.attribute-name.scss",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "invalid",
|
||||
settings: {
|
||||
foreground: "var(--theme-error)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.underline",
|
||||
settings: {
|
||||
fontStyle: "underline",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.bold",
|
||||
settings: {
|
||||
fontStyle: "bold",
|
||||
foreground: "var(--theme-markdown-strong)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.heading",
|
||||
settings: {
|
||||
fontStyle: "bold",
|
||||
foreground: "var(--theme-markdown-heading)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.italic",
|
||||
settings: {
|
||||
fontStyle: "italic",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.strikethrough",
|
||||
settings: {
|
||||
fontStyle: "strikethrough",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.inserted",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-added)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.deleted",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-removed)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.changed",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-context)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "punctuation.definition.quote.begin.markdown",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-block-quote)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "punctuation.definition.list.begin.markdown",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-list-enumeration)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "markup.inline.raw",
|
||||
settings: {
|
||||
foreground: "var(--theme-markdown-code)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "punctuation.definition.tag",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-punctuation)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["meta.preprocessor", "entity.name.function.preprocessor"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.preprocessor.string",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.preprocessor.numeric",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.structure.dictionary.key.python",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "meta.diff.header",
|
||||
settings: {
|
||||
foreground: "var(--theme-diff-hunk-header)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "storage",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "storage.type",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["storage.modifier", "keyword.operator.noexcept"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["string", "meta.embedded.assembly"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "string.tag",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "string.value",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "string.regexp",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"punctuation.definition.template-expression.begin",
|
||||
"punctuation.definition.template-expression.end",
|
||||
"punctuation.section.embedded",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["meta.template.expression"],
|
||||
settings: {
|
||||
foreground: "var(--theme-text)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"support.type.vendored.property-name",
|
||||
"support.type.property-name",
|
||||
"source.css variable",
|
||||
"source.coffee.embedded",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.control",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.operator",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"keyword.operator.new",
|
||||
"keyword.operator.expression",
|
||||
"keyword.operator.cast",
|
||||
"keyword.operator.sizeof",
|
||||
"keyword.operator.alignof",
|
||||
"keyword.operator.typeid",
|
||||
"keyword.operator.alignas",
|
||||
"keyword.operator.instanceof",
|
||||
"keyword.operator.logical.python",
|
||||
"keyword.operator.wordlike",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.other.unit",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "support.function.git-rebase",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.sha.git-rebase",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-number)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"],
|
||||
settings: {
|
||||
foreground: "var(--theme-text)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "variable.language",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"entity.name.function",
|
||||
"support.function",
|
||||
"support.constant.handlebars",
|
||||
"source.powershell variable.other.member",
|
||||
"entity.name.operator.custom-literal",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-function)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"support.class",
|
||||
"support.type",
|
||||
"entity.name.type",
|
||||
"entity.name.namespace",
|
||||
"entity.other.attribute",
|
||||
"entity.name.scope-resolution",
|
||||
"entity.name.class",
|
||||
"storage.type.numeric.go",
|
||||
"storage.type.byte.go",
|
||||
"storage.type.boolean.go",
|
||||
"storage.type.string.go",
|
||||
"storage.type.uintptr.go",
|
||||
"storage.type.error.go",
|
||||
"storage.type.rune.go",
|
||||
"storage.type.cs",
|
||||
"storage.type.generic.cs",
|
||||
"storage.type.modifier.cs",
|
||||
"storage.type.variable.cs",
|
||||
"storage.type.annotation.java",
|
||||
"storage.type.generic.java",
|
||||
"storage.type.java",
|
||||
"storage.type.object.array.java",
|
||||
"storage.type.primitive.array.java",
|
||||
"storage.type.primitive.java",
|
||||
"storage.type.token.java",
|
||||
"storage.type.groovy",
|
||||
"storage.type.annotation.groovy",
|
||||
"storage.type.parameters.groovy",
|
||||
"storage.type.generic.groovy",
|
||||
"storage.type.object.array.groovy",
|
||||
"storage.type.primitive.array.groovy",
|
||||
"storage.type.primitive.groovy",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-type)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"meta.type.cast.expr",
|
||||
"meta.type.new.expr",
|
||||
"support.constant.math",
|
||||
"support.constant.dom",
|
||||
"support.constant.json",
|
||||
"entity.other.inherited-class",
|
||||
"punctuation.separator.namespace.ruby",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-type)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"keyword.control",
|
||||
"source.cpp keyword.operator.new",
|
||||
"keyword.operator.delete",
|
||||
"keyword.other.using",
|
||||
"keyword.other.directive.using",
|
||||
"keyword.other.operator",
|
||||
"entity.name.operator",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"variable",
|
||||
"meta.definition.variable.name",
|
||||
"support.variable",
|
||||
"entity.name.variable",
|
||||
"constant.other.placeholder",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["variable.other.constant", "variable.other.enummember"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["meta.object-literal.key"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-variable)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"support.constant.property-value",
|
||||
"support.constant.font-name",
|
||||
"support.constant.media-type",
|
||||
"support.constant.media",
|
||||
"constant.other.color.rgb-value",
|
||||
"constant.other.rgb-value",
|
||||
"support.constant.color",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"punctuation.definition.group.regexp",
|
||||
"punctuation.definition.group.assertion.regexp",
|
||||
"punctuation.definition.character-class.regexp",
|
||||
"punctuation.character.set.begin.regexp",
|
||||
"punctuation.character.set.end.regexp",
|
||||
"keyword.operator.negation.regexp",
|
||||
"support.other.parenthesis.regexp",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-string)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"constant.character.character-class.regexp",
|
||||
"constant.other.character-class.set.regexp",
|
||||
"constant.other.character-class.regexp",
|
||||
"constant.character.set.regexp",
|
||||
],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "keyword.operator.quantifier.regexp",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ["constant.character", "constant.other.option"],
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-keyword)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "constant.character.escape",
|
||||
settings: {
|
||||
foreground: "var(--theme-syntax-operator)",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "entity.name.label",
|
||||
settings: {
|
||||
foreground: "var(--theme-text-muted)",
|
||||
},
|
||||
},
|
||||
],
|
||||
type: "dark",
|
||||
}
|
||||
|
||||
const highlighter = await createHighlighter({
|
||||
themes: [theme],
|
||||
langs: [],
|
||||
})
|
||||
|
||||
type ShikiContext = typeof highlighter
|
||||
|
||||
const ctx = createContext<ShikiContext>()
|
||||
|
||||
export function ShikiProvider(props: ParentProps) {
|
||||
return <ctx.Provider value={highlighter}>{props.children}</ctx.Provider>
|
||||
}
|
||||
|
||||
export function useShiki() {
|
||||
const value = useContext(ctx)
|
||||
if (!value) {
|
||||
throw new Error("useShiki must be used within a ShikiProvider")
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Message, Agent, Provider, Session, Part, Config, Path, File, FileNode } from "@opencode-ai/sdk"
|
||||
import { createStore, produce, reconcile } from "solid-js/store"
|
||||
import { useSDK } from "./sdk"
|
||||
import { createContext, Show, useContext, type ParentProps } from "solid-js"
|
||||
import { useSDK, useEvent } from "@/context"
|
||||
import { Binary } from "@/utils/binary"
|
||||
|
||||
function init() {
|
||||
@@ -33,69 +33,67 @@ function init() {
|
||||
changes: [],
|
||||
})
|
||||
|
||||
const sdk = useSDK()
|
||||
|
||||
sdk.event.subscribe().then(async (events) => {
|
||||
for await (const event of events.stream) {
|
||||
switch (event.type) {
|
||||
case "session.updated": {
|
||||
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
|
||||
if (result.found) {
|
||||
setStore("session", result.index, reconcile(event.properties.info))
|
||||
break
|
||||
}
|
||||
setStore(
|
||||
"session",
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, event.properties.info)
|
||||
}),
|
||||
)
|
||||
const bus = useEvent()
|
||||
bus.listen((event) => {
|
||||
switch (event.type) {
|
||||
case "session.updated": {
|
||||
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
|
||||
if (result.found) {
|
||||
setStore("session", result.index, reconcile(event.properties.info))
|
||||
break
|
||||
}
|
||||
case "message.updated": {
|
||||
const messages = store.message[event.properties.info.sessionID]
|
||||
if (!messages) {
|
||||
setStore("message", event.properties.info.sessionID, [event.properties.info])
|
||||
break
|
||||
}
|
||||
const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
|
||||
if (result.found) {
|
||||
setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
|
||||
break
|
||||
}
|
||||
setStore(
|
||||
"message",
|
||||
event.properties.info.sessionID,
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, event.properties.info)
|
||||
}),
|
||||
)
|
||||
setStore(
|
||||
"session",
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, event.properties.info)
|
||||
}),
|
||||
)
|
||||
break
|
||||
}
|
||||
case "message.updated": {
|
||||
const messages = store.message[event.properties.info.sessionID]
|
||||
if (!messages) {
|
||||
setStore("message", event.properties.info.sessionID, [event.properties.info])
|
||||
break
|
||||
}
|
||||
case "message.part.updated": {
|
||||
const parts = store.part[event.properties.part.messageID]
|
||||
if (!parts) {
|
||||
setStore("part", event.properties.part.messageID, [event.properties.part])
|
||||
break
|
||||
}
|
||||
const result = Binary.search(parts, event.properties.part.id, (p) => p.id)
|
||||
if (result.found) {
|
||||
setStore("part", event.properties.part.messageID, result.index, reconcile(event.properties.part))
|
||||
break
|
||||
}
|
||||
setStore(
|
||||
"part",
|
||||
event.properties.part.messageID,
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, event.properties.part)
|
||||
}),
|
||||
)
|
||||
const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
|
||||
if (result.found) {
|
||||
setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
|
||||
break
|
||||
}
|
||||
setStore(
|
||||
"message",
|
||||
event.properties.info.sessionID,
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, event.properties.info)
|
||||
}),
|
||||
)
|
||||
break
|
||||
}
|
||||
case "message.part.updated": {
|
||||
const parts = store.part[event.properties.part.messageID]
|
||||
if (!parts) {
|
||||
setStore("part", event.properties.part.messageID, [event.properties.part])
|
||||
break
|
||||
}
|
||||
const result = Binary.search(parts, event.properties.part.id, (p) => p.id)
|
||||
if (result.found) {
|
||||
setStore("part", event.properties.part.messageID, result.index, reconcile(event.properties.part))
|
||||
break
|
||||
}
|
||||
setStore(
|
||||
"part",
|
||||
event.properties.part.messageID,
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, event.properties.part)
|
||||
}),
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const sdk = useSDK()
|
||||
Promise.all([
|
||||
sdk.config.providers().then((x) => setStore("provider", x.data!.providers)),
|
||||
sdk.path.get().then((x) => setStore("path", x.data!)),
|
||||
|
||||
+24
-10
@@ -4,7 +4,15 @@ import { Router, Route } from "@solidjs/router"
|
||||
import "@/index.css"
|
||||
import Layout from "@/pages/layout"
|
||||
import Home from "@/pages"
|
||||
import { SDKProvider, SyncProvider, LocalProvider, ThemeProvider } from "@/context"
|
||||
import {
|
||||
EventProvider,
|
||||
SDKProvider,
|
||||
SyncProvider,
|
||||
LocalProvider,
|
||||
ThemeProvider,
|
||||
ShikiProvider,
|
||||
MarkedProvider,
|
||||
} from "@/context"
|
||||
|
||||
const root = document.getElementById("root")
|
||||
|
||||
@@ -18,15 +26,21 @@ render(
|
||||
() => (
|
||||
<div class="h-full bg-background text-text-muted">
|
||||
<ThemeProvider defaultTheme="opencode" defaultDarkMode={true}>
|
||||
<SDKProvider>
|
||||
<SyncProvider>
|
||||
<LocalProvider>
|
||||
<Router root={Layout}>
|
||||
<Route path="/" component={Home} />
|
||||
</Router>
|
||||
</LocalProvider>
|
||||
</SyncProvider>
|
||||
</SDKProvider>
|
||||
<ShikiProvider>
|
||||
<MarkedProvider>
|
||||
<SDKProvider>
|
||||
<EventProvider>
|
||||
<SyncProvider>
|
||||
<LocalProvider>
|
||||
<Router root={Layout}>
|
||||
<Route path="/" component={Home} />
|
||||
</Router>
|
||||
</LocalProvider>
|
||||
</SyncProvider>
|
||||
</EventProvider>
|
||||
</SDKProvider>
|
||||
</MarkedProvider>
|
||||
</ShikiProvider>
|
||||
</ThemeProvider>
|
||||
</div>
|
||||
),
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
||||
"build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||
"start": "vinxi start",
|
||||
"version": "0.10.3"
|
||||
"version": "0.11.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
|
||||
&[data-slot="payment-amount"] {
|
||||
color: var(--color-text);
|
||||
|
||||
&[data-refunded="true"] {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,9 @@ export function PaymentSection() {
|
||||
{formatDateForTable(date)}
|
||||
</td>
|
||||
<td data-slot="payment-id">{payment.id}</td>
|
||||
<td data-slot="payment-amount">${((payment.amount ?? 0) / 100000000).toFixed(2)}</td>
|
||||
<td data-slot="payment-amount" data-refunded={!!payment.timeRefunded}>
|
||||
${((payment.amount ?? 0) / 100000000).toFixed(2)}
|
||||
</td>
|
||||
<td data-slot="payment-receipt">
|
||||
<button
|
||||
onClick={async () => {
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
.root {
|
||||
[data-slot="section-content"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
[data-slot="reload-error"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
|
||||
p {
|
||||
color: var(--color-danger);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[data-slot="create-form"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
[data-slot="payment"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
min-width: 14.5rem;
|
||||
width: fit-content;
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-slot="credit-card"] {
|
||||
padding: var(--space-3-5) var(--space-4);
|
||||
background-color: var(--color-bg-surface);
|
||||
border-radius: var(--border-radius-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
[data-slot="card-icon"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
[data-slot="card-details"] {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-1);
|
||||
|
||||
[data-slot="secret"] {
|
||||
position: relative;
|
||||
bottom: 2px;
|
||||
font-size: var(--font-size-lg);
|
||||
color: var(--color-text-muted);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
[data-slot="number"] {
|
||||
font-size: var(--font-size-3xl);
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="button-row"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
align-items: center;
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
flex-direction: column;
|
||||
|
||||
> button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="create-form"] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Make Enable Billing button full width when it's the only button */
|
||||
> button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
[data-slot="usage"] {
|
||||
p {
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.5;
|
||||
color: var(--color-text-secondary);
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import { json, query, action, useParams, createAsync, useSubmission } from "@solidjs/router"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import styles from "./billing-section.module.css"
|
||||
import { Database, eq } from "@opencode/console-core/drizzle/index.js"
|
||||
import { WorkspaceTable } from "@opencode/console-core/schema/workspace.sql.js"
|
||||
import { Show } from "solid-js"
|
||||
|
||||
const updateShare = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
const dataShare = form.get("dataShare")?.toString() === "true" ? true : null
|
||||
return json(
|
||||
await withActor(() => {
|
||||
return Database.use((tx) =>
|
||||
tx
|
||||
.update(WorkspaceTable)
|
||||
.set({
|
||||
dataShare,
|
||||
})
|
||||
.where(eq(WorkspaceTable.id, workspaceID)),
|
||||
)
|
||||
}, workspaceID),
|
||||
{ revalidate: getWorkspaceInfo.key },
|
||||
)
|
||||
}, "workspace.disableShare")
|
||||
|
||||
const getWorkspaceInfo = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
return withActor(() => {
|
||||
return Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
dataShare: WorkspaceTable.dataShare,
|
||||
})
|
||||
.from(WorkspaceTable)
|
||||
.where(eq(WorkspaceTable.id, workspaceID))
|
||||
.then((r) => r[0]),
|
||||
)
|
||||
}, workspaceID)
|
||||
}, "workspace.get")
|
||||
|
||||
export function PrivacySection() {
|
||||
const params = useParams()
|
||||
const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id))
|
||||
const updateShareSubmission = useSubmission(updateShare)
|
||||
|
||||
return (
|
||||
<section class={styles.root}>
|
||||
<div data-slot="section-title">
|
||||
<h2>Privacy controls</h2>
|
||||
<p>
|
||||
Some providers offer data-sharing programs. If you opt in, you voluntarily <b>share your usage data</b> with
|
||||
them, which they may use to improve their services, including <b>model training</b>.
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
By opting in, you gain access to <b>discounted pricing</b> from the provider. You can opt in or out at any
|
||||
time.
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
<a target="_blank" href="/docs/zen">
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<Show when={workspaceInfo()?.dataShare}>
|
||||
<div data-slot="payment">
|
||||
<div data-slot="credit-card">
|
||||
<div data-slot="card-details">
|
||||
<span data-slot="number">You are currently opted in to the data-sharing program.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<div data-slot="section-content">
|
||||
<div data-slot="payment">
|
||||
<div data-slot="button-row">
|
||||
<form action={updateShare} method="post" data-slot="create-form">
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<input type="hidden" name="dataShare" value={workspaceInfo()?.dataShare ? "false" : "true"} />
|
||||
<button data-color="ghost" type="submit" disabled={updateShareSubmission.pending}>
|
||||
{workspaceInfo()?.dataShare
|
||||
? updateShareSubmission.pending
|
||||
? "Opting out..."
|
||||
: "Opt out"
|
||||
: updateShareSubmission.pending
|
||||
? "Opting in..."
|
||||
: "Opt in"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -175,7 +175,7 @@ export default function Home() {
|
||||
|
||||
<div data-component="legal">
|
||||
<span>
|
||||
©2025 <a href="https://anoma.ly">Anomaly Innovations</a>
|
||||
©2025 <a href="https://anoma.ly">Anomaly</a>
|
||||
</span>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Billing } from "@opencode/console-core/billing.js"
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { Database, eq, sql } from "@opencode/console-core/drizzle/index.js"
|
||||
import { and, Database, eq, sql } from "@opencode/console-core/drizzle/index.js"
|
||||
import { BillingTable, PaymentTable } from "@opencode/console-core/schema/billing.sql.js"
|
||||
import { Identifier } from "@opencode/console-core/identifier.js"
|
||||
import { centsToMicroCents } from "@opencode/console-core/util/price.js"
|
||||
@@ -91,6 +91,39 @@ export async function POST(input: APIEvent) {
|
||||
})
|
||||
})
|
||||
}
|
||||
if (body.type === "charge.refunded") {
|
||||
const customerID = body.data.object.customer as string
|
||||
const paymentIntentID = body.data.object.payment_intent as string
|
||||
if (!customerID) throw new Error("Customer ID not found")
|
||||
if (!paymentIntentID) throw new Error("Payment ID not found")
|
||||
|
||||
const workspaceID = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
workspaceID: BillingTable.workspaceID,
|
||||
})
|
||||
.from(BillingTable)
|
||||
.where(eq(BillingTable.customerID, customerID))
|
||||
.then((rows) => rows[0]?.workspaceID),
|
||||
)
|
||||
if (!workspaceID) throw new Error("Workspace ID not found")
|
||||
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(PaymentTable)
|
||||
.set({
|
||||
timeRefunded: new Date(body.created * 1000),
|
||||
})
|
||||
.where(and(eq(PaymentTable.paymentID, paymentIntentID), eq(PaymentTable.workspaceID, workspaceID)))
|
||||
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} - ${centsToMicroCents(Billing.CHARGE_AMOUNT)}`,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||
})
|
||||
}
|
||||
|
||||
console.log("finished handling")
|
||||
|
||||
|
||||
@@ -2,15 +2,11 @@ import "./[id].css"
|
||||
import { MonthlyLimitSection } from "~/component/workspace/monthly-limit-section"
|
||||
import { NewUserSection } from "~/component/workspace/new-user-section"
|
||||
import { BillingSection } from "~/component/workspace/billing-section"
|
||||
import { PrivacySection } from "~/component/workspace/privacy-section"
|
||||
import { PaymentSection } from "~/component/workspace/payment-section"
|
||||
import { UsageSection } from "~/component/workspace/usage-section"
|
||||
import { KeySection } from "~/component/workspace/key-section"
|
||||
import { Show } from "solid-js"
|
||||
import { useParams } from "@solidjs/router"
|
||||
|
||||
export default function () {
|
||||
const params = useParams()
|
||||
return (
|
||||
<div data-page="workspace-[id]">
|
||||
<section data-component="title-section">
|
||||
@@ -29,9 +25,6 @@ export default function () {
|
||||
<KeySection />
|
||||
<BillingSection />
|
||||
<MonthlyLimitSection />
|
||||
<Show when={isBeta(params.id)}>
|
||||
<PrivacySection />
|
||||
</Show>
|
||||
<UsageSection />
|
||||
<PaymentSection />
|
||||
</div>
|
||||
|
||||
@@ -45,6 +45,7 @@ export async function handler(
|
||||
const ModelSchema = z.object({
|
||||
cost: ModelCostSchema,
|
||||
cost200K: ModelCostSchema.optional(),
|
||||
allowAnonymous: z.boolean().optional(),
|
||||
providers: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
@@ -52,7 +53,6 @@ export async function handler(
|
||||
apiKey: z.string(),
|
||||
model: z.string(),
|
||||
weight: z.number().optional(),
|
||||
allowAnonymous: z.boolean().optional(),
|
||||
headerMappings: z.record(z.string(), z.string()).optional(),
|
||||
disabled: z.boolean().optional(),
|
||||
}),
|
||||
@@ -85,9 +85,10 @@ export async function handler(
|
||||
session: input.request.headers.get("x-opencode-session"),
|
||||
request: input.request.headers.get("x-opencode-request"),
|
||||
})
|
||||
const authInfo = await authenticate()
|
||||
const modelInfo = validateModel(body.model, authInfo)
|
||||
const providerInfo = selectProvider(modelInfo, authInfo)
|
||||
const modelInfo = validateModel(body.model)
|
||||
const providerInfo = selectProvider(modelInfo)
|
||||
const authInfo = await authenticate(modelInfo)
|
||||
validateBilling(modelInfo, authInfo)
|
||||
logger.metric({ provider: providerInfo.id })
|
||||
|
||||
// Request to model provider
|
||||
@@ -220,16 +221,41 @@ export async function handler(
|
||||
)
|
||||
}
|
||||
|
||||
async function authenticate() {
|
||||
function validateModel(reqModel: string) {
|
||||
const json = JSON.parse(Resource.ZEN_MODELS.value)
|
||||
|
||||
const allModels = z.record(z.string(), ModelSchema).parse(json)
|
||||
|
||||
if (!(reqModel in allModels)) {
|
||||
throw new ModelError(`Model ${reqModel} not supported`)
|
||||
}
|
||||
const modelId = reqModel as keyof typeof allModels
|
||||
const modelData = allModels[modelId]
|
||||
|
||||
logger.metric({ model: modelId })
|
||||
|
||||
return { id: modelId, ...modelData }
|
||||
}
|
||||
|
||||
function selectProvider(model: Model) {
|
||||
const providers = model.providers
|
||||
.filter((provider) => !provider.disabled)
|
||||
.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
|
||||
return providers[Math.floor(Math.random() * providers.length)]
|
||||
}
|
||||
|
||||
async function authenticate(model: Model) {
|
||||
const apiKey = opts.parseApiKey(input.request.headers)
|
||||
if (!apiKey) return
|
||||
if (!apiKey) {
|
||||
if (model.allowAnonymous) return
|
||||
throw new AuthError("Missing API key.")
|
||||
}
|
||||
|
||||
const data = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
apiKey: KeyTable.id,
|
||||
workspaceID: KeyTable.workspaceID,
|
||||
dataShare: WorkspaceTable.dataShare,
|
||||
balance: BillingTable.balance,
|
||||
paymentMethodID: BillingTable.paymentMethodID,
|
||||
monthlyLimit: BillingTable.monthlyLimit,
|
||||
@@ -250,67 +276,42 @@ export async function handler(
|
||||
})
|
||||
|
||||
const isFree = FREE_WORKSPACES.includes(data.workspaceID)
|
||||
if (!isFree) {
|
||||
if (!data.paymentMethodID) throw new CreditsError("No payment method")
|
||||
if (data.balance <= 0) throw new CreditsError("Insufficient balance")
|
||||
if (
|
||||
data.monthlyLimit &&
|
||||
data.monthlyUsage &&
|
||||
data.timeMonthlyUsageUpdated &&
|
||||
data.monthlyUsage >= centsToMicroCents(data.monthlyLimit * 100)
|
||||
) {
|
||||
const now = new Date()
|
||||
const currentYear = now.getUTCFullYear()
|
||||
const currentMonth = now.getUTCMonth()
|
||||
const dateYear = data.timeMonthlyUsageUpdated.getUTCFullYear()
|
||||
const dateMonth = data.timeMonthlyUsageUpdated.getUTCMonth()
|
||||
if (currentYear === dateYear && currentMonth === dateMonth)
|
||||
throw new MonthlyLimitError(`You have reached your monthly spending limit of $${data.monthlyLimit}.`)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
apiKeyId: data.apiKey,
|
||||
workspaceID: data.workspaceID,
|
||||
dataShare: data.dataShare,
|
||||
billing: {
|
||||
paymentMethodID: data.paymentMethodID,
|
||||
balance: data.balance,
|
||||
monthlyLimit: data.monthlyLimit,
|
||||
monthlyUsage: data.monthlyUsage,
|
||||
timeMonthlyUsageUpdated: data.timeMonthlyUsageUpdated,
|
||||
},
|
||||
isFree,
|
||||
}
|
||||
}
|
||||
|
||||
function validateModel(reqModel: string, authInfo: Awaited<ReturnType<typeof authenticate>>) {
|
||||
const json = JSON.parse(Resource.ZEN_MODELS.value)
|
||||
function validateBilling(model: Model, authInfo: Awaited<ReturnType<typeof authenticate>>) {
|
||||
if (!authInfo || authInfo.isFree) return
|
||||
if (model.allowAnonymous) return
|
||||
|
||||
const allModels = z
|
||||
.record(
|
||||
z.string(),
|
||||
z.object({
|
||||
standard: ModelSchema,
|
||||
dataShare: ModelSchema.optional(),
|
||||
}),
|
||||
)
|
||||
.parse(json)
|
||||
|
||||
if (!(reqModel in allModels)) {
|
||||
throw new ModelError(`Model ${reqModel} not supported`)
|
||||
const billing = authInfo.billing
|
||||
if (!billing.paymentMethodID) throw new CreditsError("No payment method")
|
||||
if (billing.balance <= 0) throw new CreditsError("Insufficient balance")
|
||||
if (
|
||||
billing.monthlyLimit &&
|
||||
billing.monthlyUsage &&
|
||||
billing.timeMonthlyUsageUpdated &&
|
||||
billing.monthlyUsage >= centsToMicroCents(billing.monthlyLimit * 100)
|
||||
) {
|
||||
const now = new Date()
|
||||
const currentYear = now.getUTCFullYear()
|
||||
const currentMonth = now.getUTCMonth()
|
||||
const dateYear = billing.timeMonthlyUsageUpdated.getUTCFullYear()
|
||||
const dateMonth = billing.timeMonthlyUsageUpdated.getUTCMonth()
|
||||
if (currentYear === dateYear && currentMonth === dateMonth)
|
||||
throw new MonthlyLimitError(`You have reached your monthly spending limit of $${billing.monthlyLimit}.`)
|
||||
}
|
||||
const modelId = reqModel as keyof typeof allModels
|
||||
const modelData = authInfo?.dataShare
|
||||
? (allModels[modelId].dataShare ?? allModels[modelId].standard)
|
||||
: allModels[modelId].standard
|
||||
logger.metric({ model: modelId })
|
||||
return { id: modelId, ...modelData }
|
||||
}
|
||||
|
||||
function selectProvider(model: Model, authInfo: Awaited<ReturnType<typeof authenticate>>) {
|
||||
let providers = model.providers.filter((provider) => !provider.disabled)
|
||||
|
||||
if (!authInfo) {
|
||||
providers = providers.filter((provider) => provider.allowAnonymous)
|
||||
if (providers.length === 0) throw new AuthError("Missing API key.")
|
||||
}
|
||||
|
||||
const picks = providers.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
|
||||
return picks[Math.floor(Math.random() * picks.length)]
|
||||
}
|
||||
|
||||
async function trackUsage(
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `workspace` DROP COLUMN `data_share`;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `payment` ADD `time_refunded` timestamp(3);
|
||||
@@ -0,0 +1,643 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "7f3989cb-3e8b-430e-a0f5-f87051d1d824",
|
||||
"prevId": "12189a4e-5083-4b17-b8e3-8279c9a3e61a",
|
||||
"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
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"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_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_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
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"actor": {
|
||||
"name": "actor",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"old_name": {
|
||||
"name": "old_name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"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
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_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
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"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": false,
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,650 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "45b67fb4-77ce-4aa2-b883-1971429c69f5",
|
||||
"prevId": "7f3989cb-3e8b-430e-a0f5-f87051d1d824",
|
||||
"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
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"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_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_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
|
||||
},
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"actor": {
|
||||
"name": "actor",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"old_name": {
|
||||
"name": "old_name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"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
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_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
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"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": false,
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,20 @@
|
||||
"when": 1758289919722,
|
||||
"tag": "0014_demonic_princess_powerful",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "5",
|
||||
"when": 1758428484500,
|
||||
"tag": "0015_cloudy_revanche",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "5",
|
||||
"when": 1758663086739,
|
||||
"tag": "0016_cold_la_nuit",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/console-core",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -30,6 +30,7 @@ export const PaymentTable = mysqlTable(
|
||||
customerID: varchar("customer_id", { length: 255 }),
|
||||
paymentID: varchar("payment_id", { length: 255 }),
|
||||
amount: bigint("amount", { mode: "number" }).notNull(),
|
||||
timeRefunded: utc("time_refunded"),
|
||||
},
|
||||
(table) => [...workspaceIndexes(table)],
|
||||
)
|
||||
|
||||
@@ -7,7 +7,6 @@ export const WorkspaceTable = mysqlTable(
|
||||
id: ulid("id").notNull().primaryKey(),
|
||||
slug: varchar("slug", { length: 255 }),
|
||||
name: varchar("name", { length: 255 }),
|
||||
dataShare: boolean("data_share"),
|
||||
...timestamps,
|
||||
},
|
||||
(table) => [uniqueIndex("slug").on(table.slug)],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/console-function",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -9,7 +9,12 @@ export default {
|
||||
if (event.event.request.method !== "POST") continue
|
||||
|
||||
const url = new URL(event.event.request.url)
|
||||
if (url.pathname !== "/zen/v1/chat/completions") return
|
||||
if (
|
||||
url.pathname !== "/zen/v1/chat/completions" &&
|
||||
url.pathname !== "/zen/v1/messages" &&
|
||||
url.pathname !== "/zen/v1/responses"
|
||||
)
|
||||
return
|
||||
|
||||
let metrics = {
|
||||
event_type: "completions",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/console-scripts",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/function",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
|
||||
@@ -29,9 +29,9 @@ for (const [os, arch] of targets) {
|
||||
console.log(`building ${os}-${arch}`)
|
||||
const name = `${pkg.name}-${os}-${arch}`
|
||||
await $`mkdir -p dist/${name}/bin`
|
||||
await $`CGO_ENABLED=0 GOOS=${os} GOARCH=${GOARCH[arch]} go build -ldflags="-s -w -X main.Version=${version}" -o ../opencode/dist/${name}/bin/tui ../tui/cmd/opencode/main.go`.cwd(
|
||||
"../tui",
|
||||
)
|
||||
await $`CGO_ENABLED=0 GOOS=${os} GOARCH=${GOARCH[arch]} go build -ldflags="-s -w -X main.Version=${version}" -o ../opencode/dist/${name}/bin/tui ../tui/cmd/opencode/main.go`
|
||||
.cwd("../tui")
|
||||
.quiet()
|
||||
await Bun.build({
|
||||
compile: {
|
||||
target: `bun-${os}-${arch}` as any,
|
||||
|
||||
@@ -5,10 +5,14 @@ import { $ } from "bun"
|
||||
|
||||
import pkg from "../package.json"
|
||||
|
||||
const dry = process.env["OPENCODE_DRY"] === "true"
|
||||
const version = process.env["OPENCODE_VERSION"]!
|
||||
const snapshot = process.env["OPENCODE_SNAPSHOT"] === "true"
|
||||
const npmTag = snapshot ? "snapshot" : "latest"
|
||||
let version = process.env["OPENCODE_VERSION"]
|
||||
const tag = process.env["OPENCODE_TAG"] ?? (snapshot ? "snapshot" : "latest")
|
||||
if (!version && snapshot) {
|
||||
version = `0.0.0-${tag}-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
|
||||
process.env["OPENCODE_VERSION"] = version
|
||||
}
|
||||
if (!version) throw new Error("OPENCODE_VERSION is required")
|
||||
|
||||
console.log(`publishing ${version}`)
|
||||
|
||||
@@ -41,12 +45,10 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
||||
2,
|
||||
),
|
||||
)
|
||||
if (!dry) {
|
||||
for (const [name] of Object.entries(binaries)) {
|
||||
await $`cd dist/${name} && chmod 777 -R . && bun publish --access public --tag ${npmTag}`
|
||||
}
|
||||
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${npmTag}`
|
||||
for (const [name] of Object.entries(binaries)) {
|
||||
await $`cd dist/${name} && chmod 777 -R . && bun publish --access public --tag ${tag}`
|
||||
}
|
||||
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${tag}`
|
||||
|
||||
if (!snapshot) {
|
||||
for (const key of Object.keys(binaries)) {
|
||||
@@ -142,7 +144,7 @@ if (!snapshot) {
|
||||
await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
|
||||
await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
|
||||
await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"`
|
||||
if (!dry) await $`cd ./dist/aur-${pkg} && git push`
|
||||
await $`cd ./dist/aur-${pkg} && git push`
|
||||
}
|
||||
|
||||
// Homebrew formula
|
||||
@@ -201,5 +203,5 @@ if (!snapshot) {
|
||||
await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula)
|
||||
await $`cd ./dist/homebrew-tap && git add opencode.rb`
|
||||
await $`cd ./dist/homebrew-tap && git commit -m "Update to v${version}"`
|
||||
if (!dry) await $`cd ./dist/homebrew-tap && git push`
|
||||
await $`cd ./dist/homebrew-tap && git push`
|
||||
}
|
||||
|
||||
@@ -64,6 +64,12 @@ export const RunCommand = cmd({
|
||||
type: "string",
|
||||
describe: "agent to use",
|
||||
})
|
||||
.option("format", {
|
||||
type: "string",
|
||||
choices: ["default", "json"],
|
||||
default: "default",
|
||||
describe: "format: default (formatted) or json (raw JSON events)",
|
||||
})
|
||||
},
|
||||
handler: async (args) => {
|
||||
let message = args.message.join(" ")
|
||||
@@ -144,6 +150,20 @@ export const RunCommand = cmd({
|
||||
)
|
||||
}
|
||||
|
||||
function outputJsonEvent(type: string, data: any) {
|
||||
if (args.format === "json") {
|
||||
const jsonEvent = {
|
||||
type,
|
||||
timestamp: Date.now(),
|
||||
sessionID: session?.id,
|
||||
...data,
|
||||
}
|
||||
process.stdout.write(JSON.stringify(jsonEvent) + "\n")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
let text = ""
|
||||
|
||||
Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
|
||||
@@ -152,6 +172,7 @@ export const RunCommand = cmd({
|
||||
const part = evt.properties.part
|
||||
|
||||
if (part.type === "tool" && part.state.status === "completed") {
|
||||
if (outputJsonEvent("tool_use", { part })) return
|
||||
const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD]
|
||||
const title =
|
||||
part.state.title ||
|
||||
@@ -169,6 +190,7 @@ export const RunCommand = cmd({
|
||||
text = part.text
|
||||
|
||||
if (part.time?.end) {
|
||||
if (outputJsonEvent("text", { part })) return
|
||||
UI.empty()
|
||||
UI.println(UI.markdown(text))
|
||||
UI.empty()
|
||||
@@ -189,6 +211,7 @@ export const RunCommand = cmd({
|
||||
}
|
||||
errorMsg = errorMsg ? errorMsg + "\n" + err : err
|
||||
|
||||
if (outputJsonEvent("error", { error })) return
|
||||
UI.error(err)
|
||||
})
|
||||
|
||||
@@ -225,6 +248,7 @@ export const RunCommand = cmd({
|
||||
const isPiped = !process.stdout.isTTY
|
||||
if (isPiped) {
|
||||
const match = result.parts.findLast((x: any) => x.type === "text") as any
|
||||
if (outputJsonEvent("text", { text: match })) return
|
||||
if (match) process.stdout.write(UI.markdown(match.text))
|
||||
if (errorMsg) process.stdout.write(errorMsg)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Flag } from "../flag/flag"
|
||||
import { Auth } from "../auth"
|
||||
import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
|
||||
import { Instance } from "../project/instance"
|
||||
import { LSPServer } from "../lsp/server"
|
||||
|
||||
export namespace Config {
|
||||
const log = Log.create({ service: "config" })
|
||||
@@ -482,7 +483,22 @@ export namespace Config {
|
||||
}),
|
||||
]),
|
||||
)
|
||||
.optional(),
|
||||
.optional()
|
||||
.refine(
|
||||
(data) => {
|
||||
if (!data) return true
|
||||
const serverIds = new Set(Object.values(LSPServer).map((s) => s.id))
|
||||
|
||||
return Object.entries(data).every(([id, config]) => {
|
||||
if (config.disabled) return true
|
||||
if (serverIds.has(id)) return true
|
||||
return Boolean(config.extensions)
|
||||
})
|
||||
},
|
||||
{
|
||||
error: "For custom LSP servers, 'extensions' array is required.",
|
||||
},
|
||||
),
|
||||
instructions: z.array(z.string()).optional().describe("Additional instruction files or patterns to include"),
|
||||
layout: Layout.optional().describe("@deprecated Always uses stretch layout."),
|
||||
permission: z
|
||||
|
||||
@@ -28,7 +28,6 @@ export namespace FileWatcher {
|
||||
const ignore = (cfg.watcher?.ignore ?? []).map((v) => new Bun.Glob(v))
|
||||
const watcher = chokidar.watch(Instance.directory, {
|
||||
ignoreInitial: true,
|
||||
awaitWriteFinish: true,
|
||||
ignored: (filepath) => {
|
||||
return FileIgnore.match(filepath, {
|
||||
extra: ignore,
|
||||
|
||||
@@ -72,7 +72,7 @@ export namespace LSP {
|
||||
...existing,
|
||||
id: name,
|
||||
root: existing?.root ?? (async () => Instance.directory),
|
||||
extensions: item.extensions ?? existing.extensions,
|
||||
extensions: item.extensions ?? existing?.extensions ?? [],
|
||||
spawn: async (root) => {
|
||||
return {
|
||||
process: spawn(item.command[0], item.command.slice(1), {
|
||||
@@ -126,12 +126,22 @@ export namespace LSP {
|
||||
result.push(match)
|
||||
continue
|
||||
}
|
||||
const handle = await server.spawn(root).catch((err) => {
|
||||
s.broken.add(root + server.id)
|
||||
log.error(`Failed to spawn LSP server ${server.id}`, { error: err })
|
||||
return undefined
|
||||
})
|
||||
const handle = await server
|
||||
.spawn(root)
|
||||
.then((h) => {
|
||||
if (h === undefined) {
|
||||
s.broken.add(root + server.id)
|
||||
}
|
||||
return h
|
||||
})
|
||||
.catch((err) => {
|
||||
s.broken.add(root + server.id)
|
||||
log.error(`Failed to spawn LSP server ${server.id}`, { error: err })
|
||||
return undefined
|
||||
})
|
||||
if (!handle) continue
|
||||
log.info("spawned lsp server", { serverID: server.id })
|
||||
|
||||
const client = await LSPClient.create({
|
||||
serverID: server.id,
|
||||
server: handle,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { spawn, type ChildProcessWithoutNullStreams } from "child_process"
|
||||
import path from "path"
|
||||
import os from "os"
|
||||
import { Global } from "../global"
|
||||
import { Log } from "../util/log"
|
||||
import { BunProc } from "../bun"
|
||||
@@ -706,4 +707,91 @@ export namespace LSPServer {
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const JDTLS: Info = {
|
||||
id: "jdtls",
|
||||
root: NearestRoot(["pom.xml", "build.gradle", "build.gradle.kts", ".project", ".classpath"]),
|
||||
extensions: [".java"],
|
||||
async spawn(root) {
|
||||
const java = Bun.which("java")
|
||||
if (!java) {
|
||||
log.error("Java 21 or newer is required to run the JDTLS. Please install it first.")
|
||||
return
|
||||
}
|
||||
const javaMajorVersion = await $`java -version`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.then(({ stderr }) => {
|
||||
const m = /"(\d+)\.\d+\.\d+"/.exec(stderr.toString())
|
||||
return !m ? undefined : parseInt(m[1])
|
||||
})
|
||||
if (javaMajorVersion == null || javaMajorVersion < 21) {
|
||||
log.error("JDTLS requires at least Java 21.")
|
||||
return
|
||||
}
|
||||
const distPath = path.join(Global.Path.bin, "jdtls")
|
||||
const launcherDir = path.join(distPath, "plugins")
|
||||
const installed = await fs.exists(launcherDir)
|
||||
if (!installed) {
|
||||
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||
log.info("Downloading JDTLS LSP server.")
|
||||
await fs.mkdir(distPath, { recursive: true })
|
||||
const releaseURL =
|
||||
"https://www.eclipse.org/downloads/download.php?file=/jdtls/snapshots/jdt-language-server-latest.tar.gz"
|
||||
const archivePath = path.join(distPath, "release.tar.gz")
|
||||
await $`curl -L -o '${archivePath}' '${releaseURL}'`.quiet().nothrow()
|
||||
await $`tar -xzf ${archivePath}`.cwd(distPath).quiet().nothrow()
|
||||
await fs.rm(archivePath, { force: true })
|
||||
}
|
||||
const jarFileName = await $`ls org.eclipse.equinox.launcher_*.jar`
|
||||
.cwd(launcherDir)
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.then(({ stdout }) => stdout.toString().trim())
|
||||
const launcherJar = path.join(launcherDir, jarFileName)
|
||||
if (!(await fs.exists(launcherJar))) {
|
||||
log.error(`Failed to locate the JDTLS launcher module in the installed directory: ${distPath}.`)
|
||||
return
|
||||
}
|
||||
const configFile = path.join(
|
||||
distPath,
|
||||
(() => {
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
return "config_mac"
|
||||
case "linux":
|
||||
return "config_linux"
|
||||
case "win32":
|
||||
return "config_windows"
|
||||
default:
|
||||
return "config_linux"
|
||||
}
|
||||
})(),
|
||||
)
|
||||
const dataDir = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-jdtls-data"))
|
||||
return {
|
||||
process: spawn(
|
||||
java,
|
||||
[
|
||||
"-jar",
|
||||
launcherJar,
|
||||
"-configuration",
|
||||
configFile,
|
||||
"-data",
|
||||
dataDir,
|
||||
"-Declipse.application=org.eclipse.jdt.ls.core.id1",
|
||||
"-Dosgi.bundles.defaultStartLevel=4",
|
||||
"-Declipse.product=org.eclipse.jdt.ls.core.product",
|
||||
"-Dlog.level=ALL",
|
||||
"--add-modules=ALL-SYSTEM",
|
||||
"--add-opens java.base/java.util=ALL-UNNAMED",
|
||||
"--add-opens java.base/java.lang=ALL-UNNAMED",
|
||||
],
|
||||
{
|
||||
cwd: root,
|
||||
},
|
||||
),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export namespace Plugin {
|
||||
}
|
||||
const plugins = [...(config.plugin ?? [])]
|
||||
if (!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS) {
|
||||
plugins.push("opencode-copilot-auth@0.0.2")
|
||||
plugins.push("opencode-copilot-auth@0.0.3")
|
||||
plugins.push("opencode-anthropic-auth@0.0.2")
|
||||
}
|
||||
for (let plugin of plugins) {
|
||||
|
||||
@@ -16,10 +16,7 @@ import { Flag } from "../flag/flag"
|
||||
export namespace Provider {
|
||||
const log = Log.create({ service: "provider" })
|
||||
|
||||
type CustomLoader = (
|
||||
provider: ModelsDev.Provider,
|
||||
api?: string,
|
||||
) => Promise<{
|
||||
type CustomLoader = (provider: ModelsDev.Provider) => Promise<{
|
||||
autoload: boolean
|
||||
getModel?: (sdk: any, modelID: string) => Promise<any>
|
||||
options?: Record<string, any>
|
||||
@@ -40,6 +37,19 @@ export namespace Provider {
|
||||
}
|
||||
},
|
||||
async opencode(input) {
|
||||
const hasKey = await (async () => {
|
||||
if (input.env.some((item) => process.env[item])) return true
|
||||
if (await Auth.get(input.id)) return true
|
||||
return false
|
||||
})()
|
||||
|
||||
if (!hasKey) {
|
||||
for (const [key, value] of Object.entries(input.models)) {
|
||||
if (value.cost.input === 0) continue
|
||||
delete input.models[key]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
autoload: Object.keys(input.models).length > 0,
|
||||
options: {},
|
||||
|
||||
@@ -92,14 +92,14 @@ export namespace ProviderTransform {
|
||||
}
|
||||
|
||||
if (modelID.includes("gpt-5") && !modelID.includes("gpt-5-chat")) {
|
||||
result["reasoningEffort"] = "high"
|
||||
result["reasoningEffort"] = "medium"
|
||||
if (providerID !== "azure") {
|
||||
result["textVerbosity"] = "low"
|
||||
result["textVerbosity"] = modelID.includes("codex") ? "medium" : "low"
|
||||
}
|
||||
if (providerID === "opencode") {
|
||||
result["promptCacheKey"] = sessionID
|
||||
// result["include"] = ["reasoning.encrypted_content"]
|
||||
// result["reasoningSummary"] = "detailed"
|
||||
// result["reasoningSummary"] = "auto"
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -14,7 +14,10 @@ export namespace Storage {
|
||||
const MIGRATIONS: Migration[] = [
|
||||
async (dir) => {
|
||||
const project = path.resolve(dir, "../project")
|
||||
for await (const projectDir of new Bun.Glob("*").scan({ cwd: project, onlyFiles: false })) {
|
||||
for await (const projectDir of new Bun.Glob("*").scan({
|
||||
cwd: project,
|
||||
onlyFiles: false,
|
||||
})) {
|
||||
log.info(`migrating project ${projectDir}`)
|
||||
let projectID = projectDir
|
||||
const fullProjectDir = path.join(project, projectDir)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
@@ -478,9 +478,6 @@ func (m *messagesComponent) renderView() tea.Cmd {
|
||||
}
|
||||
|
||||
case opencode.AssistantMessage:
|
||||
if casted.Summary {
|
||||
continue
|
||||
}
|
||||
if casted.ID == m.app.Session.Revert.MessageID {
|
||||
reverted = true
|
||||
revertedMessageCount = 1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode/web",
|
||||
"type": "module",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -495,9 +495,7 @@ For quick reference, here are common setups.
|
||||
}
|
||||
```
|
||||
|
||||
See the full permissions guide for more patterns.
|
||||
|
||||
- /docs/permissions
|
||||
See the full [permissions guide](/docs/permissions) for more patterns.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -144,6 +144,8 @@ opencode models
|
||||
|
||||
This command displays all models available across your configured providers in the format `provider/model`.
|
||||
|
||||
This is useful for figuring out the exact model name to use in [your config](/docs/config/).
|
||||
|
||||
---
|
||||
|
||||
### run
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
---
|
||||
title: Custom tools
|
||||
description: Create custom tools to extend opencode capabilities.
|
||||
title: Custom Tools
|
||||
description: Create tools the LLM can call in opencode.
|
||||
---
|
||||
|
||||
Custom tools are functions you create that the LLM can call during conversations. They work alongside opencode's built-in tools like `read`, `write`, and `bash`.
|
||||
|
||||
---
|
||||
|
||||
## Tool structure
|
||||
## Creating a tool
|
||||
|
||||
Tools are defined as `.ts/.js` files in the `.opencode/tool/` directory. They
|
||||
can also be defined globally in `~/.config/opencode/tool/`.
|
||||
Tools are defined as **TypeScript** or **JavaScript** files.
|
||||
|
||||
The easiest way to create tools is using the `tool()` helper which provides type safety and validation. Use `tool.schema` (which is just [Zod](https://zod.dev)) to define argument types:
|
||||
---
|
||||
|
||||
```ts title=".opencode/tool/database.ts"
|
||||
### Location
|
||||
|
||||
They can defined:
|
||||
|
||||
- Locally by placing them in the `.opencode/tool/` directory of your project.
|
||||
- Or globally, by placing them in `~/.config/opencode/tool/`.
|
||||
|
||||
---
|
||||
|
||||
### Structure
|
||||
|
||||
The easiest way to create tools is using the `tool()` helper which provides type-safety and validation.
|
||||
|
||||
```ts title=".opencode/tool/database.ts" {1}
|
||||
import { tool } from "@opencode-ai/plugin"
|
||||
|
||||
export default tool({
|
||||
@@ -29,9 +41,23 @@ export default tool({
|
||||
})
|
||||
```
|
||||
|
||||
The **filename** becomes the **tool name**. The above creates a `database` tool.
|
||||
|
||||
---
|
||||
|
||||
### Arguments
|
||||
|
||||
You can use `tool.schema`, which is just [Zod](https://zod.dev), to define argument types.
|
||||
|
||||
```ts "tool.schema"
|
||||
args: {
|
||||
query: tool.schema.string().describe("SQL query to execute")
|
||||
}
|
||||
```
|
||||
|
||||
You can also import [Zod](https://zod.dev) directly and return a plain object:
|
||||
|
||||
```ts
|
||||
```ts {6}
|
||||
import { z } from "zod"
|
||||
|
||||
export default {
|
||||
@@ -46,13 +72,31 @@ export default {
|
||||
}
|
||||
```
|
||||
|
||||
The filename becomes the tool name. This creates a `database` tool.
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Tools receive context about the current session:
|
||||
|
||||
```ts title=".opencode/tool/project.ts" {8}
|
||||
import { tool } from "@opencode-ai/plugin"
|
||||
|
||||
export default tool({
|
||||
description: "Get project information",
|
||||
args: {},
|
||||
async execute(args, context) {
|
||||
// Access context information
|
||||
const { agent, sessionID, messageID } = context
|
||||
return `Agent: ${agent}, Session: ${sessionID}, Message: ${messageID}`
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multiple tools per file
|
||||
|
||||
You can export multiple tools from a single file. Each export becomes a separate tool with the name `<filename>_<exportname>`:
|
||||
You can also export multiple tools from a single file. Each export becomes **a separate tool** with the name **`<filename>_<exportname>`**:
|
||||
|
||||
```ts title=".opencode/tool/math.ts"
|
||||
import { tool } from "@opencode-ai/plugin"
|
||||
@@ -81,45 +125,3 @@ export const multiply = tool({
|
||||
```
|
||||
|
||||
This creates two tools: `math_add` and `math_multiply`.
|
||||
|
||||
---
|
||||
|
||||
## Arguments
|
||||
|
||||
Use `tool.schema` (which is just [Zod](https://zod.dev)) to define tool arguments with validation and descriptions:
|
||||
|
||||
```ts title=".opencode/tool/calculator.ts"
|
||||
import { tool } from "@opencode-ai/plugin"
|
||||
|
||||
export default tool({
|
||||
description: "Perform mathematical calculations",
|
||||
args: {
|
||||
expression: tool.schema.string().describe("Mathematical expression to evaluate"),
|
||||
precision: tool.schema.number().optional().describe("Decimal precision"),
|
||||
},
|
||||
async execute(args) {
|
||||
// Your calculation logic here
|
||||
return `Result: ${eval(args.expression).toFixed(args.precision || 2)}`
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Tools receive context about the current session:
|
||||
|
||||
```ts title=".opencode/tool/project.ts"
|
||||
import { tool } from "@opencode-ai/plugin"
|
||||
|
||||
export default tool({
|
||||
description: "Get project information",
|
||||
args: {},
|
||||
async execute(args, context) {
|
||||
// Access context information
|
||||
const { agent, sessionID, messageID } = context
|
||||
return `Agent: ${agent}, Session: ${sessionID}, Message: ${messageID}`
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -24,6 +24,8 @@ To install opencode on VS Code and popular forks like Cursor, Windsurf, VSCodium
|
||||
2. Open the integrated terminal
|
||||
3. Run `opencode` - the extension installs automatically
|
||||
|
||||
If on the other hand you want to use your own IDE when you run `/editor` or `/export` from the TUI, you'll need to set `export EDITOR="code --wait"`. [Learn more](/docs/tui/#editor-setup).
|
||||
|
||||
---
|
||||
|
||||
### Manual Install
|
||||
|
||||
@@ -37,7 +37,7 @@ The easiest way to install opencode is through the install script.
|
||||
curl -fsSL https://opencode.ai/install | bash
|
||||
```
|
||||
|
||||
You can also install it with the following:
|
||||
You can also install it with the following commands:
|
||||
|
||||
- **Using Node.js**
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: LSP Servers
|
||||
description: opencode integrates with your LSP servers.
|
||||
---
|
||||
|
||||
opencode integrates with your Language Server Protocol (LSP) to help the LLM interacts with your codebase. It uses diagnostics to provide feedback to the LLM.
|
||||
opencode integrates with your Language Server Protocol (LSP) to help the LLM interact with your codebase. It uses diagnostics to provide feedback to the LLM.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -356,6 +356,8 @@ To use your GitHub Copilot subscription with opencode:
|
||||
:::note
|
||||
Some models might need a [Pro+
|
||||
subscription](https://github.com/features/copilot/plans) to use.
|
||||
|
||||
Some models need to be manually enabled in your [GitHub Copilot settings](https://docs.github.com/en/copilot/how-tos/use-ai-models/configure-access-to-ai-models#setup-for-individual-use).
|
||||
:::
|
||||
|
||||
1. Run `opencode auth login` and select GitHub Copilot.
|
||||
|
||||
@@ -25,18 +25,18 @@ Without truecolor support, themes may appear with reduced color accuracy or fall
|
||||
|
||||
opencode comes with several built-in themes.
|
||||
|
||||
| Name | Description |
|
||||
| ------------ | ------------------------------------------ |
|
||||
| `system` | Adapts to your terminal's background color |
|
||||
| `tokyonight` | Based on the Tokyonight theme |
|
||||
| `everforest` | Based on the Everforest theme |
|
||||
| `ayu` | Based on the Ayu dark theme |
|
||||
| `catppuccin` | Based on the Catppuccin theme |
|
||||
| `gruvbox` | Based on the Gruvbox theme |
|
||||
| `kanagawa` | Based on the Kanagawa theme |
|
||||
| `nord` | Based on the Nord theme |
|
||||
| `matrix` | Hacker-style green on black theme |
|
||||
| `one-dark` | Based on the Atom One Dark theme |
|
||||
| Name | Description |
|
||||
| ------------ | ---------------------------------------------------------------------------- |
|
||||
| `system` | Adapts to your terminal's background color |
|
||||
| `tokyonight` | Based on the [Tokyonight](https://github.com/folke/tokyonight.nvim) theme |
|
||||
| `everforest` | Based on the [Everforest](https://github.com/sainnhe/everforest) theme |
|
||||
| `ayu` | Based on the [Ayu](https://github.com/ayu-theme) dark theme |
|
||||
| `catppuccin` | Based on the [Catppuccin](https://github.com/catppuccin) theme |
|
||||
| `gruvbox` | Based on the [Gruvbox](https://github.com/morhetz/gruvbox) theme |
|
||||
| `kanagawa` | Based on the [Kanagawa](https://github.com/rebelot/kanagawa.nvim) theme |
|
||||
| `nord` | Based on the [Nord](https://github.com/nordtheme/nord) theme |
|
||||
| `matrix` | Hacker-style green on black theme |
|
||||
| `one-dark` | Based on the [Atom One](https://github.com/Th3Whit3Wolf/one-nvim) Dark theme |
|
||||
|
||||
And more, we are constantly adding new themes.
|
||||
|
||||
|
||||
@@ -185,6 +185,9 @@ Redo a previously undone message. Only available after using `/undo`.
|
||||
Any file changes will also be restored.
|
||||
:::
|
||||
|
||||
Internally, this uses Git to manage the file changes. So your project **needs to
|
||||
be a Git repository**.
|
||||
|
||||
```bash frame="none"
|
||||
/redo
|
||||
```
|
||||
@@ -237,6 +240,9 @@ Undo last message in the conversation. Removes the most recent user message, all
|
||||
Any file changes made will also be reverted.
|
||||
:::
|
||||
|
||||
Internally, this uses Git to manage the file changes. So your project **needs to
|
||||
be a Git repository**.
|
||||
|
||||
```bash frame="none"
|
||||
/undo
|
||||
```
|
||||
@@ -266,7 +272,8 @@ Both the `/editor` and `/export` commands use the editor specified in your `EDIT
|
||||
export EDITOR=nano
|
||||
export EDITOR=vim
|
||||
|
||||
# For GUI editors (VS Code, Cursor, VSCodium, Windsurf, Zed, etc.) include --wait
|
||||
# For GUI editors, VS Code, Cursor, VSCodium, Windsurf, Zed, etc.
|
||||
# include --wait
|
||||
export EDITOR="code --wait"
|
||||
```
|
||||
|
||||
@@ -279,7 +286,8 @@ Both the `/editor` and `/export` commands use the editor specified in your `EDIT
|
||||
```bash
|
||||
set EDITOR=notepad
|
||||
|
||||
# For GUI editors (VS Code, Cursor, VSCodium, Windsurf, Zed, etc.) include --wait
|
||||
# For GUI editors, VS Code, Cursor, VSCodium, Windsurf, Zed, etc.
|
||||
# include --wait
|
||||
set EDITOR=code --wait
|
||||
```
|
||||
|
||||
@@ -292,7 +300,8 @@ Both the `/editor` and `/export` commands use the editor specified in your `EDIT
|
||||
```powershell
|
||||
$env:EDITOR = "notepad"
|
||||
|
||||
# For GUI editors (VS Code, Cursor, VSCodium, Windsurf, Zed, etc.) include --wait
|
||||
# For GUI editors, VS Code, Cursor, VSCodium, Windsurf, Zed, etc.
|
||||
# include --wait
|
||||
$env:EDITOR = "code --wait"
|
||||
```
|
||||
|
||||
|
||||
@@ -64,7 +64,8 @@ You can also access our models through the following API endpoints.
|
||||
|
||||
| Model | Mode ID | Endpoint | AI SDK Package |
|
||||
| ---------------- | ---------------- | --------------------------------------------- | --------------------------- |
|
||||
| GPT-5 | gpt-5 | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
|
||||
| GPT 5 | gpt-5 | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
|
||||
| GPT 5 Codex | gpt-5-codex | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
|
||||
| Claude Sonnet 4 | claude-sonnet-4 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Claude Opus 4.1 | claude-opus-4-1 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
@@ -81,19 +82,24 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**.
|
||||
| Model | Input | Output | Cached Read | Cached Write |
|
||||
| ------------------------------- | ------ | ------ | ----------- | ------------ |
|
||||
| Qwen3 Coder 480B | $0.45 | $1.50 | - | - |
|
||||
| Grok Code Fast 1 | Free | Free | Free | - |
|
||||
| Grok Code Fast 1 | Free | Free | - | - |
|
||||
| Code Supernova | Free | Free | - | - |
|
||||
| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
|
||||
| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
|
||||
| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
||||
| Claude Opus 4.1 | $15.00 | $75.00 | $1.50 | $18.75 |
|
||||
| GPT 5 | $1.25 | $10.00 | $0.125 | - |
|
||||
| GPT 5 Codex | $1.25 | $10.00 | $0.125 | - |
|
||||
| Kimi K2 | $0.60 | $2.50 | - | - |
|
||||
|
||||
:::note
|
||||
Credit card fees are passed along at cost; we don’t charge anything beyond that.
|
||||
:::
|
||||
|
||||
Grok Code Fast 1 is currently free on opencode for a limited time. The xAI team is using this time to collect feedback and improve Grok Code.
|
||||
The free models:
|
||||
|
||||
- Grok Code Fast 1 is currently free on opencode for a limited time. The xAI team is using this time to collect feedback and improve Grok Code.
|
||||
- Code Supernova is a stealth model that's free on opencode for a limited time. The team is using this time to collect feedback and improve the model.
|
||||
|
||||
:::tip
|
||||
Subscription plans and a free tier are coming soon.
|
||||
@@ -108,6 +114,8 @@ Subscription plans and a free tier are coming soon.
|
||||
All our models are hosted in the US. Our providers follow a zero-retention policy and do not use your data for model training, with the following exceptions:
|
||||
|
||||
- Grok Code Fast 1: During its free period, collected data may be used to improve Grok Code.
|
||||
- Code Supernova: During its free period, collected data may be used to improve
|
||||
the model.
|
||||
- OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: Requests are retained for 30 days in accordance with [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "0.10.3",
|
||||
"version": "0.11.3",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user