mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-03 08:46:15 -04:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22a6849ff8 | |||
| dca3a5d80d | |||
| 508067ba5d | |||
| b6c9df970a | |||
| 1f725cc3ed | |||
| 6c99b833e4 | |||
| cd3780b7f5 | |||
| a440e09cfe | |||
| 27c211ef86 | |||
| cd528ae78f | |||
| 06c42093c8 | |||
| 0534bc0c09 | |||
| 4f33594b99 | |||
| e3f9e7785e | |||
| a20fc2dfdf | |||
| 2bf0e42367 | |||
| 10998d62b9 | |||
| aee240150b | |||
| cdd6e98af9 | |||
| 6417edf998 | |||
| 9a0735de76 | |||
| a470859f6f | |||
| f47c7c5a07 | |||
| c2f57ea74d | |||
| 9e8fd16e6e | |||
| 1b17d8070b | |||
| 1db028dc05 | |||
| b351b75156 | |||
| 2faa28e162 | |||
| bdf77701cf | |||
| 889c276558 | |||
| 9c6192b00d |
@@ -99,3 +99,6 @@
|
||||
| 2025-10-02 | 440,852 (+7,261) | 348,099 (+6,357) | 788,951 (+13,618) |
|
||||
| 2025-10-03 | 446,829 (+5,977) | 359,937 (+11,838) | 806,766 (+17,815) |
|
||||
| 2025-10-04 | 452,561 (+5,732) | 370,386 (+10,449) | 822,947 (+16,181) |
|
||||
| 2025-10-05 | 455,559 (+2,998) | 374,745 (+4,359) | 830,304 (+7,357) |
|
||||
| 2025-10-06 | 460,927 (+5,368) | 379,489 (+4,744) | 840,416 (+10,112) |
|
||||
| 2025-10-07 | 467,336 (+6,409) | 385,438 (+5,949) | 852,774 (+12,358) |
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"@kobalte/core": "catalog:",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
"@opencode-ai/console-resource": "workspace:*",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
"@solidjs/router": "^0.15.0",
|
||||
"@solidjs/start": "^1.1.0",
|
||||
@@ -27,7 +28,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -47,7 +48,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -67,7 +68,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -83,19 +84,14 @@
|
||||
},
|
||||
"packages/console/scripts": {
|
||||
"name": "@opencode-ai/console-scripts",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
"tsx": "4.20.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -129,7 +125,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
@@ -144,7 +140,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -197,7 +193,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -209,7 +205,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
},
|
||||
@@ -221,7 +217,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -243,7 +239,7 @@
|
||||
"sharp": "0.32.5",
|
||||
"shiki": "3.4.2",
|
||||
"solid-js": "catalog:",
|
||||
"toolbeam-docs-theme": "0.4.6",
|
||||
"toolbeam-docs-theme": "0.4.7",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:",
|
||||
@@ -2801,7 +2797,7 @@
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.4.6", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-s4yKn3PYnmPCl6MHPkQ2MedSgVh7FMAP64DiXnAfCls/H/RR6iTe0/SFgPTprz+HXXaKvv2iPo3XuvmDRY6cxQ=="],
|
||||
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.4.7", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-oVA/V4M4s4vtLljfnZrOSuCNomek5h9jIYkr92l4QgAQvB3ht+D7xAJIy27IGVJzYA5scUE1OK84ZZqeajoeWw=="],
|
||||
|
||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
"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.14.3"
|
||||
"version": "0.14.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
"@opencode-ai/console-resource": "workspace:*",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
"@solidjs/router": "^0.15.0",
|
||||
"@solidjs/start": "^1.1.0",
|
||||
|
||||
@@ -73,6 +73,7 @@ export const getActor = async (workspace?: string): Promise<Actor.Info> => {
|
||||
properties: {
|
||||
userID: user.id,
|
||||
workspaceID: user.workspaceID,
|
||||
accountID: user.accountID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { query } from "@solidjs/router"
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
|
||||
export const beta = query(async (workspaceID?: string) => {
|
||||
"use server"
|
||||
return Resource.App.stage === "production" ? workspaceID === "wrk_01K46JDFR0E75SG2Q8K172KF3Y" : true
|
||||
}, "beta")
|
||||
@@ -1,11 +1,29 @@
|
||||
import { Account } from "@opencode-ai/console-core/account.js"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { redirect } from "@solidjs/router"
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
try {
|
||||
const workspaces = await withActor(async () => Account.workspaces())
|
||||
const workspaces = await withActor(async () => {
|
||||
const actor = Actor.assert("account")
|
||||
return Database.transaction(async (tx) =>
|
||||
tx
|
||||
.select({ id: WorkspaceTable.id })
|
||||
.from(UserTable)
|
||||
.innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id))
|
||||
.where(
|
||||
and(
|
||||
eq(UserTable.accountID, actor.properties.accountID),
|
||||
isNull(UserTable.timeDeleted),
|
||||
isNull(WorkspaceTable.timeDeleted),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
return redirect(`/workspace/${workspaces[0].id}`)
|
||||
} catch {
|
||||
return redirect("/auth/authorize")
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
[data-component="workspace-picker"] {
|
||||
position: relative;
|
||||
/* Override blue accent colors with neutral colors */
|
||||
--color-accent: var(--color-border);
|
||||
--color-accent-hover: var(--color-border);
|
||||
--color-accent-active: var(--color-border);
|
||||
--color-primary: var(--color-border);
|
||||
--color-primary-hover: var(--color-border);
|
||||
--color-primary-active: var(--color-border);
|
||||
--color-primary-alpha-20: transparent;
|
||||
|
||||
[data-slot="trigger"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
font-family: var(--font-sans);
|
||||
cursor: pointer;
|
||||
min-width: 200px;
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="chevron"] {
|
||||
flex-shrink: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
[data-slot="dropdown"] button {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
/* Ensure text inside buttons has no underline */
|
||||
[data-slot="dropdown"] button * {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
[data-slot="dropdown"] {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
margin-top: var(--space-1);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
max-height: 240px;
|
||||
overflow-y: auto;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="option"],
|
||||
[data-slot="create-option"] {
|
||||
width: 100%;
|
||||
padding: var(--space-2-5) var(--space-3);
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
font-family: var(--font-sans);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-surface);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: var(--border-radius-sm);
|
||||
border-top-right-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-left-radius: var(--border-radius-sm);
|
||||
border-bottom-right-radius: var(--border-radius-sm);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="option"][data-selected="true"] {
|
||||
background-color: transparent;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
[data-slot="create-option"] {
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
[data-slot="create-form"] {
|
||||
margin-top: var(--space-4);
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-surface);
|
||||
}
|
||||
|
||||
[data-slot="create-input-group"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
align-items: center;
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="create-input"] {
|
||||
flex: 1;
|
||||
padding: var(--space-2-5) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
font-family: var(--font-sans);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-border);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
button[type="submit"],
|
||||
button[type="button"] {
|
||||
padding: var(--space-2-5) var(--space-4);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
&[data-color="primary"] {
|
||||
background-color: var(--color-text-secondary);
|
||||
border-color: var(--color-text-secondary);
|
||||
color: var(--color-bg);
|
||||
}
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import { query, useParams, action, createAsync, redirect } from "@solidjs/router"
|
||||
import { For, Show, createEffect, onCleanup } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||
import { Workspace } from "@opencode-ai/console-core/workspace.js"
|
||||
import "./workspace-picker.css"
|
||||
|
||||
const getWorkspaces = query(async () => {
|
||||
"use server"
|
||||
return withActor(async () => {
|
||||
return Database.transaction((tx) =>
|
||||
tx
|
||||
.select({
|
||||
id: WorkspaceTable.id,
|
||||
name: WorkspaceTable.name,
|
||||
slug: WorkspaceTable.slug,
|
||||
})
|
||||
.from(UserTable)
|
||||
.innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id))
|
||||
.where(and(eq(UserTable.accountID, Actor.account()), isNull(WorkspaceTable.timeDeleted))),
|
||||
)
|
||||
})
|
||||
}, "workspaces")
|
||||
|
||||
const createWorkspace = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const name = form.get("workspaceName") as string
|
||||
if (name?.trim()) {
|
||||
return withActor(async () => {
|
||||
const workspaceID = await Workspace.create({ name: name.trim() })
|
||||
return redirect(`/workspace/${workspaceID}`)
|
||||
})
|
||||
}
|
||||
}, "createWorkspace")
|
||||
|
||||
export function WorkspacePicker() {
|
||||
const params = useParams()
|
||||
const workspaces = createAsync(() => getWorkspaces())
|
||||
const [store, setStore] = createStore({
|
||||
showForm: false,
|
||||
showDropdown: false,
|
||||
})
|
||||
let dropdownRef: HTMLDivElement | undefined
|
||||
|
||||
const currentWorkspace = () => {
|
||||
const ws = workspaces()?.find((w) => w.id === params.id)
|
||||
return ws ? ws.name : "Select workspace"
|
||||
}
|
||||
|
||||
const handleWorkspaceNew = () => {
|
||||
setStore({ showForm: true, showDropdown: false })
|
||||
}
|
||||
|
||||
const handleSelectWorkspace = (workspaceID: string) => {
|
||||
if (workspaceID === params.id) {
|
||||
setStore("showDropdown", false)
|
||||
return
|
||||
}
|
||||
|
||||
window.location.href = `/workspace/${workspaceID}`
|
||||
}
|
||||
|
||||
// Reset signals when workspace ID changes
|
||||
createEffect(() => {
|
||||
params.id
|
||||
setStore("showForm", false)
|
||||
setStore("showDropdown", false)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (dropdownRef && !dropdownRef.contains(event.target as Node)) {
|
||||
setStore("showDropdown", false)
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("click", handleClickOutside)
|
||||
onCleanup(() => document.removeEventListener("click", handleClickOutside))
|
||||
})
|
||||
|
||||
return (
|
||||
<div data-component="workspace-picker">
|
||||
<div ref={dropdownRef}>
|
||||
<div data-slot="trigger" onClick={() => setStore("showDropdown", !store.showDropdown)}>
|
||||
<span>{currentWorkspace()}</span>
|
||||
<svg data-slot="chevron" width="12" height="8" viewBox="0 0 12 8" fill="none">
|
||||
<path
|
||||
d="M1 1L6 6L11 1"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<Show when={store.showDropdown}>
|
||||
<div data-slot="dropdown">
|
||||
<For each={workspaces()}>
|
||||
{(workspace) => (
|
||||
<button
|
||||
data-slot="option"
|
||||
data-selected={workspace.id === params.id}
|
||||
type="button"
|
||||
onClick={() => handleSelectWorkspace(workspace.id)}
|
||||
>
|
||||
{workspace.name || workspace.slug}
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
<button data-slot="create-option" type="button" onClick={() => handleWorkspaceNew()}>
|
||||
+ Create New Workspace
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<Show when={store.showForm}>
|
||||
<form data-slot="create-form" action={createWorkspace} method="post">
|
||||
<div data-slot="create-input-group">
|
||||
<input
|
||||
data-slot="create-input"
|
||||
type="text"
|
||||
name="workspaceName"
|
||||
placeholder="Enter workspace name"
|
||||
required
|
||||
autofocus
|
||||
/>
|
||||
<button type="submit" data-color="primary">
|
||||
Create
|
||||
</button>
|
||||
<button type="button" onClick={() => setStore("showForm", false)}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Show } from "solid-js"
|
||||
import { getRequestEvent } from "solid-js/web"
|
||||
import { query, action, redirect, createAsync, RouteSectionProps, useParams, A } from "@solidjs/router"
|
||||
import "./workspace.css"
|
||||
import { useAuthSession } from "~/context/auth.session"
|
||||
import { IconLogo } from "../component/icon"
|
||||
import { WorkspacePicker } from "./workspace-picker"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { query, action, redirect, createAsync, RouteSectionProps, useParams, A } from "@solidjs/router"
|
||||
import { User } from "@opencode-ai/console-core/user.js"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { getRequestEvent } from "solid-js/web"
|
||||
import { beta } from "~/lib/beta"
|
||||
|
||||
const getUserInfo = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
@@ -35,6 +38,7 @@ const logout = action(async () => {
|
||||
export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||
const params = useParams()
|
||||
const userInfo = createAsync(() => getUserInfo(params.id))
|
||||
const isBeta = createAsync(() => beta(params.id))
|
||||
return (
|
||||
<main data-page="workspace">
|
||||
<header data-component="workspace-header">
|
||||
@@ -44,6 +48,9 @@ export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||
</A>
|
||||
</div>
|
||||
<div data-slot="header-actions">
|
||||
<Show when={isBeta()}>
|
||||
<WorkspacePicker />
|
||||
</Show>
|
||||
<span data-slot="user">{userInfo()?.email}</span>
|
||||
<form action={logout} method="post">
|
||||
<button type="submit" formaction={logout}>
|
||||
|
||||
@@ -6,28 +6,30 @@ import { PaymentSection } from "./payment-section"
|
||||
import { UsageSection } from "./usage-section"
|
||||
import { KeySection } from "./key-section"
|
||||
import { MemberSection } from "./member-section"
|
||||
import { SettingsSection } from "./settings-section"
|
||||
import { Show } from "solid-js"
|
||||
import { createAsync, query, useParams } from "@solidjs/router"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { User } from "@opencode-ai/console-core/user.js"
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import { beta } from "~/lib/beta"
|
||||
|
||||
const getUser = query(async (workspaceID: string) => {
|
||||
const getUserInfo = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
return withActor(async () => {
|
||||
const actor = Actor.assert("user")
|
||||
const user = await User.fromID(actor.properties.userID)
|
||||
return {
|
||||
isAdmin: user?.role === "admin",
|
||||
isBeta: Resource.App.stage === "production" ? workspaceID === "wrk_01K46JDFR0E75SG2Q8K172KF3Y" : true,
|
||||
}
|
||||
}, workspaceID)
|
||||
}, "user.get")
|
||||
|
||||
export default function () {
|
||||
const params = useParams()
|
||||
const data = createAsync(() => getUser(params.id))
|
||||
const userInfo = createAsync(() => getUserInfo(params.id))
|
||||
const isBeta = createAsync(() => beta(params.id))
|
||||
|
||||
return (
|
||||
<div data-page="workspace-[id]">
|
||||
<section data-component="title-section">
|
||||
@@ -44,15 +46,16 @@ export default function () {
|
||||
<div data-slot="sections">
|
||||
<NewUserSection />
|
||||
<KeySection />
|
||||
<Show when={data()?.isAdmin}>
|
||||
<Show when={data()?.isBeta}>
|
||||
<Show when={userInfo()?.isAdmin}>
|
||||
<Show when={isBeta()}>
|
||||
<SettingsSection />
|
||||
<MemberSection />
|
||||
</Show>
|
||||
<BillingSection />
|
||||
<MonthlyLimitSection />
|
||||
</Show>
|
||||
<UsageSection />
|
||||
<Show when={data()?.isAdmin}>
|
||||
<Show when={userInfo()?.isAdmin}>
|
||||
<PaymentSection />
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
@@ -56,25 +56,30 @@ const removeMember = action(async (form: FormData) => {
|
||||
)
|
||||
}, "member.remove")
|
||||
|
||||
const updateMemberRole = action(async (form: FormData) => {
|
||||
const updateMember = action(async (form: FormData) => {
|
||||
"use server"
|
||||
|
||||
const id = form.get("id")?.toString()
|
||||
if (!id) return { error: "ID is required" }
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
const role = form.get("role")?.toString() as (typeof UserRole)[number]
|
||||
if (!role) return { error: "Role is required" }
|
||||
const limit = form.get("limit")?.toString()
|
||||
const monthlyLimit = limit && limit.trim() !== "" ? parseInt(limit) : null
|
||||
if (monthlyLimit !== null && monthlyLimit < 0) return { error: "Set a valid monthly limit" }
|
||||
|
||||
return json(
|
||||
await withActor(
|
||||
() =>
|
||||
User.updateRole({ id, role })
|
||||
User.update({ id, role, monthlyLimit })
|
||||
.then((data) => ({ error: undefined, data }))
|
||||
.catch((e) => ({ error: e.message as string })),
|
||||
workspaceID,
|
||||
),
|
||||
{ revalidate: listMembers.key },
|
||||
)
|
||||
}, "member.updateRole")
|
||||
}, "member.update")
|
||||
|
||||
export function MemberCreateForm() {
|
||||
const params = useParams()
|
||||
@@ -155,7 +160,7 @@ export function MemberCreateForm() {
|
||||
|
||||
function MemberRow(props: { member: any; workspaceID: string; currentUserID: string | null }) {
|
||||
const [editing, setEditing] = createSignal(false)
|
||||
const submission = useSubmission(updateMemberRole)
|
||||
const submission = useSubmission(updateMember)
|
||||
const isCurrentUser = () => props.currentUserID === props.member.id
|
||||
|
||||
createEffect(() => {
|
||||
@@ -164,6 +169,29 @@ function MemberRow(props: { member: any; workspaceID: string; currentUserID: str
|
||||
}
|
||||
})
|
||||
|
||||
function getUsageDisplay() {
|
||||
const currentUsage = (() => {
|
||||
const dateLastUsed = props.member.timeMonthlyUsageUpdated
|
||||
if (!dateLastUsed) return 0
|
||||
|
||||
const current = new Date().toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
timeZone: "UTC",
|
||||
})
|
||||
const lastUsed = dateLastUsed.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
timeZone: "UTC",
|
||||
})
|
||||
if (current !== lastUsed) return 0
|
||||
return ((props.member.monthlyUsage ?? 0) / 100000000).toFixed(2)
|
||||
})()
|
||||
|
||||
const limit = props.member.monthlyLimit ? `$${props.member.monthlyLimit}` : "no limit"
|
||||
return `$${currentUsage} / ${limit}`
|
||||
}
|
||||
|
||||
return (
|
||||
<Show
|
||||
when={editing()}
|
||||
@@ -171,6 +199,7 @@ function MemberRow(props: { member: any; workspaceID: string; currentUserID: str
|
||||
<tr>
|
||||
<td data-slot="member-email">{props.member.accountEmail ?? props.member.email}</td>
|
||||
<td data-slot="member-role">{props.member.role}</td>
|
||||
<td data-slot="member-usage">{getUsageDisplay()}</td>
|
||||
<Show when={!props.member.timeSeen} fallback={<td data-slot="member-joined"></td>}>
|
||||
<td data-slot="member-joined">invited</td>
|
||||
</Show>
|
||||
@@ -190,12 +219,21 @@ function MemberRow(props: { member: any; workspaceID: string; currentUserID: str
|
||||
}
|
||||
>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<form action={updateMemberRole} method="post">
|
||||
<td colspan="5">
|
||||
<form action={updateMember} method="post">
|
||||
<div data-slot="edit-member-email">{props.member.accountEmail ?? props.member.email}</div>
|
||||
<input type="hidden" name="id" value={props.member.id} />
|
||||
<input type="hidden" name="workspaceID" value={props.workspaceID} />
|
||||
<Show when={!isCurrentUser()} fallback={<div data-slot="current-user-role">Role: {props.member.role}</div>}>
|
||||
|
||||
<Show
|
||||
when={!isCurrentUser()}
|
||||
fallback={
|
||||
<>
|
||||
<div data-slot="current-user-role">Role: {props.member.role}</div>
|
||||
<input type="hidden" name="role" value={props.member.role} />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div data-slot="role-selector">
|
||||
<label>
|
||||
<input type="radio" name="role" value="admin" checked={props.member.role === "admin"} />
|
||||
@@ -213,18 +251,32 @@ function MemberRow(props: { member: any; workspaceID: string; currentUserID: str
|
||||
</label>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div data-slot="limit-selector">
|
||||
<label>
|
||||
<strong>Monthly Limit</strong>
|
||||
<input
|
||||
type="number"
|
||||
name="limit"
|
||||
value={props.member.monthlyLimit ?? ""}
|
||||
placeholder="No limit"
|
||||
min="0"
|
||||
/>
|
||||
<p>Set a monthly spending limit for this user</p>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Show when={submission.result && submission.result.error}>
|
||||
{(err) => <div data-slot="form-error">{err()}</div>}
|
||||
</Show>
|
||||
|
||||
<div data-slot="form-actions">
|
||||
<button type="button" data-color="ghost" onClick={() => setEditing(false)}>
|
||||
Cancel
|
||||
</button>
|
||||
<Show when={!isCurrentUser()}>
|
||||
<button type="submit" data-color="primary" disabled={submission.pending}>
|
||||
{submission.pending ? "Saving..." : "Save"}
|
||||
</button>
|
||||
</Show>
|
||||
<button type="submit" data-color="primary" disabled={submission.pending}>
|
||||
{submission.pending ? "Saving..." : "Save"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
@@ -258,6 +310,7 @@ export function MemberSection() {
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Usage</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
.root {
|
||||
[data-slot="section-content"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
[data-slot="setting"] {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="setting-info"] {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
|
||||
h3 {
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
[data-slot="current-value"] {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-muted);
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="create-form"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
min-width: 15rem;
|
||||
width: fit-content;
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
[data-slot="input-container"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
font-family: var(--font-mono);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="form-actions"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
[data-slot="form-error"] {
|
||||
color: var(--color-danger);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { json, action, useParams, useSubmission, createAsync, query } from "@solidjs/router"
|
||||
import { createEffect, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { Workspace } from "@opencode-ai/console-core/workspace.js"
|
||||
import styles from "./settings-section.module.css"
|
||||
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
|
||||
const getWorkspaceInfo = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
return withActor(
|
||||
() =>
|
||||
Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
id: WorkspaceTable.id,
|
||||
name: WorkspaceTable.name,
|
||||
slug: WorkspaceTable.slug,
|
||||
})
|
||||
.from(WorkspaceTable)
|
||||
.where(eq(WorkspaceTable.id, workspaceID))
|
||||
.then((rows) => rows[0] || null),
|
||||
),
|
||||
workspaceID,
|
||||
)
|
||||
}, "workspace.get")
|
||||
|
||||
const updateWorkspace = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const name = form.get("name")?.toString().trim()
|
||||
if (!name) return { error: "Workspace name is required." }
|
||||
if (name.length > 255) return { error: "Name must be 255 characters or less." }
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required." }
|
||||
return json(
|
||||
await withActor(
|
||||
() =>
|
||||
Workspace.update({ name })
|
||||
.then(() => ({ error: undefined }))
|
||||
.catch((e) => ({ error: e.message as string })),
|
||||
workspaceID,
|
||||
),
|
||||
)
|
||||
}, "workspace.update")
|
||||
|
||||
export function SettingsSection() {
|
||||
const params = useParams()
|
||||
const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id))
|
||||
const submission = useSubmission(updateWorkspace)
|
||||
const [store, setStore] = createStore({ show: false })
|
||||
|
||||
let input: HTMLInputElement
|
||||
|
||||
createEffect(() => {
|
||||
if (!submission.pending && submission.result && !submission.result.error) {
|
||||
hide()
|
||||
}
|
||||
})
|
||||
|
||||
function show() {
|
||||
while (true) {
|
||||
submission.clear()
|
||||
if (!submission.result) break
|
||||
}
|
||||
setStore("show", true)
|
||||
input.focus()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
setStore("show", false)
|
||||
}
|
||||
|
||||
return (
|
||||
<section class={styles.root}>
|
||||
<div data-slot="section-title">
|
||||
<h2>Settings</h2>
|
||||
<p>Update your workspace name and preferences.</p>
|
||||
</div>
|
||||
<div data-slot="section-content">
|
||||
<div data-slot="setting">
|
||||
<div data-slot="setting-info">
|
||||
<h3>Workspace Name</h3>
|
||||
<p data-slot="current-value">{workspaceInfo()?.name}</p>
|
||||
</div>
|
||||
<Show
|
||||
when={!store.show}
|
||||
fallback={
|
||||
<form action={updateWorkspace} method="post" data-slot="create-form">
|
||||
<div data-slot="input-container">
|
||||
<input
|
||||
required
|
||||
ref={(r) => (input = r)}
|
||||
data-component="input"
|
||||
name="name"
|
||||
type="text"
|
||||
placeholder="Workspace name"
|
||||
value={workspaceInfo()?.name ?? "Default"}
|
||||
/>
|
||||
<Show when={submission.result && submission.result.error}>
|
||||
{(err) => <div data-slot="form-error">{err()}</div>}
|
||||
</Show>
|
||||
</div>
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<div data-slot="form-actions">
|
||||
<button type="reset" data-color="ghost" onClick={() => hide()}>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" data-color="primary" disabled={submission.pending}>
|
||||
{submission.pending ? "Updating..." : "Update"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
>
|
||||
<button data-color="primary" onClick={() => show()}>
|
||||
Edit Name
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { Billing } from "../../../../core/src/billing"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { ZenModel } from "@opencode-ai/console-core/model.js"
|
||||
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||
|
||||
export async function handler(
|
||||
input: APIEvent,
|
||||
@@ -33,6 +34,7 @@ export async function handler(
|
||||
class AuthError extends Error {}
|
||||
class CreditsError extends Error {}
|
||||
class MonthlyLimitError extends Error {}
|
||||
class UserLimitError extends Error {}
|
||||
class ModelError extends Error {}
|
||||
|
||||
type Model = z.infer<typeof ZenModel.ModelSchema>
|
||||
@@ -181,6 +183,7 @@ export async function handler(
|
||||
error instanceof AuthError ||
|
||||
error instanceof CreditsError ||
|
||||
error instanceof MonthlyLimitError ||
|
||||
error instanceof UserLimitError ||
|
||||
error instanceof ModelError
|
||||
)
|
||||
return new Response(
|
||||
@@ -243,10 +246,15 @@ export async function handler(
|
||||
monthlyLimit: BillingTable.monthlyLimit,
|
||||
monthlyUsage: BillingTable.monthlyUsage,
|
||||
timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated,
|
||||
userID: UserTable.id,
|
||||
userMonthlyLimit: UserTable.monthlyLimit,
|
||||
userMonthlyUsage: UserTable.monthlyUsage,
|
||||
timeUserMonthlyUsageUpdated: UserTable.timeMonthlyUsageUpdated,
|
||||
})
|
||||
.from(KeyTable)
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
|
||||
.innerJoin(BillingTable, eq(BillingTable.workspaceID, KeyTable.workspaceID))
|
||||
.innerJoin(UserTable, and(eq(UserTable.workspaceID, KeyTable.workspaceID), eq(UserTable.id, KeyTable.userID)))
|
||||
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
@@ -269,6 +277,12 @@ export async function handler(
|
||||
monthlyUsage: data.monthlyUsage,
|
||||
timeMonthlyUsageUpdated: data.timeMonthlyUsageUpdated,
|
||||
},
|
||||
user: {
|
||||
id: data.userID,
|
||||
monthlyLimit: data.userMonthlyLimit,
|
||||
monthlyUsage: data.userMonthlyUsage,
|
||||
timeMonthlyUsageUpdated: data.timeUserMonthlyUsageUpdated,
|
||||
},
|
||||
isFree,
|
||||
}
|
||||
}
|
||||
@@ -280,19 +294,34 @@ export async function handler(
|
||||
const billing = authInfo.billing
|
||||
if (!billing.paymentMethodID) throw new CreditsError("No payment method")
|
||||
if (billing.balance <= 0) throw new CreditsError("Insufficient balance")
|
||||
|
||||
const now = new Date()
|
||||
const currentYear = now.getUTCFullYear()
|
||||
const currentMonth = now.getUTCMonth()
|
||||
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}.`)
|
||||
throw new MonthlyLimitError(
|
||||
`Your workspace has reached its monthly spending limit of $${billing.monthlyLimit}.`,
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
authInfo.user.monthlyLimit &&
|
||||
authInfo.user.monthlyUsage &&
|
||||
authInfo.user.timeMonthlyUsageUpdated &&
|
||||
authInfo.user.monthlyUsage >= centsToMicroCents(authInfo.user.monthlyLimit * 100)
|
||||
) {
|
||||
const dateYear = authInfo.user.timeMonthlyUsageUpdated.getUTCFullYear()
|
||||
const dateMonth = authInfo.user.timeMonthlyUsageUpdated.getUTCMonth()
|
||||
if (currentYear === dateYear && currentMonth === dateMonth)
|
||||
throw new UserLimitError(`You have reached your monthly spending limit of $${authInfo.user.monthlyLimit}.`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,6 +415,18 @@ export async function handler(
|
||||
timeMonthlyUsageUpdated: sql`now()`,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, authInfo.workspaceID))
|
||||
await tx
|
||||
.update(UserTable)
|
||||
.set({
|
||||
monthlyUsage: sql`
|
||||
CASE
|
||||
WHEN MONTH(${UserTable.timeMonthlyUsageUpdated}) = MONTH(now()) AND YEAR(${UserTable.timeMonthlyUsageUpdated}) = YEAR(now()) THEN ${UserTable.monthlyUsage} + ${cost}
|
||||
ELSE ${cost}
|
||||
END
|
||||
`,
|
||||
timeMonthlyUsageUpdated: sql`now()`,
|
||||
})
|
||||
.where(and(eq(UserTable.workspaceID, authInfo.workspaceID), eq(UserTable.id, authInfo.user.id)))
|
||||
})
|
||||
|
||||
await Database.use((tx) =>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `workspace` MODIFY COLUMN `name` varchar(255) NOT NULL;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `user` ADD `monthly_limit` int;--> statement-breakpoint
|
||||
ALTER TABLE `user` ADD `monthly_usage` bigint;--> statement-breakpoint
|
||||
ALTER TABLE `user` ADD `time_monthly_usage_updated` timestamp(3);
|
||||
@@ -0,0 +1,709 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "a331e38c-c2e3-406d-a1ff-b0af7229cd85",
|
||||
"prevId": "05e873f6-1556-4bcb-8e19-14971e37610a",
|
||||
"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
|
||||
},
|
||||
"invoice_id": {
|
||||
"name": "invoice_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_refunded": {
|
||||
"name": "time_refunded",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"usage": {
|
||||
"name": "usage",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reasoning_tokens": {
|
||||
"name": "reasoning_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_read_tokens": {
|
||||
"name": "cache_read_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_5m_tokens": {
|
||||
"name": "cache_write_5m_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_1h_tokens": {
|
||||
"name": "cache_write_1h_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cost": {
|
||||
"name": "cost",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_used": {
|
||||
"name": "time_used",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "enum('admin','member')",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"workspace": {
|
||||
"name": "workspace",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,730 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "33551b4c-fc2e-4753-8d9d-0971f333e65d",
|
||||
"prevId": "a331e38c-c2e3-406d-a1ff-b0af7229cd85",
|
||||
"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
|
||||
},
|
||||
"invoice_id": {
|
||||
"name": "invoice_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_refunded": {
|
||||
"name": "time_refunded",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"usage": {
|
||||
"name": "usage",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reasoning_tokens": {
|
||||
"name": "reasoning_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_read_tokens": {
|
||||
"name": "cache_read_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_5m_tokens": {
|
||||
"name": "cache_write_5m_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_1h_tokens": {
|
||||
"name": "cache_write_1h_tokens",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cost": {
|
||||
"name": "cost",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_used": {
|
||||
"name": "time_used",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "enum('admin','member')",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_limit": {
|
||||
"name": "monthly_limit",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_usage": {
|
||||
"name": "monthly_usage",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_monthly_usage_updated": {
|
||||
"name": "time_monthly_usage_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"workspace": {
|
||||
"name": "workspace",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -197,6 +197,20 @@
|
||||
"when": 1759553466608,
|
||||
"tag": "0027_hot_wong",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 28,
|
||||
"version": "5",
|
||||
"when": 1759805025276,
|
||||
"tag": "0028_careful_cerise",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 29,
|
||||
"version": "5",
|
||||
"when": 1759811835558,
|
||||
"tag": "0029_panoramic_harrier",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { z } from "zod"
|
||||
import { and, eq, getTableColumns, isNull } from "drizzle-orm"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { fn } from "./util/fn"
|
||||
import { Database } from "./drizzle"
|
||||
import { Identifier } from "./identifier"
|
||||
import { AccountTable } from "./schema/account.sql"
|
||||
import { Actor } from "./actor"
|
||||
import { WorkspaceTable } from "./schema/workspace.sql"
|
||||
import { UserTable } from "./schema/user.sql"
|
||||
|
||||
export namespace Account {
|
||||
export const create = fn(
|
||||
@@ -46,16 +43,4 @@ export namespace Account {
|
||||
.then((rows) => rows[0])
|
||||
}),
|
||||
)
|
||||
|
||||
export const workspaces = async () => {
|
||||
const actor = Actor.assert("account")
|
||||
return Database.transaction(async (tx) =>
|
||||
tx
|
||||
.select(getTableColumns(WorkspaceTable))
|
||||
.from(WorkspaceTable)
|
||||
.innerJoin(UserTable, eq(UserTable.workspaceID, WorkspaceTable.id))
|
||||
.where(and(eq(UserTable.accountID, actor.properties.accountID), isNull(WorkspaceTable.timeDeleted)))
|
||||
.execute(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export namespace Actor {
|
||||
properties: {
|
||||
userID: string
|
||||
workspaceID: string
|
||||
accountID: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +72,12 @@ export namespace Actor {
|
||||
}
|
||||
throw new Error(`actor of type "${actor.type}" is not associated with a workspace`)
|
||||
}
|
||||
|
||||
export function account() {
|
||||
const actor = use()
|
||||
if ("accountID" in actor.properties) {
|
||||
return actor.properties.accountID
|
||||
}
|
||||
throw new Error(`actor of type "${actor.type}" is not associated with an account`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Stripe } from "stripe"
|
||||
import { and, Database, eq, sql } from "./drizzle"
|
||||
import { Database, eq, sql } from "./drizzle"
|
||||
import { BillingTable, PaymentTable, UsageTable } from "./schema/billing.sql"
|
||||
import { Actor } from "./actor"
|
||||
import { fn } from "./util/fn"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mysqlTable, uniqueIndex, varchar, int, mysqlEnum, index } from "drizzle-orm/mysql-core"
|
||||
import { mysqlTable, uniqueIndex, varchar, int, mysqlEnum, index, bigint } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
|
||||
import { workspaceIndexes } from "./workspace.sql"
|
||||
|
||||
@@ -15,6 +15,9 @@ export const UserTable = mysqlTable(
|
||||
timeSeen: utc("time_seen"),
|
||||
color: int("color"),
|
||||
role: mysqlEnum("role", UserRole).notNull(),
|
||||
monthlyLimit: int("monthly_limit"),
|
||||
monthlyUsage: bigint("monthly_usage", { mode: "number" }),
|
||||
timeMonthlyUsageUpdated: utc("time_monthly_usage_updated"),
|
||||
},
|
||||
(table) => [
|
||||
...workspaceIndexes(table),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { primaryKey, mysqlTable, uniqueIndex, varchar, boolean } from "drizzle-orm/mysql-core"
|
||||
import { primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, ulid } from "../drizzle/types"
|
||||
|
||||
export const WorkspaceTable = mysqlTable(
|
||||
@@ -6,7 +6,7 @@ export const WorkspaceTable = mysqlTable(
|
||||
{
|
||||
id: ulid("id").notNull().primaryKey(),
|
||||
slug: varchar("slug", { length: 255 }),
|
||||
name: varchar("name", { length: 255 }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
...timestamps,
|
||||
},
|
||||
(table) => [uniqueIndex("slug").on(table.slug)],
|
||||
|
||||
@@ -172,22 +172,21 @@ export namespace User {
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return invitations.length
|
||||
})
|
||||
|
||||
export const updateRole = fn(
|
||||
export const update = fn(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
role: z.enum(UserRole),
|
||||
monthlyLimit: z.number().nullable(),
|
||||
}),
|
||||
async ({ id, role }) => {
|
||||
async ({ id, role, monthlyLimit }) => {
|
||||
await assertAdmin()
|
||||
if (role === "member") assertNotSelf(id)
|
||||
return await Database.use((tx) =>
|
||||
tx
|
||||
.update(UserTable)
|
||||
.set({ role })
|
||||
.set({ role, monthlyLimit })
|
||||
.where(and(eq(UserTable.id, id), eq(UserTable.workspaceID, Actor.workspace()))),
|
||||
)
|
||||
},
|
||||
|
||||
@@ -7,36 +7,69 @@ import { UserTable } from "./schema/user.sql"
|
||||
import { BillingTable } from "./schema/billing.sql"
|
||||
import { WorkspaceTable } from "./schema/workspace.sql"
|
||||
import { Key } from "./key"
|
||||
import { eq, sql } from "drizzle-orm"
|
||||
|
||||
export namespace Workspace {
|
||||
export const create = fn(z.void(), async () => {
|
||||
const account = Actor.assert("account")
|
||||
const workspaceID = Identifier.create("workspace")
|
||||
const userID = Identifier.create("user")
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx.insert(WorkspaceTable).values({
|
||||
id: workspaceID,
|
||||
export const create = fn(
|
||||
z.object({
|
||||
name: z.string().min(1),
|
||||
}),
|
||||
async ({ name }) => {
|
||||
const account = Actor.assert("account")
|
||||
const workspaceID = Identifier.create("workspace")
|
||||
const userID = Identifier.create("user")
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx.insert(WorkspaceTable).values({
|
||||
id: workspaceID,
|
||||
name,
|
||||
})
|
||||
await tx.insert(UserTable).values({
|
||||
workspaceID,
|
||||
id: userID,
|
||||
accountID: account.properties.accountID,
|
||||
name: "",
|
||||
role: "admin",
|
||||
})
|
||||
await tx.insert(BillingTable).values({
|
||||
workspaceID,
|
||||
id: Identifier.create("billing"),
|
||||
balance: 0,
|
||||
})
|
||||
})
|
||||
await tx.insert(UserTable).values({
|
||||
workspaceID,
|
||||
id: userID,
|
||||
accountID: account.properties.accountID,
|
||||
name: "",
|
||||
role: "admin",
|
||||
})
|
||||
await tx.insert(BillingTable).values({
|
||||
workspaceID,
|
||||
id: Identifier.create("billing"),
|
||||
balance: 0,
|
||||
})
|
||||
})
|
||||
await Actor.provide(
|
||||
"system",
|
||||
{
|
||||
workspaceID,
|
||||
},
|
||||
() => Key.create({ userID, name: "Default API Key" }),
|
||||
await Actor.provide(
|
||||
"system",
|
||||
{
|
||||
workspaceID,
|
||||
},
|
||||
() => Key.create({ userID, name: "Default API Key" }),
|
||||
)
|
||||
return workspaceID
|
||||
},
|
||||
)
|
||||
|
||||
export const update = fn(
|
||||
z.object({
|
||||
name: z.string().min(1).max(255),
|
||||
}),
|
||||
async ({ name }) => {
|
||||
const workspaceID = Actor.workspace()
|
||||
return await Database.use((tx) =>
|
||||
tx
|
||||
.update(WorkspaceTable)
|
||||
.set({
|
||||
name,
|
||||
})
|
||||
.where(eq(WorkspaceTable.id, workspaceID)),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export const remove = fn(z.void(), async () => {
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(WorkspaceTable)
|
||||
.set({ timeDeleted: sql`now()` })
|
||||
.where(eq(WorkspaceTable.id, Actor.workspace())),
|
||||
)
|
||||
return workspaceID
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -11,6 +11,9 @@ import { Workspace } from "@opencode-ai/console-core/workspace.js"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import { User } from "@opencode-ai/console-core/user.js"
|
||||
import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||
|
||||
type Env = {
|
||||
AuthStorage: KVNamespace
|
||||
@@ -123,9 +126,22 @@ export default {
|
||||
})
|
||||
}
|
||||
await Actor.provide("account", { accountID, email }, async () => {
|
||||
const workspaceCount = await User.joinInvitedWorkspaces()
|
||||
if (workspaceCount === 0) {
|
||||
await Workspace.create()
|
||||
await User.joinInvitedWorkspaces()
|
||||
const workspaces = await Database.transaction(async (tx) =>
|
||||
tx
|
||||
.select({ id: WorkspaceTable.id })
|
||||
.from(WorkspaceTable)
|
||||
.innerJoin(UserTable, eq(UserTable.workspaceID, WorkspaceTable.id))
|
||||
.where(
|
||||
and(
|
||||
eq(UserTable.accountID, accountID),
|
||||
isNull(UserTable.timeDeleted),
|
||||
isNull(WorkspaceTable.timeDeleted),
|
||||
),
|
||||
),
|
||||
)
|
||||
if (workspaces.length === 0) {
|
||||
await Workspace.create({ name: "Default" })
|
||||
}
|
||||
})
|
||||
return ctx.subject("account", accountID, { accountID, email })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-scripts",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"shell": "sst shell -- bun tsx",
|
||||
"shell-dev": "sst shell --stage dev -- bun tsx",
|
||||
"shell-prod": "sst shell --stage production -- bun tsx"
|
||||
"shell": "sst shell -- bun",
|
||||
"shell-dev": "sst shell --stage dev -- bun",
|
||||
"shell-prod": "sst shell --stage production -- bun"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
"tsx": "4.20.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"@opencode-ai/console-core": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
|
||||
@@ -106,7 +106,7 @@ export const RunCommand = cmd({
|
||||
|
||||
if (args.session) return Session.get(args.session)
|
||||
|
||||
return Session.create()
|
||||
return Session.create({})
|
||||
})()
|
||||
|
||||
if (!session) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import z from "zod/v4"
|
||||
import { Session } from "../session"
|
||||
import { Bus } from "../bus"
|
||||
import { Instance } from "../project/instance"
|
||||
import { withTimeout } from "@/util/timeout"
|
||||
|
||||
export namespace MCP {
|
||||
const log = Log.create({ service: "mcp" })
|
||||
@@ -20,11 +21,13 @@ export namespace MCP {
|
||||
}),
|
||||
)
|
||||
|
||||
type MCPClient = Awaited<ReturnType<typeof experimental_createMCPClient>>
|
||||
|
||||
const state = Instance.state(
|
||||
async () => {
|
||||
const cfg = await Config.get()
|
||||
const clients: {
|
||||
[name: string]: Awaited<ReturnType<typeof experimental_createMCPClient>>
|
||||
[name: string]: MCPClient
|
||||
} = {}
|
||||
for (const [key, mcp] of Object.entries(cfg.mcp ?? {})) {
|
||||
if (mcp.enabled === false) {
|
||||
@@ -128,8 +131,17 @@ export namespace MCP {
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, client] of Object.entries(clients)) {
|
||||
const result = await withTimeout(client.tools(), 5000).catch(() => {})
|
||||
if (!result) {
|
||||
log.warn("mcp client verification failed, removing client", { key })
|
||||
delete clients[key]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
clients,
|
||||
config: cfg.mcp ?? {},
|
||||
}
|
||||
},
|
||||
async (state) => {
|
||||
@@ -139,6 +151,23 @@ export namespace MCP {
|
||||
},
|
||||
)
|
||||
|
||||
export async function status() {
|
||||
return state().then((state) => {
|
||||
const result: Record<string, "connected" | "failed" | "disabled"> = {}
|
||||
for (const [key, client] of Object.entries(state.config)) {
|
||||
if (client.enabled === false) {
|
||||
result[key] = "disabled"
|
||||
continue
|
||||
}
|
||||
if (state.clients[key]) {
|
||||
result[key] = "connected"
|
||||
}
|
||||
result[key] = "failed"
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
export async function clients() {
|
||||
return state().then((state) => state.clients)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ import { SessionPrompt } from "../session/prompt"
|
||||
import { SessionCompaction } from "../session/compaction"
|
||||
import { SessionRevert } from "../session/revert"
|
||||
import { lazy } from "../util/lazy"
|
||||
import { Todo } from "../session/todo"
|
||||
import { InstanceBootstrap } from "../project/bootstrap"
|
||||
import { MCP } from "../mcp"
|
||||
|
||||
const ERRORS = {
|
||||
400: {
|
||||
@@ -306,7 +308,7 @@ export namespace Server {
|
||||
validator(
|
||||
"param",
|
||||
z.object({
|
||||
id: z.string(),
|
||||
id: Session.get.schema,
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
@@ -334,7 +336,7 @@ export namespace Server {
|
||||
validator(
|
||||
"param",
|
||||
z.object({
|
||||
id: z.string(),
|
||||
id: Session.children.schema,
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
@@ -343,6 +345,34 @@ export namespace Server {
|
||||
return c.json(session)
|
||||
},
|
||||
)
|
||||
.get(
|
||||
"/session/:id/todo",
|
||||
describeRoute({
|
||||
description: "Get the todo list for a session",
|
||||
operationId: "session.todo",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Todo list",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(Todo.Info.array()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
validator(
|
||||
"param",
|
||||
z.object({
|
||||
id: z.string().meta({ description: "Session ID" }),
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
const sessionID = c.req.valid("param").id
|
||||
const todos = await Todo.get(sessionID)
|
||||
return c.json(todos)
|
||||
},
|
||||
)
|
||||
.post(
|
||||
"/session",
|
||||
describeRoute({
|
||||
@@ -360,18 +390,10 @@ export namespace Server {
|
||||
},
|
||||
},
|
||||
}),
|
||||
validator(
|
||||
"json",
|
||||
z
|
||||
.object({
|
||||
parentID: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
),
|
||||
validator("json", Session.create.schema.optional()),
|
||||
async (c) => {
|
||||
const body = c.req.valid("json") ?? {}
|
||||
const session = await Session.create(body.parentID, body.title)
|
||||
const session = await Session.create(body)
|
||||
return c.json(session)
|
||||
},
|
||||
)
|
||||
@@ -394,7 +416,7 @@ export namespace Server {
|
||||
validator(
|
||||
"param",
|
||||
z.object({
|
||||
id: z.string(),
|
||||
id: Session.remove.schema,
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
@@ -465,14 +487,7 @@ export namespace Server {
|
||||
id: z.string().meta({ description: "Session ID" }),
|
||||
}),
|
||||
),
|
||||
validator(
|
||||
"json",
|
||||
z.object({
|
||||
messageID: z.string(),
|
||||
providerID: z.string(),
|
||||
modelID: z.string(),
|
||||
}),
|
||||
),
|
||||
validator("json", Session.initialize.schema.omit({ sessionID: true })),
|
||||
async (c) => {
|
||||
const sessionID = c.req.valid("param").id
|
||||
const body = c.req.valid("json")
|
||||
@@ -480,6 +495,36 @@ export namespace Server {
|
||||
return c.json(true)
|
||||
},
|
||||
)
|
||||
.post(
|
||||
"/session/:id/fork",
|
||||
describeRoute({
|
||||
description: "Fork an existing session at a specific message",
|
||||
operationId: "session.fork",
|
||||
responses: {
|
||||
200: {
|
||||
description: "200",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(Session.Info),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
validator(
|
||||
"param",
|
||||
z.object({
|
||||
id: Session.fork.schema.shape.sessionID,
|
||||
}),
|
||||
),
|
||||
validator("json", Session.fork.schema.omit({ sessionID: true })),
|
||||
async (c) => {
|
||||
const sessionID = c.req.valid("param").id
|
||||
const body = c.req.valid("json")
|
||||
const result = await Session.fork({ ...body, sessionID })
|
||||
return c.json(result)
|
||||
},
|
||||
)
|
||||
.post(
|
||||
"/session/:id/abort",
|
||||
describeRoute({
|
||||
@@ -554,7 +599,7 @@ export namespace Server {
|
||||
validator(
|
||||
"param",
|
||||
z.object({
|
||||
id: z.string(),
|
||||
id: Session.unshare.schema,
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
@@ -657,7 +702,7 @@ export namespace Server {
|
||||
),
|
||||
async (c) => {
|
||||
const params = c.req.valid("param")
|
||||
const message = await Session.getMessage(params.id, params.messageID)
|
||||
const message = await Session.getMessage({ sessionID: params.id, messageID: params.messageID })
|
||||
return c.json(message)
|
||||
},
|
||||
)
|
||||
@@ -1139,6 +1184,26 @@ export namespace Server {
|
||||
return c.json(modes)
|
||||
},
|
||||
)
|
||||
.get(
|
||||
"/mcp",
|
||||
describeRoute({
|
||||
description: "Get MCP server status",
|
||||
operationId: "mcp.status",
|
||||
responses: {
|
||||
200: {
|
||||
description: "MCP server status",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(z.any()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
return c.json(await MCP.status())
|
||||
},
|
||||
)
|
||||
.post(
|
||||
"/tui/append-prompt",
|
||||
describeRoute({
|
||||
|
||||
@@ -144,7 +144,7 @@ export namespace SessionCompaction {
|
||||
},
|
||||
],
|
||||
})
|
||||
const usage = Session.getUsage(model.info, generated.usage, generated.providerMetadata)
|
||||
const usage = Session.getUsage({ model: model.info, usage: generated.usage, metadata: generated.providerMetadata })
|
||||
msg.cost += usage.cost
|
||||
msg.tokens = usage.tokens
|
||||
msg.summary = true
|
||||
|
||||
@@ -17,6 +17,7 @@ import { MessageV2 } from "./message-v2"
|
||||
import { Project } from "../project/project"
|
||||
import { Instance } from "../project/instance"
|
||||
import { SessionPrompt } from "./prompt"
|
||||
import { fn } from "@/util/fn"
|
||||
|
||||
export namespace Session {
|
||||
const log = Log.create({ service: "session" })
|
||||
@@ -92,19 +93,58 @@ export namespace Session {
|
||||
),
|
||||
}
|
||||
|
||||
export async function create(parentID?: string, title?: string) {
|
||||
return createNext({
|
||||
parentID,
|
||||
directory: Instance.directory,
|
||||
title,
|
||||
})
|
||||
}
|
||||
export const create = fn(
|
||||
z
|
||||
.object({
|
||||
parentID: Identifier.schema("session").optional(),
|
||||
title: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
async (input) => {
|
||||
return createNext({
|
||||
parentID: input?.parentID,
|
||||
directory: Instance.directory,
|
||||
title: input?.title,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
export async function touch(sessionID: string) {
|
||||
export const fork = fn(
|
||||
z.object({
|
||||
sessionID: Identifier.schema("session"),
|
||||
messageID: Identifier.schema("message").optional(),
|
||||
}),
|
||||
async (input) => {
|
||||
const session = await createNext({
|
||||
directory: Instance.directory,
|
||||
})
|
||||
const msgs = await messages(input.sessionID)
|
||||
for (const msg of msgs) {
|
||||
if (input.messageID && msg.info.id >= input.messageID) break
|
||||
const cloned = await updateMessage({
|
||||
...msg.info,
|
||||
sessionID: session.id,
|
||||
id: Identifier.ascending("message"),
|
||||
})
|
||||
|
||||
for (const part of msg.parts) {
|
||||
await updatePart({
|
||||
...part,
|
||||
id: Identifier.ascending("part"),
|
||||
messageID: cloned.id,
|
||||
sessionID: session.id,
|
||||
})
|
||||
}
|
||||
}
|
||||
return session
|
||||
},
|
||||
)
|
||||
|
||||
export const touch = fn(Identifier.schema("session"), async (sessionID) => {
|
||||
await update(sessionID, (draft) => {
|
||||
draft.time.updated = Date.now()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export async function createNext(input: { id?: string; title?: string; parentID?: string; directory: string }) {
|
||||
const result: Info = {
|
||||
@@ -138,16 +178,16 @@ export namespace Session {
|
||||
return result
|
||||
}
|
||||
|
||||
export async function get(id: string) {
|
||||
export const get = fn(Identifier.schema("session"), async (id) => {
|
||||
const read = await Storage.read<Info>(["session", Instance.project.id, id])
|
||||
return read as Info
|
||||
}
|
||||
})
|
||||
|
||||
export async function getShare(id: string) {
|
||||
export const getShare = fn(Identifier.schema("session"), async (id) => {
|
||||
return Storage.read<ShareInfo>(["share", id])
|
||||
}
|
||||
})
|
||||
|
||||
export async function share(id: string) {
|
||||
export const share = fn(Identifier.schema("session"), async (id) => {
|
||||
const cfg = await Config.get()
|
||||
if (cfg.share === "disabled") {
|
||||
throw new Error("Sharing is disabled in configuration")
|
||||
@@ -170,9 +210,9 @@ export namespace Session {
|
||||
}
|
||||
}
|
||||
return share
|
||||
}
|
||||
})
|
||||
|
||||
export async function unshare(id: string) {
|
||||
export const unshare = fn(Identifier.schema("session"), async (id) => {
|
||||
const share = await getShare(id)
|
||||
if (!share) return
|
||||
await Storage.remove(["share", id])
|
||||
@@ -180,7 +220,7 @@ export namespace Session {
|
||||
draft.share = undefined
|
||||
})
|
||||
await Share.remove(id, share.secret)
|
||||
}
|
||||
})
|
||||
|
||||
export async function update(id: string, editor: (session: Info) => void) {
|
||||
const project = Instance.project
|
||||
@@ -194,7 +234,7 @@ export namespace Session {
|
||||
return result
|
||||
}
|
||||
|
||||
export async function messages(sessionID: string) {
|
||||
export const messages = fn(Identifier.schema("session"), async (sessionID) => {
|
||||
const result = [] as MessageV2.WithParts[]
|
||||
for (const p of await Storage.list(["message", sessionID])) {
|
||||
const read = await Storage.read<MessageV2.Info>(p)
|
||||
@@ -205,16 +245,22 @@ export namespace Session {
|
||||
}
|
||||
result.sort((a, b) => (a.info.id > b.info.id ? 1 : -1))
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
export async function getMessage(sessionID: string, messageID: string) {
|
||||
return {
|
||||
info: await Storage.read<MessageV2.Info>(["message", sessionID, messageID]),
|
||||
parts: await getParts(messageID),
|
||||
}
|
||||
}
|
||||
export const getMessage = fn(
|
||||
z.object({
|
||||
sessionID: Identifier.schema("session"),
|
||||
messageID: Identifier.schema("message"),
|
||||
}),
|
||||
async (input) => {
|
||||
return {
|
||||
info: await Storage.read<MessageV2.Info>(["message", input.sessionID, input.messageID]),
|
||||
parts: await getParts(input.messageID),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export async function getParts(messageID: string) {
|
||||
export const getParts = fn(Identifier.schema("message"), async (messageID) => {
|
||||
const result = [] as MessageV2.Part[]
|
||||
for (const item of await Storage.list(["part", messageID])) {
|
||||
const read = await Storage.read<MessageV2.Part>(item)
|
||||
@@ -222,7 +268,7 @@ export namespace Session {
|
||||
}
|
||||
result.sort((a, b) => (a.id > b.id ? 1 : -1))
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
export async function* list() {
|
||||
const project = Instance.project
|
||||
@@ -231,7 +277,7 @@ export namespace Session {
|
||||
}
|
||||
}
|
||||
|
||||
export async function children(parentID: string) {
|
||||
export const children = fn(Identifier.schema("session"), async (parentID) => {
|
||||
const project = Instance.project
|
||||
const result = [] as Session.Info[]
|
||||
for (const item of await Storage.list(["session", project.id])) {
|
||||
@@ -240,14 +286,14 @@ export namespace Session {
|
||||
result.push(session)
|
||||
}
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
export async function remove(sessionID: string, emitEvent = true) {
|
||||
export const remove = fn(Identifier.schema("session"), async (sessionID) => {
|
||||
const project = Instance.project
|
||||
try {
|
||||
const session = await get(sessionID)
|
||||
for (const child of await children(sessionID)) {
|
||||
await remove(child.id, false)
|
||||
await remove(child.id)
|
||||
}
|
||||
await unshare(sessionID).catch(() => {})
|
||||
for (const msg of await Storage.list(["message", sessionID])) {
|
||||
@@ -257,64 +303,75 @@ export namespace Session {
|
||||
await Storage.remove(msg)
|
||||
}
|
||||
await Storage.remove(["session", project.id, sessionID])
|
||||
if (emitEvent) {
|
||||
Bus.publish(Event.Deleted, {
|
||||
info: session,
|
||||
})
|
||||
}
|
||||
Bus.publish(Event.Deleted, {
|
||||
info: session,
|
||||
})
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export async function updateMessage(msg: MessageV2.Info) {
|
||||
export const updateMessage = fn(MessageV2.Info, async (msg) => {
|
||||
await Storage.write(["message", msg.sessionID, msg.id], msg)
|
||||
Bus.publish(MessageV2.Event.Updated, {
|
||||
info: msg,
|
||||
})
|
||||
return msg
|
||||
}
|
||||
})
|
||||
|
||||
export async function removeMessage(sessionID: string, messageID: string) {
|
||||
await Storage.remove(["message", sessionID, messageID])
|
||||
Bus.publish(MessageV2.Event.Removed, {
|
||||
sessionID,
|
||||
messageID,
|
||||
})
|
||||
return messageID
|
||||
}
|
||||
export const removeMessage = fn(
|
||||
z.object({
|
||||
sessionID: Identifier.schema("session"),
|
||||
messageID: Identifier.schema("message"),
|
||||
}),
|
||||
async (input) => {
|
||||
await Storage.remove(["message", input.sessionID, input.messageID])
|
||||
Bus.publish(MessageV2.Event.Removed, {
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
})
|
||||
return input.messageID
|
||||
},
|
||||
)
|
||||
|
||||
export async function updatePart(part: MessageV2.Part) {
|
||||
export const updatePart = fn(MessageV2.Part, async (part) => {
|
||||
await Storage.write(["part", part.messageID, part.id], part)
|
||||
Bus.publish(MessageV2.Event.PartUpdated, {
|
||||
part,
|
||||
})
|
||||
return part
|
||||
}
|
||||
})
|
||||
|
||||
export function getUsage(model: ModelsDev.Model, usage: LanguageModelUsage, metadata?: ProviderMetadata) {
|
||||
const tokens = {
|
||||
input: usage.inputTokens ?? 0,
|
||||
output: usage.outputTokens ?? 0,
|
||||
reasoning: usage?.reasoningTokens ?? 0,
|
||||
cache: {
|
||||
write: (metadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
||||
// @ts-expect-error
|
||||
metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
|
||||
0) as number,
|
||||
read: usage.cachedInputTokens ?? 0,
|
||||
},
|
||||
}
|
||||
return {
|
||||
cost: new Decimal(0)
|
||||
.add(new Decimal(tokens.input).mul(model.cost?.input ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.output).mul(model.cost?.output ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.read).mul(model.cost?.cache_read ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.write).mul(model.cost?.cache_write ?? 0).div(1_000_000))
|
||||
.toNumber(),
|
||||
tokens,
|
||||
}
|
||||
}
|
||||
export const getUsage = fn(
|
||||
z.object({
|
||||
model: z.custom<ModelsDev.Model>(),
|
||||
usage: z.custom<LanguageModelUsage>(),
|
||||
metadata: z.custom<ProviderMetadata>().optional(),
|
||||
}),
|
||||
(input) => {
|
||||
const tokens = {
|
||||
input: input.usage.inputTokens ?? 0,
|
||||
output: input.usage.outputTokens ?? 0,
|
||||
reasoning: input.usage?.reasoningTokens ?? 0,
|
||||
cache: {
|
||||
write: (input.metadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
||||
// @ts-expect-error
|
||||
input.metadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
|
||||
0) as number,
|
||||
read: input.usage.cachedInputTokens ?? 0,
|
||||
},
|
||||
}
|
||||
return {
|
||||
cost: new Decimal(0)
|
||||
.add(new Decimal(tokens.input).mul(input.model.cost?.input ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.output).mul(input.model.cost?.output ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.read).mul(input.model.cost?.cache_read ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000))
|
||||
.toNumber(),
|
||||
tokens,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export class BusyError extends Error {
|
||||
constructor(public readonly sessionID: string) {
|
||||
@@ -322,27 +379,30 @@ export namespace Session {
|
||||
}
|
||||
}
|
||||
|
||||
export async function initialize(input: {
|
||||
sessionID: string
|
||||
modelID: string
|
||||
providerID: string
|
||||
messageID: string
|
||||
}) {
|
||||
await SessionPrompt.prompt({
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
model: {
|
||||
providerID: input.providerID,
|
||||
modelID: input.modelID,
|
||||
},
|
||||
parts: [
|
||||
{
|
||||
id: Identifier.ascending("part"),
|
||||
type: "text",
|
||||
text: PROMPT_INITIALIZE.replace("${path}", Instance.worktree),
|
||||
export const initialize = fn(
|
||||
z.object({
|
||||
sessionID: Identifier.schema("session"),
|
||||
modelID: z.string(),
|
||||
providerID: z.string(),
|
||||
messageID: Identifier.schema("message"),
|
||||
}),
|
||||
async (input) => {
|
||||
await SessionPrompt.prompt({
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
model: {
|
||||
providerID: input.providerID,
|
||||
modelID: input.modelID,
|
||||
},
|
||||
],
|
||||
})
|
||||
await Project.setInitialized(Instance.project.id)
|
||||
}
|
||||
parts: [
|
||||
{
|
||||
id: Identifier.ascending("part"),
|
||||
type: "text",
|
||||
text: PROMPT_INITIALIZE.replace("${path}", Instance.worktree),
|
||||
},
|
||||
],
|
||||
})
|
||||
await Project.setInitialized(Instance.project.id)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import { NamedError } from "../util/error"
|
||||
import { ulid } from "ulid"
|
||||
import { spawn } from "child_process"
|
||||
import { Command } from "../command"
|
||||
import { $ } from "bun"
|
||||
import { $, fileURLToPath } from "bun"
|
||||
import { ConfigMarkdown } from "../config/markdown"
|
||||
|
||||
export namespace SessionPrompt {
|
||||
@@ -528,6 +528,8 @@ export namespace SessionPrompt {
|
||||
)
|
||||
|
||||
return {
|
||||
title: "",
|
||||
metadata: {},
|
||||
output,
|
||||
}
|
||||
}
|
||||
@@ -589,7 +591,7 @@ export namespace SessionPrompt {
|
||||
log.info("file", { mime: part.mime })
|
||||
// have to normalize, symbol search returns absolute paths
|
||||
// Decode the pathname since URL constructor doesn't automatically decode it
|
||||
const filepath = decodeURIComponent(url.pathname)
|
||||
const filepath = fileURLToPath(part.url)
|
||||
const stat = await Bun.file(filepath).stat()
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
@@ -604,14 +606,14 @@ export namespace SessionPrompt {
|
||||
end: url.searchParams.get("end"),
|
||||
}
|
||||
if (range.start != null) {
|
||||
const filePath = part.url.split("?")[0]
|
||||
const filePathURI = part.url.split("?")[0]
|
||||
let start = parseInt(range.start)
|
||||
let end = range.end ? parseInt(range.end) : undefined
|
||||
// some LSP servers (eg, gopls) don't give full range in
|
||||
// workspace/symbol searches, so we'll try to find the
|
||||
// symbol in the document to get the full range
|
||||
if (start === end) {
|
||||
const symbols = await LSP.documentSymbol(filePath)
|
||||
const symbols = await LSP.documentSymbol(filePathURI)
|
||||
for (const symbol of symbols) {
|
||||
let range: LSP.Range | undefined
|
||||
if ("range" in symbol) {
|
||||
@@ -1031,7 +1033,11 @@ export namespace SessionPrompt {
|
||||
break
|
||||
|
||||
case "finish-step":
|
||||
const usage = Session.getUsage(input.model, value.usage, value.providerMetadata)
|
||||
const usage = Session.getUsage({
|
||||
model: input.model,
|
||||
usage: value.usage,
|
||||
metadata: value.providerMetadata,
|
||||
})
|
||||
assistantMsg.cost += usage.cost
|
||||
assistantMsg.tokens = usage.tokens
|
||||
await Session.updatePart({
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import z from "zod/v4"
|
||||
import { Bus } from "../bus"
|
||||
import { Storage } from "../storage/storage"
|
||||
|
||||
export namespace Todo {
|
||||
export const Info = z
|
||||
.object({
|
||||
content: z.string().describe("Brief description of the task"),
|
||||
status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
||||
priority: z.string().describe("Priority level of the task: high, medium, low"),
|
||||
id: z.string().describe("Unique identifier for the todo item"),
|
||||
})
|
||||
.meta({ ref: "Todo" })
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
export const Event = {
|
||||
Updated: Bus.event(
|
||||
"todo.updated",
|
||||
z.object({
|
||||
sessionID: z.string(),
|
||||
todos: z.array(Info),
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
||||
export async function update(input: { sessionID: string; todos: Info[] }) {
|
||||
await Storage.write(["todo", input.sessionID], input.todos)
|
||||
Bus.publish(Event.Updated, input)
|
||||
}
|
||||
|
||||
export async function get(sessionID: string) {
|
||||
return Storage.read<Info[]>(["todo", sessionID]) ?? []
|
||||
}
|
||||
}
|
||||
@@ -201,6 +201,10 @@ export const BashTool = Tool.define("bash", {
|
||||
output += "\n\n(Output was truncated due to length limit)"
|
||||
}
|
||||
|
||||
if (process.signalCode === "SIGTERM" && params.timeout) {
|
||||
output += `\n\n(Command timed out after ${timeout} ms)`
|
||||
}
|
||||
|
||||
return {
|
||||
title: params.command,
|
||||
metadata: {
|
||||
|
||||
@@ -20,7 +20,7 @@ export const GrepTool = Tool.define("grep", {
|
||||
const searchPath = params.path || Instance.directory
|
||||
|
||||
const rgPath = await Ripgrep.filepath()
|
||||
const args = ["-n", params.pattern]
|
||||
const args = ["-nH", "--field-match-separator=|", params.pattern]
|
||||
if (params.include) {
|
||||
args.push("--glob", params.include)
|
||||
}
|
||||
@@ -53,11 +53,11 @@ export const GrepTool = Tool.define("grep", {
|
||||
for (const line of lines) {
|
||||
if (!line) continue
|
||||
|
||||
const [filePath, lineNumStr, ...lineTextParts] = line.split(":")
|
||||
const [filePath, lineNumStr, ...lineTextParts] = line.split("|")
|
||||
if (!filePath || !lineNumStr || lineTextParts.length === 0) continue
|
||||
|
||||
const lineNum = parseInt(lineNumStr, 10)
|
||||
const lineText = lineTextParts.join(":")
|
||||
const lineText = lineTextParts.join("|")
|
||||
|
||||
const file = Bun.file(filePath)
|
||||
const stats = await file.stat().catch(() => null)
|
||||
|
||||
@@ -26,8 +26,11 @@ export const TaskTool = Tool.define("task", async () => {
|
||||
async execute(params, ctx) {
|
||||
const agent = await Agent.get(params.subagent_type)
|
||||
if (!agent) throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`)
|
||||
const session = await Session.create(ctx.sessionID, params.description + ` (@${agent.name} subagent)`)
|
||||
const msg = await Session.getMessage(ctx.sessionID, ctx.messageID)
|
||||
const session = await Session.create({
|
||||
parentID: ctx.sessionID,
|
||||
title: params.description + ` (@${agent.name} subagent)`,
|
||||
})
|
||||
const msg = await Session.getMessage({ sessionID: ctx.sessionID, messageID: ctx.messageID })
|
||||
if (msg.info.role !== "assistant") throw new Error("Not an assistant message")
|
||||
const messageID = Identifier.ascending("message")
|
||||
const parts: Record<string, MessageV2.ToolPart> = {}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
const parser = async () => {
|
||||
try {
|
||||
const { default: Parser } = await import("tree-sitter")
|
||||
const Bash = await import("tree-sitter-bash")
|
||||
const p = new Parser()
|
||||
p.setLanguage(Bash.language as any)
|
||||
return p
|
||||
} catch (e) {
|
||||
const { default: Parser } = await import("web-tree-sitter")
|
||||
const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, { with: { type: "wasm" } })
|
||||
await Parser.init({
|
||||
locateFile() {
|
||||
return treeWasm
|
||||
},
|
||||
})
|
||||
const { default: bashWasm } = await import("tree-sitter-bash/tree-sitter-bash.wasm" as string, {
|
||||
with: { type: "wasm" },
|
||||
})
|
||||
const bashLanguage = await Parser.Language.load(bashWasm)
|
||||
const p = new Parser()
|
||||
p.setLanguage(bashLanguage)
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
const sourceCode = `cd --foo foo/bar && echo "hello" && cd ../baz`
|
||||
|
||||
const tree = await parser().then((p) => p.parse(sourceCode))
|
||||
|
||||
// Function to extract commands and arguments
|
||||
function extractCommands(node: any): Array<{ command: string; args: string[] }> {
|
||||
const commands: Array<{ command: string; args: string[] }> = []
|
||||
|
||||
function traverse(node: any) {
|
||||
if (node.type === "command") {
|
||||
const commandNode = node.child(0)
|
||||
if (commandNode) {
|
||||
const command = commandNode.text
|
||||
const args: string[] = []
|
||||
|
||||
// Extract arguments
|
||||
for (let i = 1; i < node.childCount; i++) {
|
||||
const child = node.child(i)
|
||||
if (child && child.type === "word") {
|
||||
args.push(child.text)
|
||||
}
|
||||
}
|
||||
|
||||
commands.push({ command, args })
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse children
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
traverse(node.child(i))
|
||||
}
|
||||
}
|
||||
|
||||
traverse(node)
|
||||
return commands
|
||||
}
|
||||
|
||||
// Extract and display commands
|
||||
console.log("Source code: " + sourceCode)
|
||||
const commands = extractCommands(tree.rootNode)
|
||||
console.log("Extracted commands:")
|
||||
commands.forEach((cmd, index) => {
|
||||
console.log(`${index + 1}. Command: ${cmd.command}`)
|
||||
console.log(` Args: [${cmd.args.join(", ")}]`)
|
||||
})
|
||||
@@ -1,31 +1,18 @@
|
||||
import z from "zod/v4"
|
||||
import { Tool } from "./tool"
|
||||
import DESCRIPTION_WRITE from "./todowrite.txt"
|
||||
import { Instance } from "../project/instance"
|
||||
|
||||
const TodoInfo = z.object({
|
||||
content: z.string().describe("Brief description of the task"),
|
||||
status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
||||
priority: z.string().describe("Priority level of the task: high, medium, low"),
|
||||
id: z.string().describe("Unique identifier for the todo item"),
|
||||
})
|
||||
type TodoInfo = z.infer<typeof TodoInfo>
|
||||
|
||||
const state = Instance.state(() => {
|
||||
const todos: {
|
||||
[sessionId: string]: TodoInfo[]
|
||||
} = {}
|
||||
return todos
|
||||
})
|
||||
import { Todo } from "../session/todo"
|
||||
|
||||
export const TodoWriteTool = Tool.define("todowrite", {
|
||||
description: DESCRIPTION_WRITE,
|
||||
parameters: z.object({
|
||||
todos: z.array(TodoInfo).describe("The updated todo list"),
|
||||
todos: z.array(Todo.Info).describe("The updated todo list"),
|
||||
}),
|
||||
async execute(params, opts) {
|
||||
const todos = state()
|
||||
todos[opts.sessionID] = params.todos
|
||||
await Todo.update({
|
||||
sessionID: opts.sessionID,
|
||||
todos: params.todos,
|
||||
})
|
||||
return {
|
||||
title: `${params.todos.filter((x) => x.status !== "completed").length} todos`,
|
||||
output: JSON.stringify(params.todos, null, 2),
|
||||
@@ -40,7 +27,7 @@ export const TodoReadTool = Tool.define("todoread", {
|
||||
description: "Use this tool to read your todo list",
|
||||
parameters: z.object({}),
|
||||
async execute(_params, opts) {
|
||||
const todos = state()[opts.sessionID] ?? []
|
||||
const todos = await Todo.get(opts.sessionID)
|
||||
return {
|
||||
title: `${todos.filter((x) => x.status !== "completed").length} todos`,
|
||||
metadata: {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export function fn<T extends z.ZodType, Result>(schema: T, cb: (input: z.infer<T>) => Result) {
|
||||
const result = (input: z.infer<T>) => {
|
||||
const parsed = schema.parse(input)
|
||||
return cb(parsed)
|
||||
}
|
||||
result.force = (input: z.infer<T>) => cb(input)
|
||||
result.schema = schema
|
||||
return result
|
||||
}
|
||||
@@ -2,6 +2,14 @@
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@tsconfig/bun/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"]
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "@opentui/solid",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"customConditions": ["browser"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@tui/*": ["./src/cli/cmd/tui/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
+7
-7
@@ -2,15 +2,15 @@ name: CI
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "generated"
|
||||
- "codegen/**"
|
||||
- "integrated/**"
|
||||
- "stl-preview-head/**"
|
||||
- "stl-preview-base/**"
|
||||
- 'generated'
|
||||
- 'codegen/**'
|
||||
- 'integrated/**'
|
||||
- 'stl-preview-head/**'
|
||||
- 'stl-preview-base/**'
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- "stl-preview-head/**"
|
||||
- "stl-preview-base/**"
|
||||
- 'stl-preview-head/**'
|
||||
- 'stl-preview-base/**'
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "0.15.0"
|
||||
}
|
||||
".": "0.16.2"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
configured_endpoints: 43
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-ad911ed0bdbeca62807509f364f25fcafd7a83e0b43e027ec0a85f72b7a4d963.yml
|
||||
openapi_spec_hash: 15152513b4246bf4b5f8546fa6f1603f
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-273fc9fea965af661dfed0902d00f10d6ed844f0681ca861a58821c4902eac2f.yml
|
||||
openapi_spec_hash: c6144f23a1bac75f79be86edd405552b
|
||||
config_hash: 026ef000d34bf2f930e7b41e77d2d3ff
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## 0.16.2 (2025-09-26)
|
||||
|
||||
Full Changelog: [v0.16.1...v0.16.2](https://github.com/sst/opencode-sdk-go/compare/v0.16.1...v0.16.2)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bugfix for setting JSON keys with special characters ([ac9a36f](https://github.com/sst/opencode-sdk-go/commit/ac9a36feb1c185ebf766d76909d0b86ac805e8a6))
|
||||
|
||||
## 0.16.1 (2025-09-20)
|
||||
|
||||
Full Changelog: [v0.16.0...v0.16.1](https://github.com/sst/opencode-sdk-go/compare/v0.16.0...v0.16.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use slices.Concat instead of sometimes modifying r.Options ([12e8b40](https://github.com/sst/opencode-sdk-go/commit/12e8b40809071095b0abb9b8031686353c8ac149))
|
||||
|
||||
|
||||
### Chores
|
||||
|
||||
* bump minimum go version to 1.22 ([1a61c5c](https://github.com/sst/opencode-sdk-go/commit/1a61c5cc7e8f68cc1b0c219738cab530cb6aa3a2))
|
||||
* do not install brew dependencies in ./scripts/bootstrap by default ([f6d3eaf](https://github.com/sst/opencode-sdk-go/commit/f6d3eafffc20e124bbfae6ac5ddc1b1122ad3e27))
|
||||
* update more docs for 1.22 ([a3d0b0f](https://github.com/sst/opencode-sdk-go/commit/a3d0b0f26ed92ce1a6433f5bcf37a6436d268ba5))
|
||||
|
||||
## 0.16.0 (2025-09-17)
|
||||
|
||||
Full Changelog: [v0.15.0...v0.16.0](https://github.com/sst/opencode-sdk-go/compare/v0.15.0...v0.16.0)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** api update ([46e978e](https://github.com/sst/opencode-sdk-go/commit/46e978e43aee733d5c1c09dc5be6d8ac2a752427))
|
||||
|
||||
## 0.15.0 (2025-09-16)
|
||||
|
||||
Full Changelog: [v0.14.0...v0.15.0](https://github.com/sst/opencode-sdk-go/compare/v0.14.0...v0.15.0)
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([397048f](https://github.com/sst/opencode-sdk-go/commit/397048faca7a1de7a028edd2254a0ad7797b151f))
|
||||
* **api:** api update ([397048f](https://github.com/sst/opencode-sdk-go/commit/397048faca7a1de7a028edd2254a0ad7797b151f))
|
||||
|
||||
## 0.14.0 (2025-09-14)
|
||||
|
||||
@@ -14,7 +45,7 @@ Full Changelog: [v0.13.0...v0.14.0](https://github.com/sst/opencode-sdk-go/compa
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([dad0bc3](https://github.com/sst/opencode-sdk-go/commit/dad0bc3da99f20a0d002a6b94e049fb70f8e6a77))
|
||||
* **api:** api update ([dad0bc3](https://github.com/sst/opencode-sdk-go/commit/dad0bc3da99f20a0d002a6b94e049fb70f8e6a77))
|
||||
|
||||
## 0.13.0 (2025-09-14)
|
||||
|
||||
@@ -22,7 +53,7 @@ Full Changelog: [v0.12.0...v0.13.0](https://github.com/sst/opencode-sdk-go/compa
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([80da4fb](https://github.com/sst/opencode-sdk-go/commit/80da4fb4ea9c6afb51a7e7135d9f5560ce6f2a6c))
|
||||
* **api:** api update ([80da4fb](https://github.com/sst/opencode-sdk-go/commit/80da4fb4ea9c6afb51a7e7135d9f5560ce6f2a6c))
|
||||
|
||||
## 0.12.0 (2025-09-14)
|
||||
|
||||
@@ -30,7 +61,7 @@ Full Changelog: [v0.11.0...v0.12.0](https://github.com/sst/opencode-sdk-go/compa
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([7e3808b](https://github.com/sst/opencode-sdk-go/commit/7e3808ba349dc653174b32b48a1120c18d2975c2))
|
||||
* **api:** api update ([7e3808b](https://github.com/sst/opencode-sdk-go/commit/7e3808ba349dc653174b32b48a1120c18d2975c2))
|
||||
|
||||
## 0.11.0 (2025-09-14)
|
||||
|
||||
@@ -38,7 +69,7 @@ Full Changelog: [v0.10.0...v0.11.0](https://github.com/sst/opencode-sdk-go/compa
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([a3d37f5](https://github.com/sst/opencode-sdk-go/commit/a3d37f5671545866547d351fc21b49809cc8b3c2))
|
||||
* **api:** api update ([a3d37f5](https://github.com/sst/opencode-sdk-go/commit/a3d37f5671545866547d351fc21b49809cc8b3c2))
|
||||
|
||||
## 0.10.0 (2025-09-11)
|
||||
|
||||
@@ -46,7 +77,7 @@ Full Changelog: [v0.9.0...v0.10.0](https://github.com/sst/opencode-sdk-go/compar
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([0dc01f6](https://github.com/sst/opencode-sdk-go/commit/0dc01f6695c9b8400a4dc92166c5002bb120cf50))
|
||||
* **api:** api update ([0dc01f6](https://github.com/sst/opencode-sdk-go/commit/0dc01f6695c9b8400a4dc92166c5002bb120cf50))
|
||||
|
||||
## 0.9.0 (2025-09-10)
|
||||
|
||||
@@ -54,7 +85,7 @@ Full Changelog: [v0.8.0...v0.9.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([2d3a28d](https://github.com/sst/opencode-sdk-go/commit/2d3a28df5657845aa4d73087e1737d1fc8c3ce1c))
|
||||
* **api:** api update ([2d3a28d](https://github.com/sst/opencode-sdk-go/commit/2d3a28df5657845aa4d73087e1737d1fc8c3ce1c))
|
||||
|
||||
## 0.8.0 (2025-09-01)
|
||||
|
||||
@@ -62,7 +93,7 @@ Full Changelog: [v0.7.0...v0.8.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([ae87a71](https://github.com/sst/opencode-sdk-go/commit/ae87a71949994590ace8285a39f0991ef34b664d))
|
||||
* **api:** api update ([ae87a71](https://github.com/sst/opencode-sdk-go/commit/ae87a71949994590ace8285a39f0991ef34b664d))
|
||||
|
||||
## 0.7.0 (2025-09-01)
|
||||
|
||||
@@ -70,7 +101,7 @@ Full Changelog: [v0.6.0...v0.7.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([64bb1b1](https://github.com/sst/opencode-sdk-go/commit/64bb1b1ee0cbe153abc6fb7bd9703b47911724d4))
|
||||
* **api:** api update ([64bb1b1](https://github.com/sst/opencode-sdk-go/commit/64bb1b1ee0cbe153abc6fb7bd9703b47911724d4))
|
||||
|
||||
## 0.6.0 (2025-09-01)
|
||||
|
||||
@@ -78,7 +109,7 @@ Full Changelog: [v0.5.0...v0.6.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([928e384](https://github.com/sst/opencode-sdk-go/commit/928e3843355f96899f046f002b84372281dad0c8))
|
||||
* **api:** api update ([928e384](https://github.com/sst/opencode-sdk-go/commit/928e3843355f96899f046f002b84372281dad0c8))
|
||||
|
||||
## 0.5.0 (2025-08-31)
|
||||
|
||||
@@ -86,7 +117,7 @@ Full Changelog: [v0.4.0...v0.5.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([44b281d](https://github.com/sst/opencode-sdk-go/commit/44b281d0bb39c5022a984ac9d0fca1529ccc0604))
|
||||
* **api:** api update ([44b281d](https://github.com/sst/opencode-sdk-go/commit/44b281d0bb39c5022a984ac9d0fca1529ccc0604))
|
||||
|
||||
## 0.4.0 (2025-08-31)
|
||||
|
||||
@@ -94,7 +125,7 @@ Full Changelog: [v0.3.0...v0.4.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([fa9d6ec](https://github.com/sst/opencode-sdk-go/commit/fa9d6ec6472e62f4f6605d0a71a7aa8bf8a24559))
|
||||
* **api:** api update ([fa9d6ec](https://github.com/sst/opencode-sdk-go/commit/fa9d6ec6472e62f4f6605d0a71a7aa8bf8a24559))
|
||||
|
||||
## 0.3.0 (2025-08-31)
|
||||
|
||||
@@ -102,7 +133,7 @@ Full Changelog: [v0.2.0...v0.3.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([aae1c06](https://github.com/sst/opencode-sdk-go/commit/aae1c06bb5a93a1cd9c589846a84b3f16246f5da))
|
||||
* **api:** api update ([aae1c06](https://github.com/sst/opencode-sdk-go/commit/aae1c06bb5a93a1cd9c589846a84b3f16246f5da))
|
||||
|
||||
## 0.2.0 (2025-08-31)
|
||||
|
||||
@@ -110,7 +141,7 @@ Full Changelog: [v0.1.0...v0.2.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([1472790](https://github.com/sst/opencode-sdk-go/commit/1472790542515f47bd46e2a9e28d8afea024cf9c))
|
||||
* **api:** api update ([1472790](https://github.com/sst/opencode-sdk-go/commit/1472790542515f47bd46e2a9e28d8afea024cf9c))
|
||||
|
||||
## 0.1.0 (2025-08-31)
|
||||
|
||||
@@ -118,59 +149,61 @@ Full Changelog: [v0.0.1...v0.1.0](https://github.com/sst/opencode-sdk-go/compare
|
||||
|
||||
### Features
|
||||
|
||||
- **api:** api update ([3f03ddd](https://github.com/sst/opencode-sdk-go/commit/3f03dddd5ec0de98f99ce48679077dcae9ceffd6))
|
||||
- **api:** api update ([e9f79c4](https://github.com/sst/opencode-sdk-go/commit/e9f79c4792b21ef64ab0431ffd76f5a71e04d182))
|
||||
- **api:** api update ([139a686](https://github.com/sst/opencode-sdk-go/commit/139a6862d2f0ab0c8ea791663d736868be3e96e6))
|
||||
- **api:** api update ([2ed0800](https://github.com/sst/opencode-sdk-go/commit/2ed0800b2c5b99877e9f7fde669a6c005fad6b77))
|
||||
- **api:** api update ([88a87a4](https://github.com/sst/opencode-sdk-go/commit/88a87a458f56ce0c18b502c73da933f614f56e8b))
|
||||
- **api:** api update ([0e5d65b](https://github.com/sst/opencode-sdk-go/commit/0e5d65b571e7b30dc6347e6730098878ebba3a42))
|
||||
- **api:** api update ([ba381f1](https://github.com/sst/opencode-sdk-go/commit/ba381f1e07aad24e9824df7d53befae2a644f69f))
|
||||
- **api:** api update ([3f429f5](https://github.com/sst/opencode-sdk-go/commit/3f429f5b4be5607433ef5fdc0d5bf67fe590d039))
|
||||
- **api:** api update ([9f34787](https://github.com/sst/opencode-sdk-go/commit/9f347876b35b7f898060c1a5f71c322e95978e3e))
|
||||
- **api:** api update ([379c8e0](https://github.com/sst/opencode-sdk-go/commit/379c8e00197e13aebaf2f2d61277b125f1f90011))
|
||||
- **api:** api update ([550511c](https://github.com/sst/opencode-sdk-go/commit/550511c4c5b5055ac8ff22b7b11731331bd9d088))
|
||||
- **api:** api update ([547f0c2](https://github.com/sst/opencode-sdk-go/commit/547f0c262f2df1ce83eaa7267d68be64bb29b841))
|
||||
- **api:** api update ([b7b0720](https://github.com/sst/opencode-sdk-go/commit/b7b07204bff314da24b1819c128835a43ef64065))
|
||||
- **api:** api update ([7250ffc](https://github.com/sst/opencode-sdk-go/commit/7250ffcba262b916c958ddecc2a42927982db39f))
|
||||
- **api:** api update ([17fbab7](https://github.com/sst/opencode-sdk-go/commit/17fbab73111a3eae488737c69b12370bc69c65f7))
|
||||
- **api:** api update ([1270b5c](https://github.com/sst/opencode-sdk-go/commit/1270b5cd81e6ac769dcd92ade6d877891bf51bd5))
|
||||
- **api:** api update ([a238d4a](https://github.com/sst/opencode-sdk-go/commit/a238d4abd6ed7d15f3547d27a4b6ecf4aec8431e))
|
||||
- **api:** api update ([7475655](https://github.com/sst/opencode-sdk-go/commit/7475655aca577fe4f807c2f02f92171f6a358e9c))
|
||||
- **api:** api update ([429d258](https://github.com/sst/opencode-sdk-go/commit/429d258bb56e9cdeb1528be3944bf5537ac26a96))
|
||||
- **api:** api update ([f250915](https://github.com/sst/opencode-sdk-go/commit/f2509157eaf1b453e741ee9482127cad2e3ace25))
|
||||
- **api:** api update ([5efc987](https://github.com/sst/opencode-sdk-go/commit/5efc987353801d1e772c20edf162b1c75da32743))
|
||||
- **api:** api update ([98a8350](https://github.com/sst/opencode-sdk-go/commit/98a83504f7cfc361e83314c3e79a4e9ff53f0560))
|
||||
- **api:** api update ([6da8bf8](https://github.com/sst/opencode-sdk-go/commit/6da8bf8bfe91d45991fb580753d77c5534fc0b1b))
|
||||
- **api:** api update ([f8c7148](https://github.com/sst/opencode-sdk-go/commit/f8c7148ae56143823186e2675a78e82676154956))
|
||||
- **api:** manual updates ([7cf038f](https://github.com/sst/opencode-sdk-go/commit/7cf038ffae5da1b77e1cef11b5fa166a53b467f2))
|
||||
- **api:** update via SDK Studio ([068a0eb](https://github.com/sst/opencode-sdk-go/commit/068a0eb025010da0c8d86fa1bb496a39dbedcef9))
|
||||
- **api:** update via SDK Studio ([ca651ed](https://github.com/sst/opencode-sdk-go/commit/ca651edaf71d1f3678f929287474f5bc4f1aad10))
|
||||
- **api:** update via SDK Studio ([13550a5](https://github.com/sst/opencode-sdk-go/commit/13550a5c65d77325e945ed99fe0799cd1107b775))
|
||||
- **api:** update via SDK Studio ([7b73730](https://github.com/sst/opencode-sdk-go/commit/7b73730c7fa62ba966dda3541c3e97b49be8d2bf))
|
||||
- **api:** update via SDK Studio ([9e39a59](https://github.com/sst/opencode-sdk-go/commit/9e39a59b3d5d1bd5e64633732521fb28362cc70e))
|
||||
- **api:** update via SDK Studio ([9609d1b](https://github.com/sst/opencode-sdk-go/commit/9609d1b1db7806d00cb846c9914cb4935cdedf52))
|
||||
- **api:** update via SDK Studio ([51315fa](https://github.com/sst/opencode-sdk-go/commit/51315fa2eae424743ea79701e67d44447c44144d))
|
||||
- **api:** update via SDK Studio ([af07955](https://github.com/sst/opencode-sdk-go/commit/af0795543240aefaf04fc7663a348825541c79ed))
|
||||
- **api:** update via SDK Studio ([5e3468a](https://github.com/sst/opencode-sdk-go/commit/5e3468a0aaa5ed3b13e019c3a24e0ba9147d1675))
|
||||
- **api:** update via SDK Studio ([0a73e04](https://github.com/sst/opencode-sdk-go/commit/0a73e04c23c90b2061611edaa8fd6282dc0ce397))
|
||||
- **api:** update via SDK Studio ([9b7883a](https://github.com/sst/opencode-sdk-go/commit/9b7883a144eeac526d9d04538e0876a9d18bb844))
|
||||
- **client:** expand max streaming buffer size ([76303e5](https://github.com/sst/opencode-sdk-go/commit/76303e51067e78e732af26ced9d83b8bad7655c3))
|
||||
- **client:** support optional json html escaping ([449748f](https://github.com/sst/opencode-sdk-go/commit/449748f35a1d8cb6f91dc36d25bf9489f4f371bd))
|
||||
* **api:** api update ([3f03ddd](https://github.com/sst/opencode-sdk-go/commit/3f03dddd5ec0de98f99ce48679077dcae9ceffd6))
|
||||
* **api:** api update ([e9f79c4](https://github.com/sst/opencode-sdk-go/commit/e9f79c4792b21ef64ab0431ffd76f5a71e04d182))
|
||||
* **api:** api update ([139a686](https://github.com/sst/opencode-sdk-go/commit/139a6862d2f0ab0c8ea791663d736868be3e96e6))
|
||||
* **api:** api update ([2ed0800](https://github.com/sst/opencode-sdk-go/commit/2ed0800b2c5b99877e9f7fde669a6c005fad6b77))
|
||||
* **api:** api update ([88a87a4](https://github.com/sst/opencode-sdk-go/commit/88a87a458f56ce0c18b502c73da933f614f56e8b))
|
||||
* **api:** api update ([0e5d65b](https://github.com/sst/opencode-sdk-go/commit/0e5d65b571e7b30dc6347e6730098878ebba3a42))
|
||||
* **api:** api update ([ba381f1](https://github.com/sst/opencode-sdk-go/commit/ba381f1e07aad24e9824df7d53befae2a644f69f))
|
||||
* **api:** api update ([3f429f5](https://github.com/sst/opencode-sdk-go/commit/3f429f5b4be5607433ef5fdc0d5bf67fe590d039))
|
||||
* **api:** api update ([9f34787](https://github.com/sst/opencode-sdk-go/commit/9f347876b35b7f898060c1a5f71c322e95978e3e))
|
||||
* **api:** api update ([379c8e0](https://github.com/sst/opencode-sdk-go/commit/379c8e00197e13aebaf2f2d61277b125f1f90011))
|
||||
* **api:** api update ([550511c](https://github.com/sst/opencode-sdk-go/commit/550511c4c5b5055ac8ff22b7b11731331bd9d088))
|
||||
* **api:** api update ([547f0c2](https://github.com/sst/opencode-sdk-go/commit/547f0c262f2df1ce83eaa7267d68be64bb29b841))
|
||||
* **api:** api update ([b7b0720](https://github.com/sst/opencode-sdk-go/commit/b7b07204bff314da24b1819c128835a43ef64065))
|
||||
* **api:** api update ([7250ffc](https://github.com/sst/opencode-sdk-go/commit/7250ffcba262b916c958ddecc2a42927982db39f))
|
||||
* **api:** api update ([17fbab7](https://github.com/sst/opencode-sdk-go/commit/17fbab73111a3eae488737c69b12370bc69c65f7))
|
||||
* **api:** api update ([1270b5c](https://github.com/sst/opencode-sdk-go/commit/1270b5cd81e6ac769dcd92ade6d877891bf51bd5))
|
||||
* **api:** api update ([a238d4a](https://github.com/sst/opencode-sdk-go/commit/a238d4abd6ed7d15f3547d27a4b6ecf4aec8431e))
|
||||
* **api:** api update ([7475655](https://github.com/sst/opencode-sdk-go/commit/7475655aca577fe4f807c2f02f92171f6a358e9c))
|
||||
* **api:** api update ([429d258](https://github.com/sst/opencode-sdk-go/commit/429d258bb56e9cdeb1528be3944bf5537ac26a96))
|
||||
* **api:** api update ([f250915](https://github.com/sst/opencode-sdk-go/commit/f2509157eaf1b453e741ee9482127cad2e3ace25))
|
||||
* **api:** api update ([5efc987](https://github.com/sst/opencode-sdk-go/commit/5efc987353801d1e772c20edf162b1c75da32743))
|
||||
* **api:** api update ([98a8350](https://github.com/sst/opencode-sdk-go/commit/98a83504f7cfc361e83314c3e79a4e9ff53f0560))
|
||||
* **api:** api update ([6da8bf8](https://github.com/sst/opencode-sdk-go/commit/6da8bf8bfe91d45991fb580753d77c5534fc0b1b))
|
||||
* **api:** api update ([f8c7148](https://github.com/sst/opencode-sdk-go/commit/f8c7148ae56143823186e2675a78e82676154956))
|
||||
* **api:** manual updates ([7cf038f](https://github.com/sst/opencode-sdk-go/commit/7cf038ffae5da1b77e1cef11b5fa166a53b467f2))
|
||||
* **api:** update via SDK Studio ([068a0eb](https://github.com/sst/opencode-sdk-go/commit/068a0eb025010da0c8d86fa1bb496a39dbedcef9))
|
||||
* **api:** update via SDK Studio ([ca651ed](https://github.com/sst/opencode-sdk-go/commit/ca651edaf71d1f3678f929287474f5bc4f1aad10))
|
||||
* **api:** update via SDK Studio ([13550a5](https://github.com/sst/opencode-sdk-go/commit/13550a5c65d77325e945ed99fe0799cd1107b775))
|
||||
* **api:** update via SDK Studio ([7b73730](https://github.com/sst/opencode-sdk-go/commit/7b73730c7fa62ba966dda3541c3e97b49be8d2bf))
|
||||
* **api:** update via SDK Studio ([9e39a59](https://github.com/sst/opencode-sdk-go/commit/9e39a59b3d5d1bd5e64633732521fb28362cc70e))
|
||||
* **api:** update via SDK Studio ([9609d1b](https://github.com/sst/opencode-sdk-go/commit/9609d1b1db7806d00cb846c9914cb4935cdedf52))
|
||||
* **api:** update via SDK Studio ([51315fa](https://github.com/sst/opencode-sdk-go/commit/51315fa2eae424743ea79701e67d44447c44144d))
|
||||
* **api:** update via SDK Studio ([af07955](https://github.com/sst/opencode-sdk-go/commit/af0795543240aefaf04fc7663a348825541c79ed))
|
||||
* **api:** update via SDK Studio ([5e3468a](https://github.com/sst/opencode-sdk-go/commit/5e3468a0aaa5ed3b13e019c3a24e0ba9147d1675))
|
||||
* **api:** update via SDK Studio ([0a73e04](https://github.com/sst/opencode-sdk-go/commit/0a73e04c23c90b2061611edaa8fd6282dc0ce397))
|
||||
* **api:** update via SDK Studio ([9b7883a](https://github.com/sst/opencode-sdk-go/commit/9b7883a144eeac526d9d04538e0876a9d18bb844))
|
||||
* **client:** expand max streaming buffer size ([76303e5](https://github.com/sst/opencode-sdk-go/commit/76303e51067e78e732af26ced9d83b8bad7655c3))
|
||||
* **client:** support optional json html escaping ([449748f](https://github.com/sst/opencode-sdk-go/commit/449748f35a1d8cb6f91dc36d25bf9489f4f371bd))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **client:** process custom base url ahead of time ([9b360d6](https://github.com/sst/opencode-sdk-go/commit/9b360d642cf6f302104308af5622e17099899e5f))
|
||||
- **client:** resolve lint errors in streaming tests ([4d36cb0](https://github.com/sst/opencode-sdk-go/commit/4d36cb09fc9d436734d5dab1c499acaa88568ff7))
|
||||
- close body before retrying ([4da3f7f](https://github.com/sst/opencode-sdk-go/commit/4da3f7f372bad222a189ba3eabcfde3373166ae5))
|
||||
- don't try to deserialize as json when ResponseBodyInto is []byte ([595291f](https://github.com/sst/opencode-sdk-go/commit/595291f6dba6af472f160b9f8e3d145002f43a4a))
|
||||
* **client:** process custom base url ahead of time ([9b360d6](https://github.com/sst/opencode-sdk-go/commit/9b360d642cf6f302104308af5622e17099899e5f))
|
||||
* **client:** resolve lint errors in streaming tests ([4d36cb0](https://github.com/sst/opencode-sdk-go/commit/4d36cb09fc9d436734d5dab1c499acaa88568ff7))
|
||||
* close body before retrying ([4da3f7f](https://github.com/sst/opencode-sdk-go/commit/4da3f7f372bad222a189ba3eabcfde3373166ae5))
|
||||
* don't try to deserialize as json when ResponseBodyInto is []byte ([595291f](https://github.com/sst/opencode-sdk-go/commit/595291f6dba6af472f160b9f8e3d145002f43a4a))
|
||||
|
||||
|
||||
### Chores
|
||||
|
||||
- **ci:** only run for pushes and fork pull requests ([bea59b8](https://github.com/sst/opencode-sdk-go/commit/bea59b886800ef555f89c47a9256d6392ed2e53d))
|
||||
- **internal:** codegen related update ([6a22ce6](https://github.com/sst/opencode-sdk-go/commit/6a22ce6df155f5003e80b8a75686a9e513a5568a))
|
||||
- **internal:** fix lint script for tests ([391c482](https://github.com/sst/opencode-sdk-go/commit/391c482148ed0a77c4ad52807abeb2d540b56797))
|
||||
- **internal:** update comment in script ([b7f1c3e](https://github.com/sst/opencode-sdk-go/commit/b7f1c3e16935c71e243004b8f321d661cd8e9474))
|
||||
- lint tests ([616796b](https://github.com/sst/opencode-sdk-go/commit/616796b761704bde6be5c6c2428f28c79c7f05ff))
|
||||
- lint tests in subpackages ([50c82ff](https://github.com/sst/opencode-sdk-go/commit/50c82ff0757c973834b68adc22566b70f767b611))
|
||||
- sync repo ([2f34d5d](https://github.com/sst/opencode-sdk-go/commit/2f34d5d53e56e9cdc3df99be7ee7efc83dd977a3))
|
||||
- update @stainless-api/prism-cli to v5.15.0 ([2f24852](https://github.com/sst/opencode-sdk-go/commit/2f2485216d4f4891d1fbfbc23ff8410c2f35152a))
|
||||
* **ci:** only run for pushes and fork pull requests ([bea59b8](https://github.com/sst/opencode-sdk-go/commit/bea59b886800ef555f89c47a9256d6392ed2e53d))
|
||||
* **internal:** codegen related update ([6a22ce6](https://github.com/sst/opencode-sdk-go/commit/6a22ce6df155f5003e80b8a75686a9e513a5568a))
|
||||
* **internal:** fix lint script for tests ([391c482](https://github.com/sst/opencode-sdk-go/commit/391c482148ed0a77c4ad52807abeb2d540b56797))
|
||||
* **internal:** update comment in script ([b7f1c3e](https://github.com/sst/opencode-sdk-go/commit/b7f1c3e16935c71e243004b8f321d661cd8e9474))
|
||||
* lint tests ([616796b](https://github.com/sst/opencode-sdk-go/commit/616796b761704bde6be5c6c2428f28c79c7f05ff))
|
||||
* lint tests in subpackages ([50c82ff](https://github.com/sst/opencode-sdk-go/commit/50c82ff0757c973834b68adc22566b70f767b611))
|
||||
* sync repo ([2f34d5d](https://github.com/sst/opencode-sdk-go/commit/2f34d5d53e56e9cdc3df99be7ee7efc83dd977a3))
|
||||
* update @stainless-api/prism-cli to v5.15.0 ([2f24852](https://github.com/sst/opencode-sdk-go/commit/2f2485216d4f4891d1fbfbc23ff8410c2f35152a))
|
||||
|
||||
@@ -9,7 +9,7 @@ $ ./scripts/build
|
||||
|
||||
This will install all the required dependencies and build the SDK.
|
||||
|
||||
You can also [install go 1.18+ manually](https://go.dev/doc/install).
|
||||
You can also [install go 1.22+ manually](https://go.dev/doc/install).
|
||||
|
||||
## Modifying/Adding code
|
||||
|
||||
|
||||
@@ -24,14 +24,14 @@ Or to pin the version:
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```sh
|
||||
go get -u 'github.com/sst/opencode-sdk-go@v0.15.0'
|
||||
go get -u 'github.com/sst/opencode-sdk-go@v0.16.2'
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## Requirements
|
||||
|
||||
This library requires Go 1.18+.
|
||||
This library requires Go 1.22+.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewAgentService(opts ...option.RequestOption) (r *AgentService) {
|
||||
|
||||
// List all agents
|
||||
func (r *AgentService) List(ctx context.Context, query AgentListParams, opts ...option.RequestOption) (res *[]Agent, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "agent"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewAppService(opts ...option.RequestOption) (r *AppService) {
|
||||
|
||||
// Write a log entry to the server logs
|
||||
func (r *AppService) Log(ctx context.Context, params AppLogParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "log"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...)
|
||||
return
|
||||
@@ -43,7 +44,7 @@ func (r *AppService) Log(ctx context.Context, params AppLogParams, opts ...optio
|
||||
|
||||
// List all providers
|
||||
func (r *AppService) Providers(ctx context.Context, query AppProvidersParams, opts ...option.RequestOption) (res *AppProvidersResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "config/providers"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/requestconfig"
|
||||
"github.com/sst/opencode-sdk-go/option"
|
||||
@@ -95,7 +96,7 @@ func NewClient(opts ...option.RequestOption) (r *Client) {
|
||||
// For even greater flexibility, see [option.WithResponseInto] and
|
||||
// [option.WithResponseBodyInto].
|
||||
func (r *Client) Execute(ctx context.Context, method string, path string, params interface{}, res interface{}, opts ...option.RequestOption) error {
|
||||
opts = append(r.Options, opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
return requestconfig.ExecuteNewRequest(ctx, method, path, params, res, opts...)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewCommandService(opts ...option.RequestOption) (r *CommandService) {
|
||||
|
||||
// List all commands
|
||||
func (r *CommandService) List(ctx context.Context, query CommandListParams, opts ...option.RequestOption) (res *[]Command, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "command"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -38,7 +39,7 @@ func NewConfigService(opts ...option.RequestOption) (r *ConfigService) {
|
||||
|
||||
// Get config info
|
||||
func (r *ConfigService) Get(ctx context.Context, query ConfigGetParams, opts ...option.RequestOption) (res *Config, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "config"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
+190
-92
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -43,7 +44,7 @@ func (r *EventService) ListStreaming(ctx context.Context, query EventListParams,
|
||||
raw *http.Response
|
||||
err error
|
||||
)
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
opts = append([]option.RequestOption{option.WithHeader("Accept", "text/event-stream")}, opts...)
|
||||
path := "event"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &raw, opts...)
|
||||
@@ -61,11 +62,12 @@ type EventListResponse struct {
|
||||
// [EventListResponseEventSessionCompactedProperties], [Permission],
|
||||
// [EventListResponseEventPermissionRepliedProperties],
|
||||
// [EventListResponseEventFileEditedProperties],
|
||||
// [EventListResponseEventFileWatcherUpdatedProperties],
|
||||
// [EventListResponseEventTodoUpdatedProperties],
|
||||
// [EventListResponseEventSessionIdleProperties],
|
||||
// [EventListResponseEventSessionUpdatedProperties],
|
||||
// [EventListResponseEventSessionDeletedProperties],
|
||||
// [EventListResponseEventSessionErrorProperties], [interface{}],
|
||||
// [EventListResponseEventFileWatcherUpdatedProperties],
|
||||
// [EventListResponseEventIdeInstalledProperties].
|
||||
Properties interface{} `json:"properties,required"`
|
||||
Type EventListResponseType `json:"type,required"`
|
||||
@@ -107,11 +109,10 @@ func (r *EventListResponse) UnmarshalJSON(data []byte) (err error) {
|
||||
// [EventListResponseEventSessionCompacted],
|
||||
// [EventListResponseEventPermissionUpdated],
|
||||
// [EventListResponseEventPermissionReplied], [EventListResponseEventFileEdited],
|
||||
// [EventListResponseEventFileWatcherUpdated], [EventListResponseEventTodoUpdated],
|
||||
// [EventListResponseEventSessionIdle], [EventListResponseEventSessionUpdated],
|
||||
// [EventListResponseEventSessionDeleted], [EventListResponseEventSessionError],
|
||||
// [EventListResponseEventServerConnected],
|
||||
// [EventListResponseEventFileWatcherUpdated],
|
||||
// [EventListResponseEventIdeInstalled].
|
||||
// [EventListResponseEventServerConnected], [EventListResponseEventIdeInstalled].
|
||||
func (r EventListResponse) AsUnion() EventListResponseUnion {
|
||||
return r.union
|
||||
}
|
||||
@@ -124,11 +125,10 @@ func (r EventListResponse) AsUnion() EventListResponseUnion {
|
||||
// [EventListResponseEventSessionCompacted],
|
||||
// [EventListResponseEventPermissionUpdated],
|
||||
// [EventListResponseEventPermissionReplied], [EventListResponseEventFileEdited],
|
||||
// [EventListResponseEventFileWatcherUpdated], [EventListResponseEventTodoUpdated],
|
||||
// [EventListResponseEventSessionIdle], [EventListResponseEventSessionUpdated],
|
||||
// [EventListResponseEventSessionDeleted], [EventListResponseEventSessionError],
|
||||
// [EventListResponseEventServerConnected],
|
||||
// [EventListResponseEventFileWatcherUpdated] or
|
||||
// [EventListResponseEventIdeInstalled].
|
||||
// [EventListResponseEventServerConnected] or [EventListResponseEventIdeInstalled].
|
||||
type EventListResponseUnion interface {
|
||||
implementsEventListResponse()
|
||||
}
|
||||
@@ -177,6 +177,14 @@ func init() {
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventFileEdited{}),
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventFileWatcherUpdated{}),
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventTodoUpdated{}),
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventSessionIdle{}),
|
||||
@@ -197,10 +205,6 @@ func init() {
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventServerConnected{}),
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventFileWatcherUpdated{}),
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventIdeInstalled{}),
|
||||
@@ -799,6 +803,177 @@ func (r EventListResponseEventFileEditedType) IsKnown() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdated struct {
|
||||
Properties EventListResponseEventFileWatcherUpdatedProperties `json:"properties,required"`
|
||||
Type EventListResponseEventFileWatcherUpdatedType `json:"type,required"`
|
||||
JSON eventListResponseEventFileWatcherUpdatedJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventFileWatcherUpdatedJSON contains the JSON metadata for the
|
||||
// struct [EventListResponseEventFileWatcherUpdated]
|
||||
type eventListResponseEventFileWatcherUpdatedJSON struct {
|
||||
Properties apijson.Field
|
||||
Type apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *EventListResponseEventFileWatcherUpdated) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r eventListResponseEventFileWatcherUpdatedJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
func (r EventListResponseEventFileWatcherUpdated) implementsEventListResponse() {}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdatedProperties struct {
|
||||
Event EventListResponseEventFileWatcherUpdatedPropertiesEvent `json:"event,required"`
|
||||
File string `json:"file,required"`
|
||||
JSON eventListResponseEventFileWatcherUpdatedPropertiesJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventFileWatcherUpdatedPropertiesJSON contains the JSON
|
||||
// metadata for the struct [EventListResponseEventFileWatcherUpdatedProperties]
|
||||
type eventListResponseEventFileWatcherUpdatedPropertiesJSON struct {
|
||||
Event apijson.Field
|
||||
File apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *EventListResponseEventFileWatcherUpdatedProperties) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r eventListResponseEventFileWatcherUpdatedPropertiesJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdatedPropertiesEvent string
|
||||
|
||||
const (
|
||||
EventListResponseEventFileWatcherUpdatedPropertiesEventAdd EventListResponseEventFileWatcherUpdatedPropertiesEvent = "add"
|
||||
EventListResponseEventFileWatcherUpdatedPropertiesEventChange EventListResponseEventFileWatcherUpdatedPropertiesEvent = "change"
|
||||
EventListResponseEventFileWatcherUpdatedPropertiesEventUnlink EventListResponseEventFileWatcherUpdatedPropertiesEvent = "unlink"
|
||||
)
|
||||
|
||||
func (r EventListResponseEventFileWatcherUpdatedPropertiesEvent) IsKnown() bool {
|
||||
switch r {
|
||||
case EventListResponseEventFileWatcherUpdatedPropertiesEventAdd, EventListResponseEventFileWatcherUpdatedPropertiesEventChange, EventListResponseEventFileWatcherUpdatedPropertiesEventUnlink:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdatedType string
|
||||
|
||||
const (
|
||||
EventListResponseEventFileWatcherUpdatedTypeFileWatcherUpdated EventListResponseEventFileWatcherUpdatedType = "file.watcher.updated"
|
||||
)
|
||||
|
||||
func (r EventListResponseEventFileWatcherUpdatedType) IsKnown() bool {
|
||||
switch r {
|
||||
case EventListResponseEventFileWatcherUpdatedTypeFileWatcherUpdated:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type EventListResponseEventTodoUpdated struct {
|
||||
Properties EventListResponseEventTodoUpdatedProperties `json:"properties,required"`
|
||||
Type EventListResponseEventTodoUpdatedType `json:"type,required"`
|
||||
JSON eventListResponseEventTodoUpdatedJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventTodoUpdatedJSON contains the JSON metadata for the struct
|
||||
// [EventListResponseEventTodoUpdated]
|
||||
type eventListResponseEventTodoUpdatedJSON struct {
|
||||
Properties apijson.Field
|
||||
Type apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *EventListResponseEventTodoUpdated) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r eventListResponseEventTodoUpdatedJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
func (r EventListResponseEventTodoUpdated) implementsEventListResponse() {}
|
||||
|
||||
type EventListResponseEventTodoUpdatedProperties struct {
|
||||
SessionID string `json:"sessionID,required"`
|
||||
Todos []EventListResponseEventTodoUpdatedPropertiesTodo `json:"todos,required"`
|
||||
JSON eventListResponseEventTodoUpdatedPropertiesJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventTodoUpdatedPropertiesJSON contains the JSON metadata for
|
||||
// the struct [EventListResponseEventTodoUpdatedProperties]
|
||||
type eventListResponseEventTodoUpdatedPropertiesJSON struct {
|
||||
SessionID apijson.Field
|
||||
Todos apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *EventListResponseEventTodoUpdatedProperties) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r eventListResponseEventTodoUpdatedPropertiesJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type EventListResponseEventTodoUpdatedPropertiesTodo struct {
|
||||
// Unique identifier for the todo item
|
||||
ID string `json:"id,required"`
|
||||
// Brief description of the task
|
||||
Content string `json:"content,required"`
|
||||
// Priority level of the task: high, medium, low
|
||||
Priority string `json:"priority,required"`
|
||||
// Current status of the task: pending, in_progress, completed, cancelled
|
||||
Status string `json:"status,required"`
|
||||
JSON eventListResponseEventTodoUpdatedPropertiesTodoJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventTodoUpdatedPropertiesTodoJSON contains the JSON metadata
|
||||
// for the struct [EventListResponseEventTodoUpdatedPropertiesTodo]
|
||||
type eventListResponseEventTodoUpdatedPropertiesTodoJSON struct {
|
||||
ID apijson.Field
|
||||
Content apijson.Field
|
||||
Priority apijson.Field
|
||||
Status apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *EventListResponseEventTodoUpdatedPropertiesTodo) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r eventListResponseEventTodoUpdatedPropertiesTodoJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type EventListResponseEventTodoUpdatedType string
|
||||
|
||||
const (
|
||||
EventListResponseEventTodoUpdatedTypeTodoUpdated EventListResponseEventTodoUpdatedType = "todo.updated"
|
||||
)
|
||||
|
||||
func (r EventListResponseEventTodoUpdatedType) IsKnown() bool {
|
||||
switch r {
|
||||
case EventListResponseEventTodoUpdatedTypeTodoUpdated:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type EventListResponseEventSessionIdle struct {
|
||||
Properties EventListResponseEventSessionIdleProperties `json:"properties,required"`
|
||||
Type EventListResponseEventSessionIdleType `json:"type,required"`
|
||||
@@ -1210,84 +1385,6 @@ func (r EventListResponseEventServerConnectedType) IsKnown() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdated struct {
|
||||
Properties EventListResponseEventFileWatcherUpdatedProperties `json:"properties,required"`
|
||||
Type EventListResponseEventFileWatcherUpdatedType `json:"type,required"`
|
||||
JSON eventListResponseEventFileWatcherUpdatedJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventFileWatcherUpdatedJSON contains the JSON metadata for the
|
||||
// struct [EventListResponseEventFileWatcherUpdated]
|
||||
type eventListResponseEventFileWatcherUpdatedJSON struct {
|
||||
Properties apijson.Field
|
||||
Type apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *EventListResponseEventFileWatcherUpdated) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r eventListResponseEventFileWatcherUpdatedJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
func (r EventListResponseEventFileWatcherUpdated) implementsEventListResponse() {}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdatedProperties struct {
|
||||
Event EventListResponseEventFileWatcherUpdatedPropertiesEvent `json:"event,required"`
|
||||
File string `json:"file,required"`
|
||||
JSON eventListResponseEventFileWatcherUpdatedPropertiesJSON `json:"-"`
|
||||
}
|
||||
|
||||
// eventListResponseEventFileWatcherUpdatedPropertiesJSON contains the JSON
|
||||
// metadata for the struct [EventListResponseEventFileWatcherUpdatedProperties]
|
||||
type eventListResponseEventFileWatcherUpdatedPropertiesJSON struct {
|
||||
Event apijson.Field
|
||||
File apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *EventListResponseEventFileWatcherUpdatedProperties) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r eventListResponseEventFileWatcherUpdatedPropertiesJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdatedPropertiesEvent string
|
||||
|
||||
const (
|
||||
EventListResponseEventFileWatcherUpdatedPropertiesEventAdd EventListResponseEventFileWatcherUpdatedPropertiesEvent = "add"
|
||||
EventListResponseEventFileWatcherUpdatedPropertiesEventChange EventListResponseEventFileWatcherUpdatedPropertiesEvent = "change"
|
||||
EventListResponseEventFileWatcherUpdatedPropertiesEventUnlink EventListResponseEventFileWatcherUpdatedPropertiesEvent = "unlink"
|
||||
)
|
||||
|
||||
func (r EventListResponseEventFileWatcherUpdatedPropertiesEvent) IsKnown() bool {
|
||||
switch r {
|
||||
case EventListResponseEventFileWatcherUpdatedPropertiesEventAdd, EventListResponseEventFileWatcherUpdatedPropertiesEventChange, EventListResponseEventFileWatcherUpdatedPropertiesEventUnlink:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type EventListResponseEventFileWatcherUpdatedType string
|
||||
|
||||
const (
|
||||
EventListResponseEventFileWatcherUpdatedTypeFileWatcherUpdated EventListResponseEventFileWatcherUpdatedType = "file.watcher.updated"
|
||||
)
|
||||
|
||||
func (r EventListResponseEventFileWatcherUpdatedType) IsKnown() bool {
|
||||
switch r {
|
||||
case EventListResponseEventFileWatcherUpdatedTypeFileWatcherUpdated:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type EventListResponseEventIdeInstalled struct {
|
||||
Properties EventListResponseEventIdeInstalledProperties `json:"properties,required"`
|
||||
Type EventListResponseEventIdeInstalledType `json:"type,required"`
|
||||
@@ -1361,18 +1458,19 @@ const (
|
||||
EventListResponseTypePermissionUpdated EventListResponseType = "permission.updated"
|
||||
EventListResponseTypePermissionReplied EventListResponseType = "permission.replied"
|
||||
EventListResponseTypeFileEdited EventListResponseType = "file.edited"
|
||||
EventListResponseTypeFileWatcherUpdated EventListResponseType = "file.watcher.updated"
|
||||
EventListResponseTypeTodoUpdated EventListResponseType = "todo.updated"
|
||||
EventListResponseTypeSessionIdle EventListResponseType = "session.idle"
|
||||
EventListResponseTypeSessionUpdated EventListResponseType = "session.updated"
|
||||
EventListResponseTypeSessionDeleted EventListResponseType = "session.deleted"
|
||||
EventListResponseTypeSessionError EventListResponseType = "session.error"
|
||||
EventListResponseTypeServerConnected EventListResponseType = "server.connected"
|
||||
EventListResponseTypeFileWatcherUpdated EventListResponseType = "file.watcher.updated"
|
||||
EventListResponseTypeIdeInstalled EventListResponseType = "ide.installed"
|
||||
)
|
||||
|
||||
func (r EventListResponseType) IsKnown() bool {
|
||||
switch r {
|
||||
case EventListResponseTypeInstallationUpdated, EventListResponseTypeLspClientDiagnostics, EventListResponseTypeMessageUpdated, EventListResponseTypeMessageRemoved, EventListResponseTypeMessagePartUpdated, EventListResponseTypeMessagePartRemoved, EventListResponseTypeSessionCompacted, EventListResponseTypePermissionUpdated, EventListResponseTypePermissionReplied, EventListResponseTypeFileEdited, EventListResponseTypeSessionIdle, EventListResponseTypeSessionUpdated, EventListResponseTypeSessionDeleted, EventListResponseTypeSessionError, EventListResponseTypeServerConnected, EventListResponseTypeFileWatcherUpdated, EventListResponseTypeIdeInstalled:
|
||||
case EventListResponseTypeInstallationUpdated, EventListResponseTypeLspClientDiagnostics, EventListResponseTypeMessageUpdated, EventListResponseTypeMessageRemoved, EventListResponseTypeMessagePartUpdated, EventListResponseTypeMessagePartRemoved, EventListResponseTypeSessionCompacted, EventListResponseTypePermissionUpdated, EventListResponseTypePermissionReplied, EventListResponseTypeFileEdited, EventListResponseTypeFileWatcherUpdated, EventListResponseTypeTodoUpdated, EventListResponseTypeSessionIdle, EventListResponseTypeSessionUpdated, EventListResponseTypeSessionDeleted, EventListResponseTypeSessionError, EventListResponseTypeServerConnected, EventListResponseTypeIdeInstalled:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewFileService(opts ...option.RequestOption) (r *FileService) {
|
||||
|
||||
// List files and directories
|
||||
func (r *FileService) List(ctx context.Context, query FileListParams, opts ...option.RequestOption) (res *[]FileNode, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "file"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
@@ -43,7 +44,7 @@ func (r *FileService) List(ctx context.Context, query FileListParams, opts ...op
|
||||
|
||||
// Read a file
|
||||
func (r *FileService) Read(ctx context.Context, query FileReadParams, opts ...option.RequestOption) (res *FileReadResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "file/content"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
@@ -51,7 +52,7 @@ func (r *FileService) Read(ctx context.Context, query FileReadParams, opts ...op
|
||||
|
||||
// Get file status
|
||||
func (r *FileService) Status(ctx context.Context, query FileStatusParams, opts ...option.RequestOption) (res *[]File, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "file/status"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewFindService(opts ...option.RequestOption) (r *FindService) {
|
||||
|
||||
// Find files
|
||||
func (r *FindService) Files(ctx context.Context, query FindFilesParams, opts ...option.RequestOption) (res *[]string, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "find/file"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
@@ -43,7 +44,7 @@ func (r *FindService) Files(ctx context.Context, query FindFilesParams, opts ...
|
||||
|
||||
// Find workspace symbols
|
||||
func (r *FindService) Symbols(ctx context.Context, query FindSymbolsParams, opts ...option.RequestOption) (res *[]Symbol, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "find/symbol"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
@@ -51,7 +52,7 @@ func (r *FindService) Symbols(ctx context.Context, query FindSymbolsParams, opts
|
||||
|
||||
// Find text in files
|
||||
func (r *FindService) Text(ctx context.Context, query FindTextParams, opts ...option.RequestOption) (res *[]FindTextResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "find"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/sst/opencode-sdk-go
|
||||
|
||||
go 1.21
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/tidwall/gjson v1.14.4
|
||||
|
||||
@@ -224,7 +224,7 @@ func (d *decoderBuilder) newUnionDecoder(t reflect.Type) decoderFunc {
|
||||
}
|
||||
|
||||
if len(unionEntry.discriminatorKey) != 0 {
|
||||
discriminatorValue := n.Get(unionEntry.discriminatorKey).Value()
|
||||
discriminatorValue := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
|
||||
if discriminatorValue == variant.DiscriminatorValue {
|
||||
inner := reflect.New(variant.Type).Elem()
|
||||
err := decoder(n, inner, state)
|
||||
|
||||
@@ -18,6 +18,10 @@ import (
|
||||
|
||||
var encoders sync.Map // map[encoderEntry]encoderFunc
|
||||
|
||||
// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
|
||||
// special characters that sjson interprets as a path.
|
||||
var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace
|
||||
|
||||
func Marshal(value interface{}) ([]byte, error) {
|
||||
e := &encoder{dateFormat: time.RFC3339}
|
||||
return e.marshal(value)
|
||||
@@ -276,7 +280,7 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
|
||||
if encoded == nil {
|
||||
continue
|
||||
}
|
||||
json, err = sjson.SetRawBytes(json, ef.tag.name, encoded)
|
||||
json, err = sjson.SetRawBytes(json, EscapeSJSONKey(ef.tag.name), encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -354,7 +358,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error)
|
||||
}
|
||||
encodedKeyString = string(encodedKeyBytes)
|
||||
}
|
||||
encodedKey := []byte(sjsonReplacer.Replace(encodedKeyString))
|
||||
encodedKey := []byte(encodedKeyString)
|
||||
pairs = append(pairs, mapPair{key: encodedKey, value: iter.Value()})
|
||||
}
|
||||
|
||||
@@ -372,7 +376,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error)
|
||||
if len(encodedValue) == 0 {
|
||||
continue
|
||||
}
|
||||
json, err = sjson.SetRawBytes(json, string(p.key), encodedValue)
|
||||
json, err = sjson.SetRawBytes(json, EscapeSJSONKey(string(p.key)), encodedValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -392,7 +396,3 @@ func (e *encoder) newMapEncoder(t reflect.Type) encoderFunc {
|
||||
return json, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have
|
||||
// special characters that sjson interprets as a path.
|
||||
var sjsonReplacer *strings.Replacer = strings.NewReplacer(".", "\\.", ":", "\\:", "*", "\\*")
|
||||
|
||||
@@ -362,7 +362,7 @@ var tests = map[string]struct {
|
||||
"date_time_nano_missing_t_coerce": {`"2007-03-01 13:03:05.123456789Z"`, time.Date(2007, time.March, 1, 13, 3, 5, 123456789, time.UTC)},
|
||||
|
||||
"map_string": {`{"foo":"bar"}`, map[string]string{"foo": "bar"}},
|
||||
"map_string_with_sjson_path_chars": {`{":a.b.c*:d*-1e.f":"bar"}`, map[string]string{":a.b.c*:d*-1e.f": "bar"}},
|
||||
"map_string_with_sjson_path_chars": {`{":a.b.c*:d*-1e.f@g?h":"bar"}`, map[string]string{":a.b.c*:d*-1e.f@g?h": "bar"}},
|
||||
"map_interface": {`{"a":1,"b":"str","c":false}`, map[string]interface{}{"a": float64(1), "b": "str", "c": false}},
|
||||
|
||||
"primitive_struct": {
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
package internal
|
||||
|
||||
const PackageVersion = "0.15.0" // x-release-please-version
|
||||
const PackageVersion = "0.16.2" // x-release-please-version
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewPathService(opts ...option.RequestOption) (r *PathService) {
|
||||
|
||||
// Get the current path
|
||||
func (r *PathService) Get(ctx context.Context, query PathGetParams, opts ...option.RequestOption) (res *Path, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "path"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewProjectService(opts ...option.RequestOption) (r *ProjectService) {
|
||||
|
||||
// List all projects
|
||||
func (r *ProjectService) List(ctx context.Context, query ProjectListParams, opts ...option.RequestOption) (res *[]Project, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "project"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
@@ -43,7 +44,7 @@ func (r *ProjectService) List(ctx context.Context, query ProjectListParams, opts
|
||||
|
||||
// Get the current project
|
||||
func (r *ProjectService) Current(ctx context.Context, query ProjectCurrentParams, opts ...option.RequestOption) (res *Project, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "project/current"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -60,5 +60,8 @@
|
||||
}
|
||||
],
|
||||
"release-type": "go",
|
||||
"extra-files": ["internal/version.go", "README.md"]
|
||||
}
|
||||
"extra-files": [
|
||||
"internal/version.go",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
@@ -4,10 +4,18 @@ set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then
|
||||
if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
|
||||
brew bundle check >/dev/null 2>&1 || {
|
||||
echo "==> Installing Homebrew dependencies…"
|
||||
brew bundle
|
||||
echo -n "==> Install Homebrew dependencies? (y/N): "
|
||||
read -r response
|
||||
case "$response" in
|
||||
[yY][eE][sS]|[yY])
|
||||
brew bundle
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
echo
|
||||
}
|
||||
fi
|
||||
|
||||
|
||||
+41
-34
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -42,7 +43,7 @@ func NewSessionService(opts ...option.RequestOption) (r *SessionService) {
|
||||
|
||||
// Create a new session
|
||||
func (r *SessionService) New(ctx context.Context, params SessionNewParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "session"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...)
|
||||
return
|
||||
@@ -50,7 +51,7 @@ func (r *SessionService) New(ctx context.Context, params SessionNewParams, opts
|
||||
|
||||
// Update session properties
|
||||
func (r *SessionService) Update(ctx context.Context, id string, params SessionUpdateParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -62,7 +63,7 @@ func (r *SessionService) Update(ctx context.Context, id string, params SessionUp
|
||||
|
||||
// List all sessions
|
||||
func (r *SessionService) List(ctx context.Context, query SessionListParams, opts ...option.RequestOption) (res *[]Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "session"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
|
||||
return
|
||||
@@ -70,7 +71,7 @@ func (r *SessionService) List(ctx context.Context, query SessionListParams, opts
|
||||
|
||||
// Delete a session and all its data
|
||||
func (r *SessionService) Delete(ctx context.Context, id string, body SessionDeleteParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -82,7 +83,7 @@ func (r *SessionService) Delete(ctx context.Context, id string, body SessionDele
|
||||
|
||||
// Abort a session
|
||||
func (r *SessionService) Abort(ctx context.Context, id string, body SessionAbortParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -94,7 +95,7 @@ func (r *SessionService) Abort(ctx context.Context, id string, body SessionAbort
|
||||
|
||||
// Get a session's children
|
||||
func (r *SessionService) Children(ctx context.Context, id string, query SessionChildrenParams, opts ...option.RequestOption) (res *[]Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -106,7 +107,7 @@ func (r *SessionService) Children(ctx context.Context, id string, query SessionC
|
||||
|
||||
// Send a new command to a session
|
||||
func (r *SessionService) Command(ctx context.Context, id string, params SessionCommandParams, opts ...option.RequestOption) (res *SessionCommandResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -118,7 +119,7 @@ func (r *SessionService) Command(ctx context.Context, id string, params SessionC
|
||||
|
||||
// Get session
|
||||
func (r *SessionService) Get(ctx context.Context, id string, query SessionGetParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -130,7 +131,7 @@ func (r *SessionService) Get(ctx context.Context, id string, query SessionGetPar
|
||||
|
||||
// Analyze the app and create an AGENTS.md file
|
||||
func (r *SessionService) Init(ctx context.Context, id string, params SessionInitParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -142,7 +143,7 @@ func (r *SessionService) Init(ctx context.Context, id string, params SessionInit
|
||||
|
||||
// Get a message from a session
|
||||
func (r *SessionService) Message(ctx context.Context, id string, messageID string, query SessionMessageParams, opts ...option.RequestOption) (res *SessionMessageResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -158,7 +159,7 @@ func (r *SessionService) Message(ctx context.Context, id string, messageID strin
|
||||
|
||||
// List messages for a session
|
||||
func (r *SessionService) Messages(ctx context.Context, id string, query SessionMessagesParams, opts ...option.RequestOption) (res *[]SessionMessagesResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -170,7 +171,7 @@ func (r *SessionService) Messages(ctx context.Context, id string, query SessionM
|
||||
|
||||
// Create and send a new message to a session
|
||||
func (r *SessionService) Prompt(ctx context.Context, id string, params SessionPromptParams, opts ...option.RequestOption) (res *SessionPromptResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -182,7 +183,7 @@ func (r *SessionService) Prompt(ctx context.Context, id string, params SessionPr
|
||||
|
||||
// Revert a message
|
||||
func (r *SessionService) Revert(ctx context.Context, id string, params SessionRevertParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -194,7 +195,7 @@ func (r *SessionService) Revert(ctx context.Context, id string, params SessionRe
|
||||
|
||||
// Share a session
|
||||
func (r *SessionService) Share(ctx context.Context, id string, body SessionShareParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -206,7 +207,7 @@ func (r *SessionService) Share(ctx context.Context, id string, body SessionShare
|
||||
|
||||
// Run a shell command
|
||||
func (r *SessionService) Shell(ctx context.Context, id string, params SessionShellParams, opts ...option.RequestOption) (res *AssistantMessage, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -218,7 +219,7 @@ func (r *SessionService) Shell(ctx context.Context, id string, params SessionShe
|
||||
|
||||
// Summarize the session
|
||||
func (r *SessionService) Summarize(ctx context.Context, id string, params SessionSummarizeParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -230,7 +231,7 @@ func (r *SessionService) Summarize(ctx context.Context, id string, params Sessio
|
||||
|
||||
// Restore all reverted messages
|
||||
func (r *SessionService) Unrevert(ctx context.Context, id string, body SessionUnrevertParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -242,7 +243,7 @@ func (r *SessionService) Unrevert(ctx context.Context, id string, body SessionUn
|
||||
|
||||
// Unshare the session
|
||||
func (r *SessionService) Unshare(ctx context.Context, id string, body SessionUnshareParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
@@ -1724,14 +1725,15 @@ func (r SymbolSourceRangeStartParam) MarshalJSON() (data []byte, err error) {
|
||||
}
|
||||
|
||||
type TextPart struct {
|
||||
ID string `json:"id,required"`
|
||||
MessageID string `json:"messageID,required"`
|
||||
SessionID string `json:"sessionID,required"`
|
||||
Text string `json:"text,required"`
|
||||
Type TextPartType `json:"type,required"`
|
||||
Synthetic bool `json:"synthetic"`
|
||||
Time TextPartTime `json:"time"`
|
||||
JSON textPartJSON `json:"-"`
|
||||
ID string `json:"id,required"`
|
||||
MessageID string `json:"messageID,required"`
|
||||
SessionID string `json:"sessionID,required"`
|
||||
Text string `json:"text,required"`
|
||||
Type TextPartType `json:"type,required"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
Synthetic bool `json:"synthetic"`
|
||||
Time TextPartTime `json:"time"`
|
||||
JSON textPartJSON `json:"-"`
|
||||
}
|
||||
|
||||
// textPartJSON contains the JSON metadata for the struct [TextPart]
|
||||
@@ -1741,6 +1743,7 @@ type textPartJSON struct {
|
||||
SessionID apijson.Field
|
||||
Text apijson.Field
|
||||
Type apijson.Field
|
||||
Metadata apijson.Field
|
||||
Synthetic apijson.Field
|
||||
Time apijson.Field
|
||||
raw string
|
||||
@@ -1797,6 +1800,7 @@ type TextPartInputParam struct {
|
||||
Text param.Field[string] `json:"text,required"`
|
||||
Type param.Field[TextPartInputType] `json:"type,required"`
|
||||
ID param.Field[string] `json:"id"`
|
||||
Metadata param.Field[map[string]interface{}] `json:"metadata"`
|
||||
Synthetic param.Field[bool] `json:"synthetic"`
|
||||
Time param.Field[TextPartInputTimeParam] `json:"time"`
|
||||
}
|
||||
@@ -1831,14 +1835,15 @@ func (r TextPartInputTimeParam) MarshalJSON() (data []byte, err error) {
|
||||
}
|
||||
|
||||
type ToolPart struct {
|
||||
ID string `json:"id,required"`
|
||||
CallID string `json:"callID,required"`
|
||||
MessageID string `json:"messageID,required"`
|
||||
SessionID string `json:"sessionID,required"`
|
||||
State ToolPartState `json:"state,required"`
|
||||
Tool string `json:"tool,required"`
|
||||
Type ToolPartType `json:"type,required"`
|
||||
JSON toolPartJSON `json:"-"`
|
||||
ID string `json:"id,required"`
|
||||
CallID string `json:"callID,required"`
|
||||
MessageID string `json:"messageID,required"`
|
||||
SessionID string `json:"sessionID,required"`
|
||||
State ToolPartState `json:"state,required"`
|
||||
Tool string `json:"tool,required"`
|
||||
Type ToolPartType `json:"type,required"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
JSON toolPartJSON `json:"-"`
|
||||
}
|
||||
|
||||
// toolPartJSON contains the JSON metadata for the struct [ToolPart]
|
||||
@@ -1850,6 +1855,7 @@ type toolPartJSON struct {
|
||||
State apijson.Field
|
||||
Tool apijson.Field
|
||||
Type apijson.Field
|
||||
Metadata apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
@@ -2556,6 +2562,7 @@ type SessionPromptParamsPart struct {
|
||||
Type param.Field[SessionPromptParamsPartsType] `json:"type,required"`
|
||||
ID param.Field[string] `json:"id"`
|
||||
Filename param.Field[string] `json:"filename"`
|
||||
Metadata param.Field[interface{}] `json:"metadata"`
|
||||
Mime param.Field[string] `json:"mime"`
|
||||
Name param.Field[string] `json:"name"`
|
||||
Source param.Field[interface{}] `json:"source"`
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestSessionNewWithOptionalParams(t *testing.T) {
|
||||
)
|
||||
_, err := client.Session.New(context.TODO(), opencode.SessionNewParams{
|
||||
Directory: opencode.F("directory"),
|
||||
ParentID: opencode.F("parentID"),
|
||||
ParentID: opencode.F("sesJ!"),
|
||||
Title: opencode.F("title"),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -106,7 +106,7 @@ func TestSessionDeleteWithOptionalParams(t *testing.T) {
|
||||
)
|
||||
_, err := client.Session.Delete(
|
||||
context.TODO(),
|
||||
"id",
|
||||
"sesJ!",
|
||||
opencode.SessionDeleteParams{
|
||||
Directory: opencode.F("directory"),
|
||||
},
|
||||
@@ -162,7 +162,7 @@ func TestSessionChildrenWithOptionalParams(t *testing.T) {
|
||||
)
|
||||
_, err := client.Session.Children(
|
||||
context.TODO(),
|
||||
"id",
|
||||
"sesJ!",
|
||||
opencode.SessionChildrenParams{
|
||||
Directory: opencode.F("directory"),
|
||||
},
|
||||
@@ -223,7 +223,7 @@ func TestSessionGetWithOptionalParams(t *testing.T) {
|
||||
)
|
||||
_, err := client.Session.Get(
|
||||
context.TODO(),
|
||||
"id",
|
||||
"sesJ!",
|
||||
opencode.SessionGetParams{
|
||||
Directory: opencode.F("directory"),
|
||||
},
|
||||
@@ -253,7 +253,7 @@ func TestSessionInitWithOptionalParams(t *testing.T) {
|
||||
context.TODO(),
|
||||
"id",
|
||||
opencode.SessionInitParams{
|
||||
MessageID: opencode.F("messageID"),
|
||||
MessageID: opencode.F("msgJ!"),
|
||||
ModelID: opencode.F("modelID"),
|
||||
ProviderID: opencode.F("providerID"),
|
||||
Directory: opencode.F("directory"),
|
||||
@@ -342,9 +342,12 @@ func TestSessionPromptWithOptionalParams(t *testing.T) {
|
||||
"id",
|
||||
opencode.SessionPromptParams{
|
||||
Parts: opencode.F([]opencode.SessionPromptParamsPartUnion{opencode.TextPartInputParam{
|
||||
Text: opencode.F("text"),
|
||||
Type: opencode.F(opencode.TextPartInputTypeText),
|
||||
ID: opencode.F("id"),
|
||||
Text: opencode.F("text"),
|
||||
Type: opencode.F(opencode.TextPartInputTypeText),
|
||||
ID: opencode.F("id"),
|
||||
Metadata: opencode.F(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
}),
|
||||
Synthetic: opencode.F(true),
|
||||
Time: opencode.F(opencode.TextPartInputTimeParam{
|
||||
Start: opencode.F(0.000000),
|
||||
@@ -533,7 +536,7 @@ func TestSessionUnshareWithOptionalParams(t *testing.T) {
|
||||
)
|
||||
_, err := client.Session.Unshare(
|
||||
context.TODO(),
|
||||
"id",
|
||||
"sesJ!",
|
||||
opencode.SessionUnshareParams{
|
||||
Directory: opencode.F("directory"),
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -40,7 +41,7 @@ func NewSessionPermissionService(opts ...option.RequestOption) (r *SessionPermis
|
||||
|
||||
// Respond to a permission request
|
||||
func (r *SessionPermissionService) Respond(ctx context.Context, id string, permissionID string, params SessionPermissionRespondParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
|
||||
+10
-9
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
||||
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||
"github.com/sst/opencode-sdk-go/internal/apiquery"
|
||||
@@ -35,7 +36,7 @@ func NewTuiService(opts ...option.RequestOption) (r *TuiService) {
|
||||
|
||||
// Append prompt to the TUI
|
||||
func (r *TuiService) AppendPrompt(ctx context.Context, params TuiAppendPromptParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/append-prompt"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...)
|
||||
return
|
||||
@@ -43,7 +44,7 @@ func (r *TuiService) AppendPrompt(ctx context.Context, params TuiAppendPromptPar
|
||||
|
||||
// Clear the prompt
|
||||
func (r *TuiService) ClearPrompt(ctx context.Context, body TuiClearPromptParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/clear-prompt"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||
return
|
||||
@@ -51,7 +52,7 @@ func (r *TuiService) ClearPrompt(ctx context.Context, body TuiClearPromptParams,
|
||||
|
||||
// Execute a TUI command (e.g. agent_cycle)
|
||||
func (r *TuiService) ExecuteCommand(ctx context.Context, params TuiExecuteCommandParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/execute-command"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...)
|
||||
return
|
||||
@@ -59,7 +60,7 @@ func (r *TuiService) ExecuteCommand(ctx context.Context, params TuiExecuteComman
|
||||
|
||||
// Open the help dialog
|
||||
func (r *TuiService) OpenHelp(ctx context.Context, body TuiOpenHelpParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/open-help"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||
return
|
||||
@@ -67,7 +68,7 @@ func (r *TuiService) OpenHelp(ctx context.Context, body TuiOpenHelpParams, opts
|
||||
|
||||
// Open the model dialog
|
||||
func (r *TuiService) OpenModels(ctx context.Context, body TuiOpenModelsParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/open-models"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||
return
|
||||
@@ -75,7 +76,7 @@ func (r *TuiService) OpenModels(ctx context.Context, body TuiOpenModelsParams, o
|
||||
|
||||
// Open the session dialog
|
||||
func (r *TuiService) OpenSessions(ctx context.Context, body TuiOpenSessionsParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/open-sessions"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||
return
|
||||
@@ -83,7 +84,7 @@ func (r *TuiService) OpenSessions(ctx context.Context, body TuiOpenSessionsParam
|
||||
|
||||
// Open the theme dialog
|
||||
func (r *TuiService) OpenThemes(ctx context.Context, body TuiOpenThemesParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/open-themes"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||
return
|
||||
@@ -91,7 +92,7 @@ func (r *TuiService) OpenThemes(ctx context.Context, body TuiOpenThemesParams, o
|
||||
|
||||
// Show a toast notification in the TUI
|
||||
func (r *TuiService) ShowToast(ctx context.Context, params TuiShowToastParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/show-toast"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...)
|
||||
return
|
||||
@@ -99,7 +100,7 @@ func (r *TuiService) ShowToast(ctx context.Context, params TuiShowToastParams, o
|
||||
|
||||
// Submit the prompt
|
||||
func (r *TuiService) SubmitPrompt(ctx context.Context, body TuiSubmitPromptParams, opts ...option.RequestOption) (res *bool, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
opts = slices.Concat(r.Options, opts)
|
||||
path := "tui/submit-prompt"
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||
return
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
@@ -32,8 +32,12 @@ import type {
|
||||
SessionUpdateResponses,
|
||||
SessionChildrenData,
|
||||
SessionChildrenResponses,
|
||||
SessionTodoData,
|
||||
SessionTodoResponses,
|
||||
SessionInitData,
|
||||
SessionInitResponses,
|
||||
SessionForkData,
|
||||
SessionForkResponses,
|
||||
SessionAbortData,
|
||||
SessionAbortResponses,
|
||||
SessionUnshareData,
|
||||
@@ -78,6 +82,8 @@ import type {
|
||||
AppLogResponses,
|
||||
AppAgentsData,
|
||||
AppAgentsResponses,
|
||||
McpStatusData,
|
||||
McpStatusResponses,
|
||||
TuiAppendPromptData,
|
||||
TuiAppendPromptResponses,
|
||||
TuiOpenHelpData,
|
||||
@@ -292,6 +298,16 @@ class Session extends _HeyApiClient {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the todo list for a session
|
||||
*/
|
||||
public todo<ThrowOnError extends boolean = false>(options: Options<SessionTodoData, ThrowOnError>) {
|
||||
return (options.client ?? this._client).get<SessionTodoResponses, unknown, ThrowOnError>({
|
||||
url: "/session/{id}/todo",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze the app and create an AGENTS.md file
|
||||
*/
|
||||
@@ -306,6 +322,20 @@ class Session extends _HeyApiClient {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork an existing session at a specific message
|
||||
*/
|
||||
public fork<ThrowOnError extends boolean = false>(options: Options<SessionForkData, ThrowOnError>) {
|
||||
return (options.client ?? this._client).post<SessionForkResponses, unknown, ThrowOnError>({
|
||||
url: "/session/{id}/fork",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort a session
|
||||
*/
|
||||
@@ -539,6 +569,18 @@ class App extends _HeyApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
class Mcp extends _HeyApiClient {
|
||||
/**
|
||||
* Get MCP server status
|
||||
*/
|
||||
public status<ThrowOnError extends boolean = false>(options?: Options<McpStatusData, ThrowOnError>) {
|
||||
return (options?.client ?? this._client).get<McpStatusResponses, unknown, ThrowOnError>({
|
||||
url: "/mcp",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Tui extends _HeyApiClient {
|
||||
/**
|
||||
* Append prompt to the TUI
|
||||
@@ -696,6 +738,7 @@ export class OpencodeClient extends _HeyApiClient {
|
||||
find = new Find({ client: this._client })
|
||||
file = new File({ client: this._client })
|
||||
app = new App({ client: this._client })
|
||||
mcp = new Mcp({ client: this._client })
|
||||
tui = new Tui({ client: this._client })
|
||||
auth = new Auth({ client: this._client })
|
||||
event = new Event({ client: this._client })
|
||||
|
||||
@@ -550,6 +550,25 @@ export type Session = {
|
||||
}
|
||||
}
|
||||
|
||||
export type Todo = {
|
||||
/**
|
||||
* Brief description of the task
|
||||
*/
|
||||
content: string
|
||||
/**
|
||||
* Current status of the task: pending, in_progress, completed, cancelled
|
||||
*/
|
||||
status: string
|
||||
/**
|
||||
* Priority level of the task: high, medium, low
|
||||
*/
|
||||
priority: string
|
||||
/**
|
||||
* Unique identifier for the todo item
|
||||
*/
|
||||
id: string
|
||||
}
|
||||
|
||||
export type UserMessage = {
|
||||
id: string
|
||||
sessionID: string
|
||||
@@ -1093,6 +1112,14 @@ export type EventFileWatcherUpdated = {
|
||||
}
|
||||
}
|
||||
|
||||
export type EventTodoUpdated = {
|
||||
type: "todo.updated"
|
||||
properties: {
|
||||
sessionID: string
|
||||
todos: Array<Todo>
|
||||
}
|
||||
}
|
||||
|
||||
export type EventSessionIdle = {
|
||||
type: "session.idle"
|
||||
properties: {
|
||||
@@ -1148,6 +1175,7 @@ export type Event =
|
||||
| EventPermissionReplied
|
||||
| EventFileEdited
|
||||
| EventFileWatcherUpdated
|
||||
| EventTodoUpdated
|
||||
| EventSessionIdle
|
||||
| EventSessionUpdated
|
||||
| EventSessionDeleted
|
||||
@@ -1440,11 +1468,34 @@ export type SessionChildrenResponses = {
|
||||
|
||||
export type SessionChildrenResponse = SessionChildrenResponses[keyof SessionChildrenResponses]
|
||||
|
||||
export type SessionTodoData = {
|
||||
body?: never
|
||||
path: {
|
||||
/**
|
||||
* Session ID
|
||||
*/
|
||||
id: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/session/{id}/todo"
|
||||
}
|
||||
|
||||
export type SessionTodoResponses = {
|
||||
/**
|
||||
* Todo list
|
||||
*/
|
||||
200: Array<Todo>
|
||||
}
|
||||
|
||||
export type SessionTodoResponse = SessionTodoResponses[keyof SessionTodoResponses]
|
||||
|
||||
export type SessionInitData = {
|
||||
body?: {
|
||||
messageID: string
|
||||
providerID: string
|
||||
modelID: string
|
||||
providerID: string
|
||||
messageID: string
|
||||
}
|
||||
path: {
|
||||
/**
|
||||
@@ -1467,6 +1518,28 @@ export type SessionInitResponses = {
|
||||
|
||||
export type SessionInitResponse = SessionInitResponses[keyof SessionInitResponses]
|
||||
|
||||
export type SessionForkData = {
|
||||
body?: {
|
||||
messageID?: string
|
||||
}
|
||||
path: {
|
||||
id: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/session/{id}/fork"
|
||||
}
|
||||
|
||||
export type SessionForkResponses = {
|
||||
/**
|
||||
* 200
|
||||
*/
|
||||
200: Session
|
||||
}
|
||||
|
||||
export type SessionForkResponse = SessionForkResponses[keyof SessionForkResponses]
|
||||
|
||||
export type SessionAbortData = {
|
||||
body?: never
|
||||
path: {
|
||||
@@ -1997,6 +2070,22 @@ export type AppAgentsResponses = {
|
||||
|
||||
export type AppAgentsResponse = AppAgentsResponses[keyof AppAgentsResponses]
|
||||
|
||||
export type McpStatusData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/mcp"
|
||||
}
|
||||
|
||||
export type McpStatusResponses = {
|
||||
/**
|
||||
* MCP server status
|
||||
*/
|
||||
200: unknown
|
||||
}
|
||||
|
||||
export type TuiAppendPromptData = {
|
||||
body?: {
|
||||
text: string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
@@ -31,7 +31,7 @@
|
||||
"sharp": "0.32.5",
|
||||
"shiki": "3.4.2",
|
||||
"solid-js": "catalog:",
|
||||
"toolbeam-docs-theme": "0.4.6"
|
||||
"toolbeam-docs-theme": "0.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"opencode": "workspace:*",
|
||||
|
||||
@@ -69,6 +69,23 @@ Your editor should be able to validate and autocomplete based on the schema.
|
||||
|
||||
---
|
||||
|
||||
### TUI
|
||||
|
||||
You can configure TUI-specific settings through the `tui` option.
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"tui": {
|
||||
"scroll_speed": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[Learn more about using the TUI here](/docs/tui).
|
||||
|
||||
---
|
||||
|
||||
### Models
|
||||
|
||||
You can configure the providers and models you want to use in your OpenCode config through the `provider`, `model` and `small_model` options.
|
||||
@@ -204,23 +221,6 @@ OpenCode will automatically download any new updates when it starts up. You can
|
||||
|
||||
---
|
||||
|
||||
### TUI
|
||||
|
||||
You can configure TUI-specific settings through the `tui` option.
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"tui": {
|
||||
"scroll_speed": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[Learn more about using the TUI here](/docs/tui).
|
||||
|
||||
---
|
||||
|
||||
### Formatters
|
||||
|
||||
You can configure code formatters through the `formatter` option.
|
||||
|
||||
@@ -111,5 +111,23 @@ For example.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration allows all commands by default (`"*": "allow"`) but requires approval for `git push` commands.
|
||||
|
||||
### Agents
|
||||
|
||||
Configure agent specific permissions
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"agent": {
|
||||
"plan": {
|
||||
"permission": {
|
||||
"bash": {
|
||||
"echo *": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -86,6 +86,18 @@ Here are some common issues and how to resolve them.
|
||||
2. Verify the model name in your config is correct
|
||||
3. Some models may require specific access or subscriptions
|
||||
|
||||
If you encounter `ProviderModelNotFoundError` you are most likely incorrectly
|
||||
referencing a model somewhere.
|
||||
Models should be referenced like so: `<providerId>/<modelId>`
|
||||
|
||||
|
||||
Examples:
|
||||
- `openai/gpt-4.1`
|
||||
- `openrouter/google/gemini-2.5-flash`
|
||||
- `opencode/kimi-k2`
|
||||
|
||||
To figure out what models you have access to, run `opencode models`
|
||||
|
||||
---
|
||||
|
||||
### ProviderInitError
|
||||
|
||||
@@ -73,6 +73,10 @@ You can also access our models through the following API endpoints.
|
||||
| Grok Code Fast 1 | grok-code | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2 | kimi-k2 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
The [model id](/docs/config/#models) in your OpenCode config
|
||||
uses the format `opencode/<model-id>`. For example, for GPT 5 Codex, you would
|
||||
use `opencode/gpt-5-codex` in your config.
|
||||
|
||||
---
|
||||
|
||||
## Pricing
|
||||
@@ -94,6 +98,8 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**.
|
||||
| GPT 5 | $1.25 | $10.00 | $0.125 | - |
|
||||
| GPT 5 Codex | $1.25 | $10.00 | $0.125 | - |
|
||||
|
||||
You might notice _Claude Haiku 3.5_ in your usage history. This is a [low cost model](/docs/config/#models) that's used to generate the titles of your sessions.
|
||||
|
||||
:::note
|
||||
Credit card fees are passed along at cost; we don't charge anything beyond that.
|
||||
:::
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
--color-border-weak: hsl(0, 1%, 85%);
|
||||
|
||||
--color-icon: hsl(0, 1%, 55%);
|
||||
|
||||
/* For the share component */
|
||||
--sl-color-bg-surface: var(--sl-color-bg-nav);
|
||||
--sl-color-divider: var(--sl-color-gray-5);
|
||||
}
|
||||
|
||||
|
||||
@@ -294,14 +298,16 @@ body > .page > header, :root[data-has-sidebar] body > .page > header {
|
||||
}
|
||||
|
||||
|
||||
.sl-container ul li a {
|
||||
nav.sidebar .sl-container ul li a,
|
||||
div.right-sidebar .sl-container ul li a {
|
||||
padding: 4px 24px !important;
|
||||
width: 100% !important;
|
||||
color: var(--color-text-weaker);
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
.sl-container ul li a:hover {
|
||||
nav.sidebar .sl-container ul li a:hover,
|
||||
div.right-sidebar .sl-container ul li a:hover {
|
||||
background: var(--color-background-weak);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -309,12 +315,14 @@ body > .page > header, :root[data-has-sidebar] body > .page > header {
|
||||
}
|
||||
}
|
||||
|
||||
.sl-container ul li ul li {
|
||||
nav.sidebar .sl-container ul li ul li,
|
||||
div.right-sidebar .sl-container ul li ul li {
|
||||
padding: 4px 12px 0 12px !important;
|
||||
}
|
||||
|
||||
|
||||
.sl-container ul li a[aria-current="true"] {
|
||||
nav.sidebar .sl-container ul li a[aria-current="true"],
|
||||
div.right-sidebar .sl-container ul li a[aria-current="true"] {
|
||||
color: var(--color-text-strong) !important;
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.6",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user