big format

This commit is contained in:
Dax Raad
2025-11-06 13:03:02 -05:00
parent 8729edc5e0
commit 1ea3a8eb9b
183 changed files with 2629 additions and 2497 deletions

View File

@@ -21,27 +21,27 @@
json
{
"recipient_name": "multi_tool_use.parallel",
"parameters": {
"tool_uses": [
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.tsx"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.ts"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.md"
}
}
]
}
"recipient_name": "multi_tool_use.parallel",
"parameters": {
"tool_uses": [
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.tsx"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.ts"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.md"
}
}
]
}
}

View File

@@ -1,15 +1,15 @@
{
"keep": {
"days": true,
"amount": 14
},
"auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json",
"files": [
{
"date": 1759827172859,
"name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log",
"hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16"
}
],
"hashType": "sha256"
"keep": {
"days": true,
"amount": 14
},
"auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json",
"files": [
{
"date": 1759827172859,
"name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log",
"hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16"
}
],
"hashType": "sha256"
}

View File

@@ -13,7 +13,10 @@ export function Faq(props: ParentProps & { question: string }) {
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M12.5 11.5H19V12.5H12.5V19H11.5V12.5H5V11.5H11.5V5H12.5V11.5Z" fill="currentColor" />
<path
d="M12.5 11.5H19V12.5H12.5V19H11.5V12.5H5V11.5H11.5V5H12.5V11.5Z"
fill="currentColor"
/>
</svg>
<svg
data-slot="faq-icon-minus"

View File

@@ -9,10 +9,23 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
<path d="M13.7124 9.14333V4.5719H18.2838V9.14333H13.7124Z" fill="currentColor" />
<path d="M13.7124 13.7136V9.14221H18.2838V13.7136H13.7124Z" fill="currentColor" />
<path d="M0 18.2857V13.7142H4.57143V18.2857H0Z" fill="currentColor" fill-opacity="0.2" />
<rect width="4.57143" height="4.57143" transform="translate(4.57178 13.7141)" fill="currentColor" />
<path d="M4.57178 18.2855V13.7141H9.14321V18.2855H4.57178Z" fill="currentColor" fill-opacity="0.2" />
<rect
width="4.57143"
height="4.57143"
transform="translate(4.57178 13.7141)"
fill="currentColor"
/>
<path
d="M4.57178 18.2855V13.7141H9.14321V18.2855H4.57178Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path d="M9.1438 18.2855V13.7141H13.7152V18.2855H9.1438Z" fill="currentColor" />
<path d="M13.7156 18.2855V13.7141H18.287V18.2855H13.7156Z" fill="currentColor" fill-opacity="0.2" />
<path
d="M13.7156 18.2855V13.7141H18.287V18.2855H13.7156Z"
fill="currentColor"
fill-opacity="0.2"
/>
<rect width="4.57143" height="4.57143" transform="translate(0 18.2859)" fill="currentColor" />
<path d="M0 22.8572V18.2858H4.57143V22.8572H0Z" fill="currentColor" fill-opacity="0.2" />
<rect
@@ -23,8 +36,16 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
fill-opacity="0.2"
/>
<path d="M4.57178 22.8573V18.2859H9.14321V22.8573H4.57178Z" fill="currentColor" />
<path d="M9.1438 22.8573V18.2859H13.7152V22.8573H9.1438Z" fill="currentColor" fill-opacity="0.2" />
<path d="M13.7156 22.8573V18.2859H18.287V22.8573H13.7156Z" fill="currentColor" fill-opacity="0.2" />
<path
d="M9.1438 22.8573V18.2859H13.7152V22.8573H9.1438Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path
d="M13.7156 22.8573V18.2859H18.287V22.8573H13.7156Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path d="M0 27.4292V22.8578H4.57143V27.4292H0Z" fill="currentColor" />
<path d="M4.57178 27.4292V22.8578H9.14321V27.4292H4.57178Z" fill="currentColor" />
<path d="M9.1438 27.4276V22.8562H13.7152V27.4276H9.1438Z" fill="currentColor" />
@@ -40,9 +61,21 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
<path d="M32.001 18.2855V13.7141H36.5724V18.2855H32.001Z" fill="currentColor" />
<path d="M36.5698 18.2855V13.7141H41.1413V18.2855H36.5698Z" fill="currentColor" />
<path d="M22.8572 22.8573V18.2859H27.4286V22.8573H22.8572Z" fill="currentColor" />
<path d="M27.4292 22.8573V18.2859H32.0006V22.8573H27.4292Z" fill="currentColor" fill-opacity="0.2" />
<path d="M32.001 22.8573V18.2859H36.5724V22.8573H32.001Z" fill="currentColor" fill-opacity="0.2" />
<path d="M36.5698 22.8573V18.2859H41.1413V22.8573H36.5698Z" fill="currentColor" fill-opacity="0.2" />
<path
d="M27.4292 22.8573V18.2859H32.0006V22.8573H27.4292Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path
d="M32.001 22.8573V18.2859H36.5724V22.8573H32.001Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path
d="M36.5698 22.8573V18.2859H41.1413V22.8573H36.5698Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path d="M22.8572 27.4292V22.8578H27.4286V27.4292H22.8572Z" fill="currentColor" />
<path d="M27.4292 27.4276V22.8562H32.0006V27.4276H27.4292Z" fill="currentColor" />
<path d="M32.001 27.4276V22.8562H36.5724V27.4276H32.001Z" fill="currentColor" />
@@ -53,16 +86,40 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
<path d="M45.7144 13.7136V9.14221H50.2858V13.7136H45.7144Z" fill="currentColor" />
<path d="M59.4299 13.7152V9.1438H64.0014V13.7152H59.4299Z" fill="currentColor" />
<path d="M45.7144 18.2855V13.7141H50.2858V18.2855H45.7144Z" fill="currentColor" />
<path d="M50.2861 18.2857V13.7142H54.8576V18.2857H50.2861Z" fill="currentColor" fill-opacity="0.2" />
<path d="M54.8579 18.2855V13.7141H59.4293V18.2855H54.8579Z" fill="currentColor" fill-opacity="0.2" />
<path
d="M50.2861 18.2857V13.7142H54.8576V18.2857H50.2861Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path
d="M54.8579 18.2855V13.7141H59.4293V18.2855H54.8579Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path d="M59.4299 18.2855V13.7141H64.0014V18.2855H59.4299Z" fill="currentColor" />
<path d="M45.7144 22.8573V18.2859H50.2858V22.8573H45.7144Z" fill="currentColor" />
<path d="M50.2861 22.8572V18.2858H54.8576V22.8572H50.2861Z" fill="currentColor" fill-opacity="0.2" />
<path d="M54.8579 22.8573V18.2859H59.4293V22.8573H54.8579Z" fill="currentColor" fill-opacity="0.2" />
<path
d="M50.2861 22.8572V18.2858H54.8576V22.8572H50.2861Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path
d="M54.8579 22.8573V18.2859H59.4293V22.8573H54.8579Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path d="M59.4299 22.8573V18.2859H64.0014V22.8573H59.4299Z" fill="currentColor" />
<path d="M45.7144 27.4292V22.8578H50.2858V27.4292H45.7144Z" fill="currentColor" />
<path d="M50.2861 27.4286V22.8572H54.8576V27.4286H50.2861Z" fill="currentColor" fill-opacity="0.2" />
<path d="M54.8579 27.4285V22.8571H59.4293V27.4285H54.8579Z" fill="currentColor" fill-opacity="0.2" />
<path
d="M50.2861 27.4286V22.8572H54.8576V27.4286H50.2861Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path
d="M54.8579 27.4285V22.8571H59.4293V27.4285H54.8579Z"
fill="currentColor"
fill-opacity="0.2"
/>
<path d="M59.4299 27.4292V22.8578H64.0014V27.4292H59.4299Z" fill="currentColor" />
</svg>
)
@@ -70,7 +127,14 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
export function IconCopy(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg {...props} width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
{...props}
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.75 8.75V2.75H21.25V15.25H15.25M15.25 8.75H2.75V21.25H15.25V8.75Z"
stroke="currentColor"
@@ -83,8 +147,20 @@ export function IconCopy(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
export function IconCheck(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg {...props} width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.75 15.0938L9 20.25L21.25 3.75" stroke="#03B000" stroke-width="2" stroke-linecap="square" />
<svg
{...props}
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.75 15.0938L9 20.25L21.25 3.75"
stroke="#03B000"
stroke-width="2"
stroke-linecap="square"
/>
</svg>
)
}
@@ -113,7 +189,14 @@ export function IconStripe(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
export function IconChevron(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg {...props} width="8" height="6" viewBox="0 0 8 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
{...props}
width="8"
height="6"
viewBox="0 0 8 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
d="M4.00024 5.04041L7.37401 1.66663L6.66691 0.959525L4.00024 3.62619L1.33357 0.959525L0.626465 1.66663L4.00024 5.04041Z"
@@ -124,7 +207,14 @@ export function IconChevron(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
export function IconWorkspaceLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg {...props} width="24" height="30" viewBox="0 0 24 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
{...props}
width="24"
height="30"
viewBox="0 0 24 30"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M18 6H6V24H18V6ZM24 30H0V0H24V30Z" fill="currentColor" />
</svg>
)
@@ -144,7 +234,10 @@ export function IconOpenAI(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
export function IconAnthropic(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M13.7891 3.93188L20.2223 20.068H23.7502L17.317 3.93188H13.7891Z" />
<path
fill="currentColor"
d="M13.7891 3.93188L20.2223 20.068H23.7502L17.317 3.93188H13.7891Z"
/>
<path
fill="currentColor"
d="M6.32538 13.6827L8.52662 8.01201L10.7279 13.6827H6.32538ZM6.68225 3.93188L0.25 20.068H3.84652L5.16202 16.6794H11.8914L13.2067 20.068H16.8033L10.371 3.93188H6.68225Z"

View File

@@ -2,6 +2,9 @@ import type { APIEvent } from "@solidjs/start/server"
import { AuthClient } from "~/context/auth"
export async function GET(input: APIEvent) {
const result = await AuthClient.authorize(new URL("./callback", input.request.url).toString(), "code")
const result = await AuthClient.authorize(
new URL("./callback", input.request.url).toString(),
"code",
)
return Response.redirect(result.url, 302)
}

View File

@@ -299,7 +299,6 @@
transition: all 0.2s ease;
text-decoration: none;
&:hover:not(:disabled) {
background: var(--color-background-strong-hover);
}
@@ -385,23 +384,21 @@
0 1px 2px -1px rgba(19, 16, 16, 0.12);
@media (max-width: 40rem) {
box-shadow:
0 0 0 1px rgba(19, 16, 16, 0.16)
box-shadow: 0 0 0 1px rgba(19, 16, 16, 0.16);
}
&:hover {
background: var(--color-background);
}
&:active {
transform: scale(0.98);
box-shadow: 0 0 0 1px rgba(19, 16, 16, 0.08), 0 6px 8px -8px rgba(19, 16, 16, 0.50);
box-shadow:
0 0 0 1px rgba(19, 16, 16, 0.08),
0 6px 8px -8px rgba(19, 16, 16, 0.5);
}
}
@media (max-width: 60rem) {
padding: 2rem 1.5rem;
}

View File

@@ -85,7 +85,10 @@ export function WorkspacePicker() {
<Dropdown trigger={currentWorkspace()} align="left">
<For each={workspaces()}>
{(workspace) => (
<DropdownItem selected={workspace.id === params.id} onClick={() => handleSelectWorkspace(workspace.id)}>
<DropdownItem
selected={workspace.id === params.id}
onClick={() => handleSelectWorkspace(workspace.id)}
>
{workspace.name || workspace.slug}
</DropdownItem>
)}
@@ -95,7 +98,11 @@ export function WorkspacePicker() {
</button>
</Dropdown>
<Modal open={store.showForm} onClose={() => setStore("showForm", false)} title="Create New Workspace">
<Modal
open={store.showForm}
onClose={() => setStore("showForm", false)}
title="Create New Workspace"
>
<form data-slot="create-form" action={createWorkspace} method="post">
<div data-slot="create-input-group">
<input

View File

@@ -89,7 +89,10 @@ export function PaymentSection() {
<td data-slot="payment-receipt">
<button
onClick={async () => {
const receiptUrl = await downloadReceiptAction(params.id, payment.paymentID!)
const receiptUrl = await downloadReceiptAction(
params.id,
payment.paymentID!,
)
if (receiptUrl) {
window.open(receiptUrl, "_blank")
}

View File

@@ -171,7 +171,6 @@
}
@media (max-width: 40rem) {
th,
td {
padding: var(--space-2) var(--space-3);
@@ -181,8 +180,7 @@
th {
&:nth-child(3)
/* Date */
{
/* Date */ {
display: none;
}
}
@@ -190,8 +188,7 @@
td {
&:nth-child(3)
/* Date */
{
/* Date */ {
display: none;
}
}

View File

@@ -146,14 +146,20 @@ export function KeySection() {
title="Copy API key"
>
<span>{key.keyDisplay}</span>
<Show when={copied()} fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}>
<Show
when={copied()}
fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}
>
<IconCheck style={{ width: "14px", height: "14px" }} />
</Show>
</button>
</Show>
</td>
<td data-slot="key-user-email">{key.email}</td>
<td data-slot="key-last-used" title={key.timeUsed ? formatDateUTC(key.timeUsed) : undefined}>
<td
data-slot="key-last-used"
title={key.timeUsed ? formatDateUTC(key.timeUsed) : undefined}
>
{key.timeUsed ? formatDateForTable(key.timeUsed) : "-"}
</td>
<td data-slot="key-actions">

View File

@@ -85,7 +85,12 @@ const updateMember = action(async (form: FormData) => {
)
}, "member.update")
function MemberRow(props: { member: any; workspaceID: string; actorID: string; actorRole: string }) {
function MemberRow(props: {
member: any
workspaceID: string
actorID: string
actorRole: string
}) {
const submission = useSubmission(updateMember)
const isCurrentUser = () => props.actorID === props.member.id
const isAdmin = () => props.actorRole === "admin"

View File

@@ -43,15 +43,24 @@ export function NewUserSection() {
<div data-component="feature-grid">
<div data-slot="feature">
<h3>Tested & Verified Models</h3>
<p>We've benchmarked and tested models specifically for coding agents to ensure the best performance.</p>
<p>
We've benchmarked and tested models specifically for coding agents to ensure the best
performance.
</p>
</div>
<div data-slot="feature">
<h3>Highest Quality</h3>
<p>Access models configured for optimal performance - no downgrades or routing to cheaper providers.</p>
<p>
Access models configured for optimal performance - no downgrades or routing to cheaper
providers.
</p>
</div>
<div data-slot="feature">
<h3>No Lock-in</h3>
<p>Use Zen with any coding agent, and continue using other providers with opencode whenever you want.</p>
<p>
Use Zen with any coding agent, and continue using other providers with opencode
whenever you want.
</p>
</div>
</div>

View File

@@ -128,7 +128,6 @@
}
@media (max-width: 40rem) {
th,
td {
padding: var(--space-2) var(--space-3);

View File

@@ -22,7 +22,9 @@ const removeProvider = action(async (form: FormData) => {
if (!provider) return { error: "Provider is required" }
const workspaceID = form.get("workspaceID")?.toString()
if (!workspaceID) return { error: "Workspace ID is required" }
return json(await withActor(() => Provider.remove({ provider }), workspaceID), { revalidate: listProviders.key })
return json(await withActor(() => Provider.remove({ provider }), workspaceID), {
revalidate: listProviders.key,
})
}, "provider.remove")
const saveProvider = action(async (form: FormData) => {
@@ -53,7 +55,10 @@ const listProviders = query(async (workspaceID: string) => {
function ProviderRow(props: { provider: Provider }) {
const params = useParams()
const providers = createAsync(() => listProviders(params.id))
const saveSubmission = useSubmission(saveProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key)
const saveSubmission = useSubmission(
saveProvider,
([fd]) => fd.get("provider")?.toString() === props.provider.key,
)
const removeSubmission = useSubmission(
removeProvider,
([fd]) => fd.get("provider")?.toString() === props.provider.key,
@@ -89,9 +94,16 @@ function ProviderRow(props: { provider: Provider }) {
<td data-slot="provider-key">
<Show
when={store.editing}
fallback={<span>{providerData() ? maskCredentials(providerData()!.credentials) : "-"}</span>}
fallback={
<span>{providerData() ? maskCredentials(providerData()!.credentials) : "-"}</span>
}
>
<form id={`provider-form-${props.provider.key}`} action={saveProvider} method="post" data-slot="edit-form">
<form
id={`provider-form-${props.provider.key}`}
action={saveProvider}
method="post"
data-slot="edit-form"
>
<div data-slot="input-wrapper">
<input
ref={(r) => (input = r)}

View File

@@ -31,7 +31,7 @@
margin: 0;
}
>button {
> button {
align-self: flex-start;
}
}
@@ -80,7 +80,7 @@
}
}
>button[type="reset"] {
> button[type="reset"] {
align-self: flex-start;
}

View File

@@ -28,7 +28,10 @@ export default function Home() {
createAsync(() => checkLoggedIn())
return (
<main data-page="zen">
<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />
<HttpHeader
name="Cache-Control"
value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400"
/>
<Title>OpenCode Zen | A curated set of reliable optimized models for coding agents</Title>
<Link rel="icon" type="image/svg+xml" href="/favicon-zen.svg" />
<Meta property="og:image" content="/social-share-zen.png" />
@@ -44,13 +47,19 @@ export default function Home() {
<img data-slot="zen logo dark" src={zenLogoDark} alt="zen logo dark" />
<strong>Reliable optimized models for coding agents</strong>
<p>
Zen gives you access to a curated set of AI models that OpenCode has tested and benchmarked specifically
for coding agents. No need to worry about inconsistent performance and quality, use validated models
that work.
Zen gives you access to a curated set of AI models that OpenCode has tested and
benchmarked specifically for coding agents. No need to worry about inconsistent
performance and quality, use validated models that work.
</p>
<div data-slot="model-logos">
<div>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<mask
id="mask0_79_128586"
style="mask-type:luminance"
@@ -71,8 +80,17 @@ export default function Home() {
</svg>
</div>
<div>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.7891 3.93164L20.2223 20.0677H23.7502L17.317 3.93164H13.7891Z" fill="currentColor" />
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M13.7891 3.93164L20.2223 20.0677H23.7502L17.317 3.93164H13.7891Z"
fill="currentColor"
/>
<path
d="M6.32538 13.6824L8.52662 8.01177L10.7279 13.6824H6.32538ZM6.68225 3.93164L0.25 20.0677H3.84652L5.16202 16.6791H11.8914L13.2067 20.0677H16.8033L10.371 3.93164H6.68225Z"
fill="currentColor"
@@ -80,7 +98,13 @@ export default function Home() {
</svg>
</div>
<div>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.16861 16.0529L17.2018 9.85156C17.5957 9.54755 18.1586 9.66612 18.3463 10.1384C19.3339 12.6288 18.8926 15.6217 16.9276 17.6766C14.9626 19.7314 12.2285 20.1821 9.72948 19.1557L6.9995 20.4775C10.9151 23.2763 15.6699 22.5841 18.6411 19.4749C20.9979 17.0103 21.7278 13.6508 21.0453 10.6214L21.0515 10.6278C20.0617 6.17736 21.2948 4.39847 23.8207 0.760904C23.8804 0.674655 23.9402 0.588405 24 0.5L20.6762 3.97585V3.96506L9.16658 16.0551"
fill="currentColor"
@@ -92,7 +116,13 @@ export default function Home() {
</svg>
</div>
<div>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
@@ -102,7 +132,13 @@ export default function Home() {
</svg>
</div>
<div>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.6241 11.346L20.3848 3.44816C20.5309 3.29931 20.4487 3 20.2601 3H16.0842C16.0388 3 15.9949 3.01897 15.9594 3.05541L7.59764 11.5629C7.46721 11.6944 7.27446 11.5771 7.27446 11.3666V3.25183C7.27446 3.11242 7.18515 3 7.07594 3H4.19843C4.08932 3 4 3.11242 4 3.25183V20.7482C4 20.8876 4.08932 21 4.19843 21H7.07594C7.18515 21 7.27446 20.8876 7.27446 20.7482V17.1834C7.27446 17.1073 7.30136 17.0344 7.34815 16.987L9.94075 14.3486C10.0031 14.2853 10.0895 14.2757 10.159 14.3232L17.0934 19.5573C18.2289 20.3412 19.4975 20.8226 20.786 20.9652C20.9008 20.9778 21 20.8606 21 20.7133V17.3559C21 17.2276 20.9249 17.1232 20.8243 17.1073C20.0659 16.9853 19.326 16.6845 18.6569 16.222L12.6538 11.764C12.5291 11.6785 12.5135 11.4584 12.6241 11.346Z"
fill="currentColor"
@@ -112,7 +148,13 @@ export default function Home() {
</div>
<a href="/auth">
<span>Get started with Zen </span>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.5 12L17 12M13 16.5L17.5 12L13 7.5"
stroke="currentColor"
@@ -124,14 +166,23 @@ export default function Home() {
</div>
<div data-slot="pricing-copy">
<p>
<strong>Add $20 Pay as you go balance</strong> <span>(+$1.23 card processing fee)</span>
<strong>Add $20 Pay as you go balance</strong>{" "}
<span>(+$1.23 card processing fee)</span>
</p>
<p>Use with any agent. Set monthly spend limits. Cancel any time.</p>
</div>
</section>
<section data-component="comparison">
<video src={compareVideo} autoplay playsinline loop muted preload="auto" poster={compareVideoPoster}>
<video
src={compareVideo}
autoplay
playsinline
loop
muted
preload="auto"
poster={compareVideoPoster}
>
Your browser does not support the video tag.
</video>
</section>
@@ -140,8 +191,8 @@ export default function Home() {
<div data-slot="section-title">
<h3>What problem is Zen solving?</h3>
<p>
There are so many models available, but only a few work well with coding agents. Most providers
configure them differently with varying results.
There are so many models available, but only a few work well with coding agents.
Most providers configure them differently with varying results.
</p>
</div>
<p>We're fixing this for everyone, not just OpenCode users.</p>
@@ -176,14 +227,15 @@ export default function Home() {
<li>
<span>[2]</span>
<div>
<strong>Use Zen with transparent pricing</strong> - <a href="/docs/zen/#pricing">pay per request</a>{" "}
with zero markups
<strong>Use Zen with transparent pricing</strong> -{" "}
<a href="/docs/zen/#pricing">pay per request</a> with zero markups
</div>
</li>
<li>
<span>[3]</span>
<div>
<strong>Auto-top up</strong> - when your balance reaches $5 well automatically add $20
<strong>Auto-top up</strong> - when your balance reaches $5 well automatically
add $20
</div>
</li>
</ul>
@@ -195,8 +247,9 @@ export default function Home() {
<div>
<span>[*]</span>
<p>
All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data
for model training, with the <a href="/docs/zen/#privacy">following exceptions</a>.
All Zen models are hosted in the US. Providers follow a zero-retention policy and
do not use your data for model training, with the{" "}
<a href="/docs/zen/#privacy">following exceptions</a>.
</p>
</div>
</div>
@@ -251,7 +304,8 @@ export default function Home() {
<span>ex-Head of Design, Laravel</span>
</div>
<div data-slot="quote">
With <span>@OpenCode</span> Zen I know all the models are tested and perfect for coding agents.
With <span>@OpenCode</span> Zen I know all the models are tested and perfect for
coding agents.
</div>
</div>
</a>
@@ -275,38 +329,44 @@ export default function Home() {
<ul>
<li>
<Faq question="What is OpenCode Zen?">
Zen is a curated set of AI models tested and benchmarked for coding agents created by the team behind
OpenCode.
Zen is a curated set of AI models tested and benchmarked for coding agents created
by the team behind OpenCode.
</Faq>
</li>
<li>
<Faq question="What makes Zen more accurate?">
Zen only provides models that have been specifically tested and benchmarked for coding agents. You
wouldnt use a butter knife to cut steak, dont use poor models for coding.
Zen only provides models that have been specifically tested and benchmarked for
coding agents. You wouldnt use a butter knife to cut steak, dont use poor models
for coding.
</Faq>
</li>
<li>
<Faq question="Is Zen cheaper?">
Zen is not for profit. Zen passes through the costs from the model providers to you. The higher Zens
usage the more OpenCode can negotiate better rates and pass those to you.
Zen is not for profit. Zen passes through the costs from the model providers to
you. The higher Zens usage the more OpenCode can negotiate better rates and pass
those to you.
</Faq>
</li>
<li>
<Faq question="How much does Zen cost?">
Zen <a href="/docs/zen/#pricing">charges per request</a> with zero markups, so you pay exactly what
the model provider charges. Your total cost depends on usage, and you can set monthly spend limits in
your <a href="/auth">account</a>. To cover costs, OpenCode adds only a small payment processing fee of
$1.23 per $20 balance top-up.
Zen <a href="/docs/zen/#pricing">charges per request</a> with zero markups, so you
pay exactly what the model provider charges. Your total cost depends on usage, and
you can set monthly spend limits in your <a href="/auth">account</a>. To cover
costs, OpenCode adds only a small payment processing fee of $1.23 per $20 balance
top-up.
</Faq>
</li>
<li>
<Faq question="What about data and privacy?">
All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data
for model training, with the <a href="/docs/zen/#privacy">following exceptions</a>.
All Zen models are hosted in the US. Providers follow a zero-retention policy and
do not use your data for model training, with the{" "}
<a href="/docs/zen/#privacy">following exceptions</a>.
</Faq>
</li>
<li>
<Faq question="Can I set spend limits?">Yes, you can set monthly spending limits in your account.</Faq>
<Faq question="Can I set spend limits?">
Yes, you can set monthly spending limits in your account.
</Faq>
</li>
<li>
<Faq question="Can I cancel?">
@@ -315,8 +375,8 @@ export default function Home() {
</li>
<li>
<Faq question="Can I use Zen with other coding agents?">
While Zen works great with OpenCode, you can use Zen with any agent. Follow the setup instructions in
your preferred coding agent.
While Zen works great with OpenCode, you can use Zen with any agent. Follow the
setup instructions in your preferred coding agent.
</Faq>
</li>
</ul>

View File

@@ -98,7 +98,10 @@ export function fromAnthropicRequest(body: any): CommonRequest {
typeof (src as any).media_type === "string" &&
typeof (src as any).data === "string"
)
return { type: "image_url", image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` } }
return {
type: "image_url",
image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` },
}
return undefined
}
@@ -120,12 +123,15 @@ export function fromAnthropicRequest(body: any): CommonRequest {
if ((p as any).type === "tool_result") {
const id = (p as any).tool_use_id
const content =
typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content)
typeof (p as any).content === "string"
? (p as any).content
: JSON.stringify((p as any).content)
msgs.push({ role: "tool", tool_call_id: id, content })
}
}
if (partsOut.length > 0) {
if (partsOut.length === 1 && partsOut[0].type === "text") msgs.push({ role: "user", content: partsOut[0].text })
if (partsOut.length === 1 && partsOut[0].type === "text")
msgs.push({ role: "user", content: partsOut[0].text })
else msgs.push({ role: "user", content: partsOut })
}
continue
@@ -137,7 +143,8 @@ export function fromAnthropicRequest(body: any): CommonRequest {
const tcs: any[] = []
for (const p of partsIn) {
if (!p || !(p as any).type) continue
if ((p as any).type === "text" && typeof (p as any).text === "string") texts.push((p as any).text)
if ((p as any).type === "text" && typeof (p as any).text === "string")
texts.push((p as any).text)
if ((p as any).type === "tool_use") {
const name = (p as any).name
const id = (p as any).id
@@ -165,7 +172,11 @@ export function fromAnthropicRequest(body: any): CommonRequest {
.filter((t: any) => t && typeof t === "object" && "input_schema" in t)
.map((t: any) => ({
type: "function",
function: { name: (t as any).name, description: (t as any).description, parameters: (t as any).input_schema },
function: {
name: (t as any).name,
description: (t as any).description,
parameters: (t as any).input_schema,
},
}))
: undefined
@@ -203,7 +214,9 @@ export function fromAnthropicRequest(body: any): CommonRequest {
export function toAnthropicRequest(body: CommonRequest) {
if (!body || typeof body !== "object") return body
const sysIn = Array.isArray(body.messages) ? body.messages.filter((m: any) => m && m.role === "system") : []
const sysIn = Array.isArray(body.messages)
? body.messages.filter((m: any) => m && m.role === "system")
: []
let ccCount = 0
const cc = () => {
ccCount++
@@ -354,7 +367,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse {
const idIn = (resp as any).id
const id =
typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}`
typeof idIn === "string"
? idIn.replace(/^msg_/, "chatcmpl_")
: `chatcmpl_${Math.random().toString(36).slice(2)}`
const model = (resp as any).model
const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : []
@@ -397,7 +412,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse {
const ct = typeof (u as any).output_tokens === "number" ? (u as any).output_tokens : undefined
const total = pt != null && ct != null ? pt + ct : undefined
const cached =
typeof (u as any).cache_read_input_tokens === "number" ? (u as any).cache_read_input_tokens : undefined
typeof (u as any).cache_read_input_tokens === "number"
? (u as any).cache_read_input_tokens
: undefined
const details = cached != null ? { cached_tokens: cached } : undefined
return {
prompt_tokens: pt,
@@ -452,7 +469,12 @@ export function toAnthropicResponse(resp: CommonResponse) {
} catch {
input = (tc as any).function.arguments
}
content.push({ type: "tool_use", id: (tc as any).id, name: (tc as any).function.name, input })
content.push({
type: "tool_use",
id: (tc as any).id,
name: (tc as any).function.name,
input,
})
}
}
}
@@ -511,13 +533,22 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
if (json.type === "content_block_start") {
const cb = json.content_block
if (cb?.type === "text") {
out.choices.push({ index: json.index ?? 0, delta: { role: "assistant", content: "" }, finish_reason: null })
out.choices.push({
index: json.index ?? 0,
delta: { role: "assistant", content: "" },
finish_reason: null,
})
} else if (cb?.type === "tool_use") {
out.choices.push({
index: json.index ?? 0,
delta: {
tool_calls: [
{ index: json.index ?? 0, id: cb.id, type: "function", function: { name: cb.name, arguments: "" } },
{
index: json.index ?? 0,
id: cb.id,
type: "function",
function: { name: cb.name, arguments: "" },
},
],
},
finish_reason: null,
@@ -532,7 +563,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
} else if (d?.type === "input_json_delta") {
out.choices.push({
index: json.index ?? 0,
delta: { tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }] },
delta: {
tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }],
},
finish_reason: null,
})
}
@@ -558,7 +591,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
prompt_tokens: u.input_tokens,
completion_tokens: u.output_tokens,
total_tokens: (u.input_tokens || 0) + (u.output_tokens || 0),
...(u.cache_read_input_tokens ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } : {}),
...(u.cache_read_input_tokens
? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } }
: {}),
}
}

View File

@@ -57,7 +57,8 @@ export const oaCompatHelper = {
const inputTokens = usage.prompt_tokens ?? 0
const outputTokens = usage.completion_tokens ?? 0
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? undefined
const cacheReadTokens = usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined
const cacheReadTokens =
usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined
return {
inputTokens: inputTokens - (cacheReadTokens ?? 0),
outputTokens,
@@ -79,7 +80,8 @@ export function fromOaCompatibleRequest(body: any): CommonRequest {
if (!m || !m.role) continue
if (m.role === "system") {
if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content })
if (typeof m.content === "string" && m.content.length > 0)
msgsOut.push({ role: "system", content: m.content })
continue
}
@@ -90,10 +92,12 @@ export function fromOaCompatibleRequest(body: any): CommonRequest {
const parts: any[] = []
for (const p of m.content) {
if (!p || !p.type) continue
if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text })
if (p.type === "text" && typeof p.text === "string")
parts.push({ type: "text", text: p.text })
if (p.type === "image_url") parts.push({ type: "image_url", image_url: p.image_url })
}
if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text })
if (parts.length === 1 && parts[0].type === "text")
msgsOut.push({ role: "user", content: parts[0].text })
else if (parts.length > 0) msgsOut.push({ role: "user", content: parts })
}
continue
@@ -137,7 +141,8 @@ export function toOaCompatibleRequest(body: CommonRequest) {
if (p.type === "image_url" && p.image_url) return { type: "image_url", image_url: p.image_url }
const s = (p as any).source
if (!s || typeof s !== "object") return undefined
if (s.type === "url" && typeof s.url === "string") return { type: "image_url", image_url: { url: s.url } }
if (s.type === "url" && typeof s.url === "string")
return { type: "image_url", image_url: { url: s.url } }
if (s.type === "base64" && typeof s.media_type === "string" && typeof s.data === "string")
return { type: "image_url", image_url: { url: `data:${s.media_type};base64,${s.data}` } }
return undefined
@@ -147,7 +152,8 @@ export function toOaCompatibleRequest(body: CommonRequest) {
if (!m || !m.role) continue
if (m.role === "system") {
if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content })
if (typeof m.content === "string" && m.content.length > 0)
msgsOut.push({ role: "system", content: m.content })
continue
}
@@ -160,11 +166,13 @@ export function toOaCompatibleRequest(body: CommonRequest) {
const parts: any[] = []
for (const p of m.content) {
if (!p || !p.type) continue
if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text })
if (p.type === "text" && typeof p.text === "string")
parts.push({ type: "text", text: p.text })
const ip = toImg(p)
if (ip) parts.push(ip)
}
if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text })
if (parts.length === 1 && parts[0].type === "text")
msgsOut.push({ role: "user", content: parts[0].text })
else if (parts.length > 0) msgsOut.push({ role: "user", content: parts })
}
continue
@@ -317,7 +325,9 @@ export function toOaCompatibleResponse(resp: CommonResponse) {
const idIn = (resp as any).id
const id =
typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}`
typeof idIn === "string"
? idIn.replace(/^msg_/, "chatcmpl_")
: `chatcmpl_${Math.random().toString(36).slice(2)}`
const model = (resp as any).model
const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : []
@@ -359,7 +369,8 @@ export function toOaCompatibleResponse(resp: CommonResponse) {
const pt = typeof u.input_tokens === "number" ? u.input_tokens : undefined
const ct = typeof u.output_tokens === "number" ? u.output_tokens : undefined
const total = pt != null && ct != null ? pt + ct : undefined
const cached = typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined
const cached =
typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined
const details = cached != null ? { cached_tokens: cached } : undefined
return {
prompt_tokens: pt,
@@ -532,7 +543,9 @@ export function toOaCompatibleChunk(chunk: CommonChunk): string {
total_tokens: chunk.usage.total_tokens,
...(chunk.usage.prompt_tokens_details?.cached_tokens
? {
prompt_tokens_details: { cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens },
prompt_tokens_details: {
cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens,
},
}
: {}),
}

View File

@@ -77,13 +77,20 @@ export function fromOpenaiRequest(body: any): CommonRequest {
typeof (s as any).media_type === "string" &&
typeof (s as any).data === "string"
)
return { type: "image_url", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } }
return {
type: "image_url",
image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
}
return undefined
}
const msgs: any[] = []
const inMsgs = Array.isArray(body.input) ? body.input : Array.isArray(body.messages) ? body.messages : []
const inMsgs = Array.isArray(body.input)
? body.input
: Array.isArray(body.messages)
? body.messages
: []
for (const m of inMsgs) {
if (!m) continue
@@ -96,7 +103,9 @@ export function fromOpenaiRequest(body: any): CommonRequest {
const args = typeof a === "string" ? a : JSON.stringify(a ?? {})
msgs.push({
role: "assistant",
tool_calls: [{ id: (m as any).id, type: "function", function: { name, arguments: args } }],
tool_calls: [
{ id: (m as any).id, type: "function", function: { name, arguments: args } },
],
})
}
if ((m as any).type === "function_call_output") {
@@ -113,7 +122,8 @@ export function fromOpenaiRequest(body: any): CommonRequest {
if (typeof c === "string" && c.length > 0) msgs.push({ role: "system", content: c })
if (Array.isArray(c)) {
const t = c.find((p: any) => p && typeof p.text === "string")
if (t && typeof t.text === "string" && t.text.length > 0) msgs.push({ role: "system", content: t.text })
if (t && typeof t.text === "string" && t.text.length > 0)
msgs.push({ role: "system", content: t.text })
}
continue
}
@@ -126,18 +136,24 @@ export function fromOpenaiRequest(body: any): CommonRequest {
const parts: any[] = []
for (const p of c) {
if (!p || !(p as any).type) continue
if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string")
if (
((p as any).type === "text" || (p as any).type === "input_text") &&
typeof (p as any).text === "string"
)
parts.push({ type: "text", text: (p as any).text })
const ip = toImg(p)
if (ip) parts.push(ip)
if ((p as any).type === "tool_result") {
const id = (p as any).tool_call_id
const content =
typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content)
typeof (p as any).content === "string"
? (p as any).content
: JSON.stringify((p as any).content)
msgs.push({ role: "tool", tool_call_id: id, content })
}
}
if (parts.length === 1 && parts[0].type === "text") msgs.push({ role: "user", content: parts[0].text })
if (parts.length === 1 && parts[0].type === "text")
msgs.push({ role: "user", content: parts[0].text })
else if (parts.length > 0) msgs.push({ role: "user", content: parts })
}
continue
@@ -153,7 +169,11 @@ export function fromOpenaiRequest(body: any): CommonRequest {
}
if ((m as any).role === "tool") {
msgs.push({ role: "tool", tool_call_id: (m as any).tool_call_id, content: (m as any).content })
msgs.push({
role: "tool",
tool_call_id: (m as any).tool_call_id,
content: (m as any).content,
})
continue
}
}
@@ -210,7 +230,10 @@ export function toOpenaiRequest(body: CommonRequest) {
typeof (s as any).media_type === "string" &&
typeof (s as any).data === "string"
)
return { type: "input_image", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } }
return {
type: "input_image",
image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
}
return undefined
}
@@ -257,7 +280,10 @@ export function toOpenaiRequest(body: CommonRequest) {
}
if ((m as any).role === "tool") {
const out = typeof (m as any).content === "string" ? (m as any).content : JSON.stringify((m as any).content)
const out =
typeof (m as any).content === "string"
? (m as any).content
: JSON.stringify((m as any).content)
input.push({ type: "function_call_output", call_id: (m as any).tool_call_id, output: out })
continue
}
@@ -325,7 +351,9 @@ export function fromOpenaiResponse(resp: any): CommonResponse {
const idIn = (r as any).id
const id =
typeof idIn === "string" ? idIn.replace(/^resp_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}`
typeof idIn === "string"
? idIn.replace(/^resp_/, "chatcmpl_")
: `chatcmpl_${Math.random().toString(36).slice(2)}`
const model = (r as any).model ?? (resp as any).model
const out = Array.isArray((r as any).output) ? (r as any).output : []
@@ -452,7 +480,9 @@ export function toOpenaiResponse(resp: CommonResponse) {
})()
return {
id: (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? `resp_${Math.random().toString(36).slice(2)}`,
id:
(resp as any).id?.replace(/^chatcmpl_/, "resp_") ??
`resp_${Math.random().toString(36).slice(2)}`,
object: "response",
model: (resp as any).model,
output: outputItems,
@@ -498,7 +528,9 @@ export function fromOpenaiChunk(chunk: string): CommonChunk | string {
if (typeof name === "string" && name.length > 0) {
out.choices.push({
index: 0,
delta: { tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }] },
delta: {
tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }],
},
finish_reason: null,
})
}
@@ -555,7 +587,12 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
const model = chunk.model
if (d.content) {
const data = { id, type: "response.output_text.delta", delta: d.content, response: { id, model } }
const data = {
id,
type: "response.output_text.delta",
delta: d.content,
response: { id, model },
}
return `event: response.output_text.delta\ndata: ${JSON.stringify(data)}`
}
@@ -565,7 +602,13 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
const data = {
type: "response.output_item.added",
output_index: 0,
item: { id: tc.id, type: "function_call", name: tc.function.name, call_id: tc.id, arguments: "" },
item: {
id: tc.id,
type: "function_call",
name: tc.function.name,
call_id: tc.id,
arguments: "",
},
}
return `event: response.output_item.added\ndata: ${JSON.stringify(data)}`
}
@@ -593,7 +636,11 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
}
: undefined
const data: any = { id, type: "response.completed", response: { id, model, ...(usage ? { usage } : {}) } }
const data: any = {
id,
type: "response.completed",
response: { id, model, ...(usage ? { usage } : {}) },
}
return `event: response.completed\ndata: ${JSON.stringify(data)}`
}

View File

@@ -15,6 +15,7 @@ body {
--font-size-9xl: 8rem;
--font-mono:
"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--font-sans: var(--font-mono);
}

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,17 +473,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -504,10 +486,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -599,10 +578,7 @@
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
@@ -610,10 +586,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -670,9 +643,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -680,9 +651,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,17 +473,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -504,10 +486,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -599,10 +578,7 @@
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
@@ -610,10 +586,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -670,9 +643,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -680,9 +651,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,17 +473,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -504,10 +486,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -592,10 +571,7 @@
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
@@ -603,10 +579,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -663,9 +636,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -673,9 +644,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,17 +473,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -504,10 +486,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -599,10 +578,7 @@
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
@@ -610,10 +586,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -670,9 +643,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -680,9 +651,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,17 +473,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -504,10 +486,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -613,18 +592,12 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
@@ -632,10 +605,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -692,9 +662,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -702,9 +670,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,17 +473,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -504,10 +486,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -613,32 +592,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -646,10 +615,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -706,9 +672,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -716,9 +680,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,17 +473,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -504,10 +486,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -599,32 +578,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -632,10 +601,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -692,9 +658,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -702,9 +666,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -493,17 +480,12 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
@@ -511,10 +493,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -606,32 +585,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -639,10 +608,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -699,9 +665,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -709,9 +673,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -493,9 +480,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -503,10 +488,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -598,32 +580,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -631,10 +603,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -691,9 +660,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -701,9 +668,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -479,9 +466,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -489,10 +474,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -584,32 +566,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -617,10 +589,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -677,9 +646,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -687,9 +654,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -479,9 +466,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -489,10 +474,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -584,32 +566,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -617,10 +589,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -677,9 +646,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -687,9 +654,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -479,9 +466,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -489,10 +474,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -605,32 +587,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -638,10 +610,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -698,9 +667,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -708,9 +675,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -479,9 +466,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -489,10 +474,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -549,10 +531,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -560,10 +539,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -676,32 +652,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -709,10 +675,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -769,9 +732,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -779,9 +740,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -398,10 +388,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -479,9 +466,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -489,10 +474,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -549,10 +531,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -560,10 +539,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -627,10 +603,7 @@
"indexes": {
"workspace_provider": {
"name": "workspace_provider",
"columns": [
"workspace_id",
"provider"
],
"columns": ["workspace_id", "provider"],
"isUnique": true
}
},
@@ -638,10 +611,7 @@
"compositePrimaryKeys": {
"provider_workspace_id_id_pk": {
"name": "provider_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -754,32 +724,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -787,10 +747,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -847,9 +804,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -857,9 +812,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -180,9 +178,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -190,10 +186,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -280,10 +273,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -405,10 +395,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -486,9 +473,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -496,10 +481,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -556,10 +538,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -567,10 +546,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -634,10 +610,7 @@
"indexes": {
"workspace_provider": {
"name": "workspace_provider",
"columns": [
"workspace_id",
"provider"
],
"columns": ["workspace_id", "provider"],
"isUnique": true
}
},
@@ -645,10 +618,7 @@
"compositePrimaryKeys": {
"provider_workspace_id_id_pk": {
"name": "provider_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -761,32 +731,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -794,10 +754,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -854,9 +811,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -864,9 +819,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -187,9 +185,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -197,10 +193,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -287,10 +280,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -412,10 +402,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -493,9 +480,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -503,10 +488,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -563,10 +545,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -574,10 +553,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -641,10 +617,7 @@
"indexes": {
"workspace_provider": {
"name": "workspace_provider",
"columns": [
"workspace_id",
"provider"
],
"columns": ["workspace_id", "provider"],
"isUnique": true
}
},
@@ -652,10 +625,7 @@
"compositePrimaryKeys": {
"provider_workspace_id_id_pk": {
"name": "provider_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -768,32 +738,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -801,10 +761,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -861,9 +818,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -871,9 +826,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -117,10 +115,7 @@
"indexes": {
"provider": {
"name": "provider",
"columns": [
"provider",
"subject"
],
"columns": ["provider", "subject"],
"isUnique": true
}
},
@@ -257,9 +252,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -267,10 +260,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -357,10 +347,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -482,10 +469,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -563,9 +547,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -573,10 +555,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -633,10 +612,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -644,10 +620,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -711,10 +684,7 @@
"indexes": {
"workspace_provider": {
"name": "workspace_provider",
"columns": [
"workspace_id",
"provider"
],
"columns": ["workspace_id", "provider"],
"isUnique": true
}
},
@@ -722,10 +692,7 @@
"compositePrimaryKeys": {
"provider_workspace_id_id_pk": {
"name": "provider_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -838,32 +805,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -871,10 +828,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -931,9 +885,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -941,9 +893,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -102,17 +102,12 @@
"indexes": {
"provider": {
"name": "provider",
"columns": [
"provider",
"subject"
],
"columns": ["provider", "subject"],
"isUnique": true
},
"account_id": {
"name": "account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
}
},
@@ -249,9 +244,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -259,10 +252,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -349,10 +339,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -474,10 +461,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -555,9 +539,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -565,10 +547,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -625,10 +604,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -636,10 +612,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -703,10 +676,7 @@
"indexes": {
"workspace_provider": {
"name": "workspace_provider",
"columns": [
"workspace_id",
"provider"
],
"columns": ["workspace_id", "provider"],
"isUnique": true
}
},
@@ -714,10 +684,7 @@
"compositePrimaryKeys": {
"provider_workspace_id_id_pk": {
"name": "provider_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -830,32 +797,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -863,10 +820,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -923,9 +877,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -933,9 +885,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -43,9 +43,7 @@
"compositePrimaryKeys": {
"account_id_pk": {
"name": "account_id_pk",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},
@@ -109,17 +107,12 @@
"indexes": {
"provider": {
"name": "provider",
"columns": [
"provider",
"subject"
],
"columns": ["provider", "subject"],
"isUnique": true
},
"account_id": {
"name": "account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
}
},
@@ -127,9 +120,7 @@
"compositePrimaryKeys": {
"auth_id_pk": {
"name": "auth_id_pk",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},
@@ -263,9 +254,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -273,10 +262,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -363,10 +349,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -488,10 +471,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -569,9 +549,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -579,10 +557,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -639,10 +614,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -650,10 +622,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -717,10 +686,7 @@
"indexes": {
"workspace_provider": {
"name": "workspace_provider",
"columns": [
"workspace_id",
"provider"
],
"columns": ["workspace_id", "provider"],
"isUnique": true
}
},
@@ -728,10 +694,7 @@
"compositePrimaryKeys": {
"provider_workspace_id_id_pk": {
"name": "provider_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -844,32 +807,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -877,10 +830,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -937,9 +887,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -947,9 +895,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -43,9 +43,7 @@
"compositePrimaryKeys": {
"account_id_pk": {
"name": "account_id_pk",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},
@@ -109,17 +107,12 @@
"indexes": {
"provider": {
"name": "provider",
"columns": [
"provider",
"subject"
],
"columns": ["provider", "subject"],
"isUnique": true
},
"account_id": {
"name": "account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
}
},
@@ -127,9 +120,7 @@
"compositePrimaryKeys": {
"auth_id_pk": {
"name": "auth_id_pk",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},
@@ -277,9 +268,7 @@
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"columns": ["customer_id"],
"isUnique": true
}
},
@@ -287,10 +276,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -377,10 +363,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -502,10 +485,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -583,9 +563,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -593,10 +571,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -653,10 +628,7 @@
"indexes": {
"model_workspace_model": {
"name": "model_workspace_model",
"columns": [
"workspace_id",
"model"
],
"columns": ["workspace_id", "model"],
"isUnique": true
}
},
@@ -664,10 +636,7 @@
"compositePrimaryKeys": {
"model_workspace_id_id_pk": {
"name": "model_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -731,10 +700,7 @@
"indexes": {
"workspace_provider": {
"name": "workspace_provider",
"columns": [
"workspace_id",
"provider"
],
"columns": ["workspace_id", "provider"],
"isUnique": true
}
},
@@ -742,10 +708,7 @@
"compositePrimaryKeys": {
"provider_workspace_id_id_pk": {
"name": "provider_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -858,32 +821,22 @@
"indexes": {
"user_account_id": {
"name": "user_account_id",
"columns": [
"workspace_id",
"account_id"
],
"columns": ["workspace_id", "account_id"],
"isUnique": true
},
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
},
"global_account_id": {
"name": "global_account_id",
"columns": [
"account_id"
],
"columns": ["account_id"],
"isUnique": false
},
"global_email": {
"name": "global_email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": false
}
},
@@ -891,10 +844,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -951,9 +901,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -961,9 +909,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},

View File

@@ -24,37 +24,40 @@ export namespace AWS {
body: z.string(),
}),
async (input) => {
const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", {
method: "POST",
headers: {
"X-Amz-Target": "SES.SendEmail",
"Content-Type": "application/json",
},
body: JSON.stringify({
FromEmailAddress: `OpenCode Zen <contact@anoma.ly>`,
Destination: {
ToAddresses: [input.to],
const res = await createClient().fetch(
"https://email.us-east-1.amazonaws.com/v2/email/outbound-emails",
{
method: "POST",
headers: {
"X-Amz-Target": "SES.SendEmail",
"Content-Type": "application/json",
},
Content: {
Simple: {
Subject: {
Charset: "UTF-8",
Data: input.subject,
},
Body: {
Text: {
body: JSON.stringify({
FromEmailAddress: `OpenCode Zen <contact@anoma.ly>`,
Destination: {
ToAddresses: [input.to],
},
Content: {
Simple: {
Subject: {
Charset: "UTF-8",
Data: input.body,
Data: input.subject,
},
Html: {
Charset: "UTF-8",
Data: input.body,
Body: {
Text: {
Charset: "UTF-8",
Data: input.body,
},
Html: {
Charset: "UTF-8",
Data: input.body,
},
},
},
},
},
}),
})
}),
},
)
if (!res.ok) {
throw new Error(`Failed to send email: ${res.statusText}`)
}

View File

@@ -5,7 +5,10 @@ import { Client } from "@planetscale/database"
import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core"
import type { ExtractTablesWithRelations } from "drizzle-orm"
import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless"
import type {
PlanetScalePreparedQueryHKT,
PlanetscaleQueryResultHKT,
} from "drizzle-orm/planetscale-serverless"
import { Context } from "../context"
import { memo } from "../util/memo"
@@ -67,7 +70,10 @@ export namespace Database {
}
}
export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: MySqlTransactionConfig) {
export async function transaction<T>(
callback: (tx: TxOrDb) => Promise<T>,
config?: MySqlTransactionConfig,
) {
try {
const { tx } = TransactionContext.use()
return callback(tx)

View File

@@ -20,8 +20,14 @@ export namespace Key {
email: AuthTable.subject,
})
.from(KeyTable)
.innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)))
.innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")))
.innerJoin(
UserTable,
and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)),
)
.innerJoin(
AuthTable,
and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")),
)
.where(
and(
...[

View File

@@ -11,7 +11,9 @@ export namespace Provider {
tx
.select()
.from(ProviderTable)
.where(and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted))),
.where(
and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted)),
),
),
)
@@ -50,7 +52,12 @@ export namespace Provider {
return Database.transaction((tx) =>
tx
.delete(ProviderTable)
.where(and(eq(ProviderTable.provider, provider), eq(ProviderTable.workspaceID, Actor.workspace()))),
.where(
and(
eq(ProviderTable.provider, provider),
eq(ProviderTable.workspaceID, Actor.workspace()),
),
),
)
},
)

View File

@@ -1,4 +1,11 @@
import { index, mysqlEnum, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
import {
index,
mysqlEnum,
mysqlTable,
primaryKey,
uniqueIndex,
varchar,
} from "drizzle-orm/mysql-core"
import { id, timestamps, ulid } from "../drizzle/types"
export const AuthProvider = ["email", "github", "google"] as const

View File

@@ -9,5 +9,8 @@ export const ModelTable = mysqlTable(
...timestamps,
model: varchar("model", { length: 64 }).notNull(),
},
(table) => [...workspaceIndexes(table), uniqueIndex("model_workspace_model").on(table.workspaceID, table.model)],
(table) => [
...workspaceIndexes(table),
uniqueIndex("model_workspace_model").on(table.workspaceID, table.model),
],
)

View File

@@ -10,5 +10,8 @@ export const ProviderTable = mysqlTable(
provider: varchar("provider", { length: 64 }).notNull(),
credentials: text("credentials").notNull(),
},
(table) => [...workspaceIndexes(table), uniqueIndex("workspace_provider").on(table.workspaceID, table.provider)],
(table) => [
...workspaceIndexes(table),
uniqueIndex("workspace_provider").on(table.workspaceID, table.provider),
],
)

View File

@@ -1,4 +1,12 @@
import { mysqlTable, uniqueIndex, varchar, int, mysqlEnum, index, bigint } 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"

View File

@@ -26,7 +26,10 @@ export namespace User {
authEmail: AuthTable.subject,
})
.from(UserTable)
.leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")))
.leftJoin(
AuthTable,
and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")),
)
.where(and(eq(UserTable.workspaceID, Actor.workspace()), isNull(UserTable.timeDeleted))),
),
)
@@ -36,7 +39,13 @@ export namespace User {
tx
.select()
.from(UserTable)
.where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id), isNull(UserTable.timeDeleted)))
.where(
and(
eq(UserTable.workspaceID, Actor.workspace()),
eq(UserTable.id, id),
isNull(UserTable.timeDeleted),
),
)
.then((rows) => rows[0]),
),
)
@@ -48,7 +57,10 @@ export namespace User {
email: AuthTable.subject,
})
.from(UserTable)
.leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")))
.leftJoin(
AuthTable,
and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")),
)
.where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id)))
.then((rows) => rows[0]?.email),
),
@@ -130,10 +142,16 @@ export namespace User {
workspaceName: WorkspaceTable.name,
})
.from(UserTable)
.innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")))
.innerJoin(
AuthTable,
and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")),
)
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, workspaceID))
.where(
and(eq(UserTable.workspaceID, workspaceID), eq(UserTable.id, Actor.assert("user").properties.userID)),
and(
eq(UserTable.workspaceID, workspaceID),
eq(UserTable.id, Actor.assert("user").properties.userID),
),
)
.then((rows) => rows[0]),
)

View File

@@ -6,97 +6,97 @@
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
Console: {
type: "sst.cloudflare.SolidStart"
url: string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
}
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
Web: {
type: "sst.cloudflare.Astro"
url: string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
import * as cloudflare from "@cloudflare/workers-types"
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
}
}

View File

@@ -6,97 +6,97 @@
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
Console: {
type: "sst.cloudflare.SolidStart"
url: string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
}
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
Web: {
type: "sst.cloudflare.Astro"
url: string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
import * as cloudflare from "@cloudflare/workers-types"
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
}
}

View File

@@ -1,6 +1,18 @@
// @ts-nocheck
import React from "react"
import { Img, Row, Html, Link, Body, Head, Button, Column, Preview, Section, Container } from "@jsx-email/all"
import {
Img,
Row,
Html,
Link,
Body,
Head,
Button,
Column,
Preview,
Section,
Container,
} from "@jsx-email/all"
import { Text, Fonts, Title, A, Span } from "../components"
import {
unit,
@@ -52,8 +64,8 @@ export const InviteEmail = ({
<Section style={{ padding: `${unit * 2}px 0 0 0` }}>
<Text style={headingText}>Join your team's OpenCode workspace</Text>
<Text style={contentText}>
You have been invited by <Span style={contentHighlightText}>{inviter}</Span> to join the{" "}
<Span style={contentHighlightText}>{workspaceName}</Span> workspace on OpenCode.
You have been invited by <Span style={contentHighlightText}>{inviter}</Span> to join
the <Span style={contentHighlightText}>{workspaceName}</Span> workspace on OpenCode.
</Text>
</Section>
@@ -61,7 +73,12 @@ export const InviteEmail = ({
<Button style={button} href={url}>
<Text style={buttonText}>
Join workspace
<Img width="24" height="24" src={`${assetsUrl}/right-arrow.png`} alt="Arrow right" />
<Img
width="24"
height="24"
src={`${assetsUrl}/right-arrow.png`}
alt="Arrow right"
/>
</Text>
</Button>
</Section>

View File

@@ -6,97 +6,97 @@
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
Console: {
type: "sst.cloudflare.SolidStart"
url: string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
}
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
Web: {
type: "sst.cloudflare.Astro"
url: string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
import * as cloudflare from "@cloudflare/workers-types"
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
}
}

View File

@@ -2,9 +2,7 @@
/* tslint:disable */
/* eslint-disable */
/// <reference types="vite/client" />
interface ImportMetaEnv {
}
interface ImportMetaEnv {}
interface ImportMeta {
readonly env: ImportMetaEnv
}

View File

@@ -268,7 +268,11 @@ export default new Hono<{ Bindings: Env }>()
// Verify permissions
const userClient = new Octokit({ auth: token })
const { data: repoData } = await userClient.repos.get({ owner, repo })
if (!repoData.permissions.admin && !repoData.permissions.push && !repoData.permissions.maintain)
if (
!repoData.permissions.admin &&
!repoData.permissions.push &&
!repoData.permissions.maintain
)
throw new Error("User does not have write permissions")
// Get installation token

View File

@@ -6,97 +6,97 @@
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
Console: {
type: "sst.cloudflare.SolidStart"
url: string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
}
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
Web: {
type: "sst.cloudflare.Astro"
url: string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
import * as cloudflare from "@cloudflare/workers-types"
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
}
}

View File

@@ -77,7 +77,8 @@ async function regenerateWindowsCmdWrappers() {
// npm_config_global is string | undefined
// if it exists, the value is true
const isGlobal = process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules"))
const isGlobal =
process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules"))
// The npm rebuild command does 2 things - Execute lifecycle scripts and rebuild bin links
// We want to skip lifecycle scripts to avoid infinite loops, so we use --ignore-scripts
@@ -93,7 +94,9 @@ async function regenerateWindowsCmdWrappers() {
console.log("Successfully rebuilt npm bin links")
} catch (error) {
console.error("Error rebuilding npm links:", error.message)
console.error("npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts")
console.error(
"npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts",
)
}
}

View File

@@ -19,12 +19,23 @@ const result = z.toJSONSchema(Config.Info, {
const schema = ctx.jsonSchema
// Preserve strictness: set additionalProperties: false for objects
if (schema && typeof schema === "object" && schema.type === "object" && schema.additionalProperties === undefined) {
if (
schema &&
typeof schema === "object" &&
schema.type === "object" &&
schema.additionalProperties === undefined
) {
schema.additionalProperties = false
}
// Add examples and default descriptions for string fields with defaults
if (schema && typeof schema === "object" && "type" in schema && schema.type === "string" && schema?.default) {
if (
schema &&
typeof schema === "object" &&
"type" in schema &&
schema.type === "string" &&
schema?.default
) {
if (!schema.examples) {
schema.examples = [schema.default]
}

View File

@@ -143,7 +143,18 @@ export namespace Agent {
tools: {},
builtIn: false,
}
const { name, model, prompt, tools, description, temperature, top_p, mode, permission, ...extra } = value
const {
name,
model,
prompt,
tools,
description,
temperature,
top_p,
mode,
permission,
...extra
} = value
item.options = {
...item.options,
...extra,
@@ -212,7 +223,10 @@ export namespace Agent {
}
}
function mergeAgentPermissions(basePermission: any, overridePermission: any): Agent.Info["permission"] {
function mergeAgentPermissions(
basePermission: any,
overridePermission: any,
): Agent.Info["permission"] {
if (typeof basePermission.bash === "string") {
basePermission.bash = {
"*": basePermission.bash,

View File

@@ -19,7 +19,10 @@ export namespace Bus {
const registry = new Map<string, EventDefinition>()
export function event<Type extends string, Properties extends ZodType>(type: Type, properties: Properties) {
export function event<Type extends string, Properties extends ZodType>(
type: Type,
properties: Properties,
) {
const result = {
type,
properties,
@@ -70,7 +73,10 @@ export namespace Bus {
export function subscribe<Definition extends EventDefinition>(
def: Definition,
callback: (event: { type: Definition["type"]; properties: z.infer<Definition["properties"]> }) => void,
callback: (event: {
type: Definition["type"]
properties: z.infer<Definition["properties"]>
}) => void,
) {
return raw(def.type, callback)
}

View File

@@ -14,7 +14,11 @@ export const AuthCommand = cmd({
command: "auth",
describe: "manage credentials",
builder: (yargs) =>
yargs.command(AuthLoginCommand).command(AuthLogoutCommand).command(AuthListCommand).demandCommand(),
yargs
.command(AuthLoginCommand)
.command(AuthLogoutCommand)
.command(AuthListCommand)
.demandCommand(),
async handler() {},
})
@@ -60,7 +64,9 @@ export const AuthListCommand = cmd({
prompts.log.info(`${provider} ${UI.Style.TEXT_DIM}${envVar}`)
}
prompts.outro(`${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s"))
prompts.outro(
`${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s"),
)
}
},
})
@@ -80,7 +86,9 @@ export const AuthLoginCommand = cmd({
UI.empty()
prompts.intro("Add credential")
if (args.url) {
const wellknown = await fetch(`${args.url}/.well-known/opencode`).then((x) => x.json() as any)
const wellknown = await fetch(`${args.url}/.well-known/opencode`).then(
(x) => x.json() as any,
)
prompts.log.info(`Running \`${wellknown.auth.command.join(" ")}\``)
const proc = Bun.spawn({
cmd: wellknown.auth.command,
@@ -102,223 +110,224 @@ export const AuthLoginCommand = cmd({
prompts.outro("Done")
return
}
await ModelsDev.refresh().catch(() => {})
const providers = await ModelsDev.get()
const priority: Record<string, number> = {
opencode: 0,
anthropic: 1,
"github-copilot": 2,
openai: 3,
google: 4,
openrouter: 5,
vercel: 6,
}
let provider = await prompts.autocomplete({
message: "Select provider",
maxItems: 8,
options: [
...pipe(
providers,
values(),
sortBy(
(x) => priority[x.id] ?? 99,
(x) => x.name ?? x.id,
),
map((x) => ({
label: x.name,
value: x.id,
hint: priority[x.id] <= 1 ? "recommended" : undefined,
})),
),
{
value: "other",
label: "Other",
},
],
})
if (prompts.isCancel(provider)) throw new UI.CancelledError()
const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider))
if (plugin && plugin.auth) {
let index = 0
if (plugin.auth.methods.length > 1) {
const method = await prompts.select({
message: "Login method",
options: [
...plugin.auth.methods.map((x, index) => ({
label: x.label,
value: index.toString(),
await ModelsDev.refresh().catch(() => {})
const providers = await ModelsDev.get()
const priority: Record<string, number> = {
opencode: 0,
anthropic: 1,
"github-copilot": 2,
openai: 3,
google: 4,
openrouter: 5,
vercel: 6,
}
let provider = await prompts.autocomplete({
message: "Select provider",
maxItems: 8,
options: [
...pipe(
providers,
values(),
sortBy(
(x) => priority[x.id] ?? 99,
(x) => x.name ?? x.id,
),
map((x) => ({
label: x.name,
value: x.id,
hint: priority[x.id] <= 1 ? "recommended" : undefined,
})),
],
})
if (prompts.isCancel(method)) throw new UI.CancelledError()
index = parseInt(method)
}
const method = plugin.auth.methods[index]
),
{
value: "other",
label: "Other",
},
],
})
// Handle prompts for all auth types
await new Promise((resolve) => setTimeout(resolve, 10))
const inputs: Record<string, string> = {}
if (method.prompts) {
for (const prompt of method.prompts) {
if (prompt.condition && !prompt.condition(inputs)) {
continue
}
if (prompt.type === "select") {
const value = await prompts.select({
message: prompt.message,
options: prompt.options,
})
if (prompts.isCancel(value)) throw new UI.CancelledError()
inputs[prompt.key] = value
} else {
const value = await prompts.text({
message: prompt.message,
placeholder: prompt.placeholder,
validate: prompt.validate ? (v) => prompt.validate!(v ?? "") : undefined,
})
if (prompts.isCancel(value)) throw new UI.CancelledError()
inputs[prompt.key] = value
}
}
}
if (prompts.isCancel(provider)) throw new UI.CancelledError()
if (method.type === "oauth") {
const authorize = await method.authorize(inputs)
if (authorize.url) {
prompts.log.info("Go to: " + authorize.url)
}
if (authorize.method === "auto") {
if (authorize.instructions) {
prompts.log.info(authorize.instructions)
}
const spinner = prompts.spinner()
spinner.start("Waiting for authorization...")
const result = await authorize.callback()
if (result.type === "failed") {
spinner.stop("Failed to authorize", 1)
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
if ("refresh" in result) {
const { type: _, provider: __, refresh, access, expires, ...extraFields } = result
await Auth.set(saveProvider, {
type: "oauth",
refresh,
access,
expires,
...extraFields,
})
}
if ("key" in result) {
await Auth.set(saveProvider, {
type: "api",
key: result.key,
})
}
spinner.stop("Login successful")
}
}
if (authorize.method === "code") {
const code = await prompts.text({
message: "Paste the authorization code here: ",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider))
if (plugin && plugin.auth) {
let index = 0
if (plugin.auth.methods.length > 1) {
const method = await prompts.select({
message: "Login method",
options: [
...plugin.auth.methods.map((x, index) => ({
label: x.label,
value: index.toString(),
})),
],
})
if (prompts.isCancel(code)) throw new UI.CancelledError()
const result = await authorize.callback(code)
if (result.type === "failed") {
prompts.log.error("Failed to authorize")
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
if ("refresh" in result) {
const { type: _, provider: __, refresh, access, expires, ...extraFields } = result
await Auth.set(saveProvider, {
type: "oauth",
refresh,
access,
expires,
...extraFields,
})
if (prompts.isCancel(method)) throw new UI.CancelledError()
index = parseInt(method)
}
const method = plugin.auth.methods[index]
// Handle prompts for all auth types
await new Promise((resolve) => setTimeout(resolve, 10))
const inputs: Record<string, string> = {}
if (method.prompts) {
for (const prompt of method.prompts) {
if (prompt.condition && !prompt.condition(inputs)) {
continue
}
if ("key" in result) {
if (prompt.type === "select") {
const value = await prompts.select({
message: prompt.message,
options: prompt.options,
})
if (prompts.isCancel(value)) throw new UI.CancelledError()
inputs[prompt.key] = value
} else {
const value = await prompts.text({
message: prompt.message,
placeholder: prompt.placeholder,
validate: prompt.validate ? (v) => prompt.validate!(v ?? "") : undefined,
})
if (prompts.isCancel(value)) throw new UI.CancelledError()
inputs[prompt.key] = value
}
}
}
if (method.type === "oauth") {
const authorize = await method.authorize(inputs)
if (authorize.url) {
prompts.log.info("Go to: " + authorize.url)
}
if (authorize.method === "auto") {
if (authorize.instructions) {
prompts.log.info(authorize.instructions)
}
const spinner = prompts.spinner()
spinner.start("Waiting for authorization...")
const result = await authorize.callback()
if (result.type === "failed") {
spinner.stop("Failed to authorize", 1)
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
if ("refresh" in result) {
const { type: _, provider: __, refresh, access, expires, ...extraFields } = result
await Auth.set(saveProvider, {
type: "oauth",
refresh,
access,
expires,
...extraFields,
})
}
if ("key" in result) {
await Auth.set(saveProvider, {
type: "api",
key: result.key,
})
}
spinner.stop("Login successful")
}
}
if (authorize.method === "code") {
const code = await prompts.text({
message: "Paste the authorization code here: ",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
})
if (prompts.isCancel(code)) throw new UI.CancelledError()
const result = await authorize.callback(code)
if (result.type === "failed") {
prompts.log.error("Failed to authorize")
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
if ("refresh" in result) {
const { type: _, provider: __, refresh, access, expires, ...extraFields } = result
await Auth.set(saveProvider, {
type: "oauth",
refresh,
access,
expires,
...extraFields,
})
}
if ("key" in result) {
await Auth.set(saveProvider, {
type: "api",
key: result.key,
})
}
prompts.log.success("Login successful")
}
}
prompts.outro("Done")
return
}
if (method.type === "api") {
if (method.authorize) {
const result = await method.authorize(inputs)
if (result.type === "failed") {
prompts.log.error("Failed to authorize")
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
await Auth.set(saveProvider, {
type: "api",
key: result.key,
})
prompts.log.success("Login successful")
}
prompts.log.success("Login successful")
prompts.outro("Done")
return
}
}
}
if (provider === "other") {
provider = await prompts.text({
message: "Enter provider id",
validate: (x) =>
x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only",
})
if (prompts.isCancel(provider)) throw new UI.CancelledError()
provider = provider.replace(/^@ai-sdk\//, "")
if (prompts.isCancel(provider)) throw new UI.CancelledError()
prompts.log.warn(
`This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`,
)
}
if (provider === "amazon-bedrock") {
prompts.log.info(
"Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID",
)
prompts.outro("Done")
return
}
if (method.type === "api") {
if (method.authorize) {
const result = await method.authorize(inputs)
if (result.type === "failed") {
prompts.log.error("Failed to authorize")
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
await Auth.set(saveProvider, {
type: "api",
key: result.key,
})
prompts.log.success("Login successful")
}
prompts.outro("Done")
return
}
if (provider === "opencode") {
prompts.log.info("Create an api key at https://opencode.ai/auth")
}
}
if (provider === "other") {
provider = await prompts.text({
message: "Enter provider id",
validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"),
if (provider === "vercel") {
prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token")
}
const key = await prompts.password({
message: "Enter your API key",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
})
if (prompts.isCancel(key)) throw new UI.CancelledError()
await Auth.set(provider, {
type: "api",
key,
})
if (prompts.isCancel(provider)) throw new UI.CancelledError()
provider = provider.replace(/^@ai-sdk\//, "")
if (prompts.isCancel(provider)) throw new UI.CancelledError()
prompts.log.warn(
`This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`,
)
}
if (provider === "amazon-bedrock") {
prompts.log.info(
"Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID",
)
prompts.outro("Done")
return
}
if (provider === "opencode") {
prompts.log.info("Create an api key at https://opencode.ai/auth")
}
if (provider === "vercel") {
prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token")
}
const key = await prompts.password({
message: "Enter your API key",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
})
if (prompts.isCancel(key)) throw new UI.CancelledError()
await Auth.set(provider, {
type: "api",
key,
})
prompts.outro("Done")
},
})
},

View File

@@ -7,7 +7,11 @@ import { EOL } from "os"
export const LSPCommand = cmd({
command: "lsp",
builder: (yargs) =>
yargs.command(DiagnosticsCommand).command(SymbolsCommand).command(DocumentSymbolsCommand).demandCommand(),
yargs
.command(DiagnosticsCommand)
.command(SymbolsCommand)
.command(DocumentSymbolsCommand)
.demandCommand(),
async handler() {},
})

View File

@@ -6,7 +6,8 @@ import { cmd } from "../cmd"
export const RipgrepCommand = cmd({
command: "rg",
builder: (yargs) => yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(),
builder: (yargs) =>
yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(),
async handler() {},
})
@@ -18,7 +19,9 @@ const TreeCommand = cmd({
}),
async handler(args) {
await bootstrap(process.cwd(), async () => {
process.stdout.write(await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit }) + EOL)
process.stdout.write(
(await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit })) + EOL,
)
})
},
})

View File

@@ -4,7 +4,8 @@ import { cmd } from "../cmd"
export const SnapshotCommand = cmd({
command: "snapshot",
builder: (yargs) => yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(),
builder: (yargs) =>
yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(),
async handler() {},
})

View File

@@ -189,7 +189,9 @@ export const GithubInstallCommand = cmd({
async function getAppInfo() {
const project = Instance.project
if (project.vcs !== "git") {
prompts.log.error(`Could not find git repository. Please run this command from a git repository.`)
prompts.log.error(
`Could not find git repository. Please run this command from a git repository.`,
)
throw new UI.CancelledError()
}
@@ -202,9 +204,13 @@ export const GithubInstallCommand = cmd({
// ie. git@github.com:sst/opencode
// ie. ssh://git@github.com/sst/opencode.git
// ie. ssh://git@github.com/sst/opencode
const parsed = info.match(/^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/)
const parsed = info.match(
/^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/,
)
if (!parsed) {
prompts.log.error(`Could not find git repository. Please run this command from a git repository.`)
prompts.log.error(
`Could not find git repository. Please run this command from a git repository.`,
)
throw new UI.CancelledError()
}
const [, owner, repo] = parsed
@@ -445,7 +451,9 @@ export const GithubRunCommand = cmd({
const summary = await summarize(response)
await pushToLocalBranch(summary)
}
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`))
const hasShared = prData.comments.nodes.some((c) =>
c.body.includes(`${shareBaseUrl}/s/${shareId}`),
)
await updateComment(`${response}${footer({ image: !hasShared })}`)
}
// Fork PR
@@ -457,7 +465,9 @@ export const GithubRunCommand = cmd({
const summary = await summarize(response)
await pushToForkBranch(summary, prData)
}
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`))
const hasShared = prData.comments.nodes.some((c) =>
c.body.includes(`${shareBaseUrl}/s/${shareId}`),
)
await updateComment(`${response}${footer({ image: !hasShared })}`)
}
}
@@ -547,8 +557,12 @@ export const GithubRunCommand = cmd({
// ie. <img alt="Image" src="https://github.com/user-attachments/assets/xxxx" />
// ie. [api.json](https://github.com/user-attachments/files/21433810/api.json)
// ie. ![Image](https://github.com/user-attachments/assets/xxxx)
const mdMatches = prompt.matchAll(/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi)
const tagMatches = prompt.matchAll(/<img .*?src="(https:\/\/github\.com\/user-attachments\/[^"]+)" \/>/gi)
const mdMatches = prompt.matchAll(
/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi,
)
const tagMatches = prompt.matchAll(
/<img .*?src="(https:\/\/github\.com\/user-attachments\/[^"]+)" \/>/gi,
)
const matches = [...mdMatches, ...tagMatches].sort((a, b) => a.index - b.index)
console.log("Images", JSON.stringify(matches, null, 2))
@@ -573,7 +587,10 @@ export const GithubRunCommand = cmd({
// Replace img tag with file path, ie. @image.png
const replacement = `@${filename}`
prompt = prompt.slice(0, start + offset) + replacement + prompt.slice(start + offset + tag.length)
prompt =
prompt.slice(0, start + offset) +
replacement +
prompt.slice(start + offset + tag.length)
offset += replacement.length - tag.length
const contentType = res.headers.get("content-type")
@@ -856,7 +873,8 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
throw new Error(`Failed to check permissions for user ${actor}: ${error}`)
}
if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`)
if (!["admin", "write"].includes(permission))
throw new Error(`User ${actor} does not have write permissions`)
}
async function createComment() {
@@ -904,7 +922,9 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
return `<a href="${shareBaseUrl}/s/${shareId}"><img width="200" alt="${titleAlt}" src="https://social-cards.sst.dev/opencode-share/${title64}.png?model=${providerID}/${modelID}&version=${session.version}&id=${shareId}" /></a>\n`
})()
const shareUrl = shareId ? `[opencode session](${shareBaseUrl}/s/${shareId})&nbsp;&nbsp;|&nbsp;&nbsp;` : ""
const shareUrl = shareId
? `[opencode session](${shareBaseUrl}/s/${shareId})&nbsp;&nbsp;|&nbsp;&nbsp;`
: ""
return `\n\n${image}${shareUrl}[github run](${runUrl})`
}
@@ -1080,9 +1100,13 @@ query($owner: String!, $repo: String!, $number: Int!) {
})
.map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`)
const files = (pr.files.nodes || []).map((f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`)
const files = (pr.files.nodes || []).map(
(f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`,
)
const reviewData = (pr.reviews.nodes || []).map((r) => {
const comments = (r.comments.nodes || []).map((c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`)
const comments = (r.comments.nodes || []).map(
(c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`,
)
return [
`- ${r.author.login} at ${r.submittedAt}:`,
` - Review body: ${r.body}`,
@@ -1104,9 +1128,15 @@ query($owner: String!, $repo: String!, $number: Int!) {
`Deletions: ${pr.deletions}`,
`Total Commits: ${pr.commits.totalCount}`,
`Changed Files: ${pr.files.nodes.length} files`,
...(comments.length > 0 ? ["<pull_request_comments>", ...comments, "</pull_request_comments>"] : []),
...(files.length > 0 ? ["<pull_request_changed_files>", ...files, "</pull_request_changed_files>"] : []),
...(reviewData.length > 0 ? ["<pull_request_reviews>", ...reviewData, "</pull_request_reviews>"] : []),
...(comments.length > 0
? ["<pull_request_comments>", ...comments, "</pull_request_comments>"]
: []),
...(files.length > 0
? ["<pull_request_changed_files>", ...files, "</pull_request_changed_files>"]
: []),
...(reviewData.length > 0
? ["<pull_request_reviews>", ...reviewData, "</pull_request_reviews>"]
: []),
"</pull_request>",
].join("\n")
}

View File

@@ -137,7 +137,9 @@ export const RunCommand = cmd({
const outputJsonEvent = (type: string, data: any) => {
if (args.format === "json") {
process.stdout.write(JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL)
process.stdout.write(
JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL,
)
return true
}
return false
@@ -157,7 +159,9 @@ export const RunCommand = cmd({
const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD]
const title =
part.state.title ||
(Object.keys(part.state.input).length > 0 ? JSON.stringify(part.state.input) : "Unknown")
(Object.keys(part.state.input).length > 0
? JSON.stringify(part.state.input)
: "Unknown")
printEvent(color, tool, title)
if (part.tool === "bash" && part.state.output?.trim()) {
UI.println()
@@ -280,7 +284,10 @@ export const RunCommand = cmd({
}
const cfgResult = await sdk.config.get()
if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) {
if (
cfgResult.data &&
(cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)
) {
const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => {
if (error instanceof Error && error.message.includes("disabled")) {
UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message)
@@ -333,7 +340,10 @@ export const RunCommand = cmd({
}
const cfgResult = await sdk.config.get()
if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) {
if (
cfgResult.data &&
(cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)
) {
const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => {
if (error instanceof Error && error.message.includes("disabled")) {
UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message)

View File

@@ -52,7 +52,11 @@ export function DialogModel() {
description: provider.name,
category: provider.name,
})),
filter((x) => Boolean(ref()?.filter) || !local.model.recent().find((y) => isDeepEqual(y, x.value))),
filter(
(x) =>
Boolean(ref()?.filter) ||
!local.model.recent().find((y) => isDeepEqual(y, x.value)),
),
),
),
),

View File

@@ -21,7 +21,7 @@ export function DialogSessionList() {
const deleteKeybind = "ctrl+d"
const currentSessionID = createMemo(() =>
route.data.type === "session" ? route.data.sessionID : undefined
route.data.type === "session" ? route.data.sessionID : undefined,
)
const options = createMemo(() => {

View File

@@ -3,9 +3,19 @@ import { TextAttributes } from "@opentui/core"
import { For } from "solid-js"
import { useTheme } from "@tui/context/theme"
const LOGO_LEFT = [` `, `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, `█░░█ █░░█ █▀▀▀ █░░█`, `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`]
const LOGO_LEFT = [
` `,
`█▀▀█ █▀▀█ █▀▀█ █▀▀▄`,
`█░░█ █░░█ █▀▀▀ █░░█`,
`▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`,
]
const LOGO_RIGHT = [``, `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, `█░░░ █░░█ █░░█ █▀▀▀`, `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`]
const LOGO_RIGHT = [
``,
`█▀▀▀ █▀▀█ █▀▀█ █▀▀█`,
`█░░░ █░░█ █░░█ █▀▀▀`,
`▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`,
]
export function Logo() {
const { theme } = useTheme()

View File

@@ -17,13 +17,11 @@ export const { use: useRoute, provider: RouteProvider } = createSimpleContext({
init: (props: { data?: Route }) => {
const [store, setStore] = createStore<Route>(
props.data ??
(
process.env["OPENCODE_ROUTE"]
(process.env["OPENCODE_ROUTE"]
? JSON.parse(process.env["OPENCODE_ROUTE"])
: {
type: "home",
}
),
type: "home",
}),
)
return {

View File

@@ -269,6 +269,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
},
async sync(sessionID: string) {
const now = Date.now()
console.log("syncing", sessionID)
const [session, messages, todo, diff] = await Promise.all([
sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }),
sdk.client.session.messages({ path: { id: sessionID } }),

View File

@@ -7,7 +7,9 @@ import { useRoute } from "@tui/context/route"
export function DialogMessage(props: { messageID: string; sessionID: string }) {
const sync = useSync()
const sdk = useSDK()
const message = createMemo(() => sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID))
const message = createMemo(() =>
sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID),
)
const route = useRoute()
return (

View File

@@ -19,7 +19,9 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s
const result = [] as DialogSelectOption<string>[]
for (const message of messages) {
if (message.role !== "user") continue
const part = (sync.data.part[message.id] ?? []).find((x) => x.type === "text" && !x.synthetic) as TextPart
const part = (sync.data.part[message.id] ?? []).find(
(x) => x.type === "text" && !x.synthetic,
) as TextPart
if (!part) continue
result.push({
title: part.text.replace(/\n/g, " "),
@@ -33,5 +35,11 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s
return result
})
return <DialogSelect onMove={(option) => props.onMove(option.value)} title="Timeline" options={options()} />
return (
<DialogSelect
onMove={(option) => props.onMove(option.value)}
title="Timeline"
options={options()}
/>
)
}

View File

@@ -105,14 +105,15 @@ export function Session() {
const sidebarVisible = createMemo(() => sidebar() === "show" || (sidebar() === "auto" && wide()))
const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4)
createEffect(() => {
sync.session.sync(route.sessionID).catch(() => {
createEffect(async () => {
await sync.session.sync(route.sessionID).catch(() => {
toast.show({
message: `Session not found: ${route.sessionID}`,
variant: "error",
})
return navigate({ type: "home" })
})
scroll.scrollBy(100_000)
})
const toast = useToast()

View File

@@ -41,7 +41,12 @@ export const TuiSpawnCommand = cmd({
)
cwd = new URL("../../../../", import.meta.url).pathname
} else cmd.push(process.execPath)
cmd.push("attach", server.url.toString(), "--dir", args.project ? path.resolve(args.project) : process.cwd())
cmd.push(
"attach",
server.url.toString(),
"--dir",
args.project ? path.resolve(args.project) : process.cwd(),
)
const proc = Bun.spawn({
cmd,
cwd,

View File

@@ -53,7 +53,9 @@ export function DialogConfirm(props: DialogConfirmProps) {
dialog.clear()
}}
>
<text fg={key === store.active ? theme.background : theme.textMuted}>{Locale.titlecase(key)}</text>
<text fg={key === store.active ? theme.background : theme.textMuted}>
{Locale.titlecase(key)}
</text>
</box>
)}
</For>

View File

@@ -5,7 +5,10 @@ import { join } from "node:path"
import { CliRenderer } from "@opentui/core"
export namespace Editor {
export async function open(opts: { value: string; renderer: CliRenderer }): Promise<string | undefined> {
export async function open(opts: {
value: string
renderer: CliRenderer
}): Promise<string | undefined> {
const editor = process.env["EDITOR"]
if (!editor) return

View File

@@ -27,7 +27,9 @@ export const UpgradeCommand = {
const detectedMethod = await Installation.method()
const method = (args.method as Installation.Method) ?? detectedMethod
if (method === "unknown") {
prompts.log.error(`opencode is installed to ${process.execPath} and may be managed by a package manager`)
prompts.log.error(
`opencode is installed to ${process.execPath} and may be managed by a package manager`,
)
const install = await prompts.select({
message: "Install anyways?",
options: [

View File

@@ -574,7 +574,10 @@ export namespace Config {
.object({
apiKey: z.string().optional(),
baseURL: z.string().optional(),
enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
enterpriseUrl: z
.string()
.optional()
.describe("GitHub Enterprise URL for copilot authentication"),
timeout: z
.union([
z

View File

@@ -81,7 +81,9 @@ export namespace Fzf {
})
}
if (config.extension === "zip") {
const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])))
const zipFileReader = new ZipReader(
new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])),
)
const entries = await zipFileReader.getEntries()
let fzfEntry: any
for (const entry of entries) {

View File

@@ -161,7 +161,9 @@ export namespace Ripgrep {
}
if (config.extension === "zip") {
if (config.extension === "zip") {
const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])))
const zipFileReader = new ZipReader(
new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])),
)
const entries = await zipFileReader.getEntries()
let rgEntry: any
for (const entry of entries) {
@@ -354,7 +356,12 @@ export namespace Ripgrep {
return lines.join("\n")
}
export async function search(input: { cwd: string; pattern: string; glob?: string[]; limit?: number }) {
export async function search(input: {
cwd: string
pattern: string
glob?: string[]
limit?: number
}) {
const args = [`${await filepath()}`, "--json", "--hidden", "--glob='!.git/*'"]
if (input.glob) {

View File

@@ -27,7 +27,10 @@ export namespace FileTime {
export async function assert(sessionID: string, filepath: string) {
const time = get(sessionID, filepath)
if (!time) throw new Error(`You must read the file ${filepath} before overwriting it. Use the Read tool first`)
if (!time)
throw new Error(
`You must read the file ${filepath} before overwriting it. Use the Read tool first`,
)
const stats = await Bun.file(filepath).stat()
if (stats.mtime.getTime() > time.getTime()) {
throw new Error(

View File

@@ -51,8 +51,10 @@ export namespace FileWatcher {
for (const evt of evts) {
log.info("event", evt)
if (evt.type === "create") Bus.publish(Event.Updated, { file: evt.path, event: "add" })
if (evt.type === "update") Bus.publish(Event.Updated, { file: evt.path, event: "change" })
if (evt.type === "delete") Bus.publish(Event.Updated, { file: evt.path, event: "unlink" })
if (evt.type === "update")
Bus.publish(Event.Updated, { file: evt.path, event: "change" })
if (evt.type === "delete")
Bus.publish(Event.Updated, { file: evt.path, event: "unlink" })
}
},
{

View File

@@ -49,7 +49,11 @@ export namespace Identifier {
return result
}
export function create(prefix: keyof typeof prefixes, descending: boolean, timestamp?: number): string {
export function create(
prefix: keyof typeof prefixes,
descending: boolean,
timestamp?: number,
): string {
const currentTimestamp = timestamp ?? Date.now()
if (currentTimestamp !== lastTimestamp) {

View File

@@ -1,5 +1,9 @@
import path from "path"
import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node"
import {
createMessageConnection,
StreamMessageReader,
StreamMessageWriter,
} from "vscode-jsonrpc/node"
import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types"
import { Log } from "../util/log"
import { LANGUAGE_EXTENSIONS } from "./language"
@@ -34,7 +38,11 @@ export namespace LSPClient {
),
}
export async function create(input: { serverID: string; server: LSPServer.Handle; root: string }) {
export async function create(input: {
serverID: string
server: LSPServer.Handle
root: string
}) {
const l = log.clone().tag("serverID", input.serverID)
l.info("starting client")
@@ -129,7 +137,9 @@ export namespace LSPClient {
},
notify: {
async open(input: { path: string }) {
input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path)
input.path = path.isAbsolute(input.path)
? input.path
: path.resolve(Instance.directory, input.path)
const file = Bun.file(input.path)
const text = await file.text()
const extension = path.extname(input.path)
@@ -171,13 +181,18 @@ export namespace LSPClient {
return diagnostics
},
async waitForDiagnostics(input: { path: string }) {
input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path)
input.path = path.isAbsolute(input.path)
? input.path
: path.resolve(Instance.directory, input.path)
log.info("waiting for diagnostics", input)
let unsub: () => void
return await withTimeout(
new Promise<void>((resolve) => {
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
if (event.properties.path === input.path && event.properties.serverID === result.serverID) {
if (
event.properties.path === input.path &&
event.properties.serverID === result.serverID
) {
log.info("got diagnostics", input)
unsub?.()
resolve()

View File

@@ -71,7 +71,10 @@ export namespace Patch {
}
// Parser implementation
function parsePatchHeader(lines: string[], startIdx: number): { filePath: string; movePath?: string; nextIdx: number } | null {
function parsePatchHeader(
lines: string[],
startIdx: number,
): { filePath: string; movePath?: string; nextIdx: number } | null {
const line = lines[startIdx]
if (line.startsWith("*** Add File:")) {
@@ -101,7 +104,10 @@ export namespace Patch {
return null
}
function parseUpdateFileChunks(lines: string[], startIdx: number): { chunks: UpdateFileChunk[]; nextIdx: number } {
function parseUpdateFileChunks(
lines: string[],
startIdx: number,
): { chunks: UpdateFileChunk[]; nextIdx: number } {
const chunks: UpdateFileChunk[] = []
let i = startIdx
@@ -155,7 +161,10 @@ export namespace Patch {
return { chunks, nextIdx: i }
}
function parseAddFileContent(lines: string[], startIdx: number): { content: string; nextIdx: number } {
function parseAddFileContent(
lines: string[],
startIdx: number,
): { content: string; nextIdx: number } {
let content = ""
let i = startIdx
@@ -183,8 +192,8 @@ export namespace Patch {
const beginMarker = "*** Begin Patch"
const endMarker = "*** End Patch"
const beginIdx = lines.findIndex(line => line.trim() === beginMarker)
const endIdx = lines.findIndex(line => line.trim() === endMarker)
const beginIdx = lines.findIndex((line) => line.trim() === beginMarker)
const endIdx = lines.findIndex((line) => line.trim() === endMarker)
if (beginIdx === -1 || endIdx === -1 || beginIdx >= endIdx) {
throw new Error("Invalid patch format: missing Begin/End markers")
@@ -232,11 +241,12 @@ export namespace Patch {
}
// Apply patch functionality
export function maybeParseApplyPatch(argv: string[]):
export function maybeParseApplyPatch(
argv: string[],
):
| { type: MaybeApplyPatch.Body; args: ApplyPatchArgs }
| { type: MaybeApplyPatch.PatchParseError; error: Error }
| { type: MaybeApplyPatch.NotApplyPatch } {
const APPLY_PATCH_COMMANDS = ["apply_patch", "applypatch"]
// Direct invocation: apply_patch <patch>
@@ -293,7 +303,10 @@ export namespace Patch {
content: string
}
export function deriveNewContentsFromChunks(filePath: string, chunks: UpdateFileChunk[]): ApplyPatchFileUpdate {
export function deriveNewContentsFromChunks(
filePath: string,
chunks: UpdateFileChunk[],
): ApplyPatchFileUpdate {
// Read original file content
let originalContent: string
try {
@@ -328,7 +341,11 @@ export namespace Patch {
}
}
function computeReplacements(originalLines: string[], filePath: string, chunks: UpdateFileChunk[]): Array<[number, number, string[]]> {
function computeReplacements(
originalLines: string[],
filePath: string,
chunks: UpdateFileChunk[],
): Array<[number, number, string[]]> {
const replacements: Array<[number, number, string[]]> = []
let lineIndex = 0
@@ -344,9 +361,10 @@ export namespace Patch {
// Handle pure addition (no old lines)
if (chunk.old_lines.length === 0) {
const insertionIdx = originalLines.length > 0 && originalLines[originalLines.length - 1] === ""
? originalLines.length - 1
: originalLines.length
const insertionIdx =
originalLines.length > 0 && originalLines[originalLines.length - 1] === ""
? originalLines.length - 1
: originalLines.length
replacements.push([insertionIdx, 0, chunk.new_lines])
continue
}
@@ -370,7 +388,7 @@ export namespace Patch {
lineIndex = found + pattern.length
} else {
throw new Error(
`Failed to find expected lines in ${filePath}:\n${chunk.old_lines.join("\n")}`
`Failed to find expected lines in ${filePath}:\n${chunk.old_lines.join("\n")}`,
)
}
}
@@ -381,7 +399,10 @@ export namespace Patch {
return replacements
}
function applyReplacements(lines: string[], replacements: Array<[number, number, string[]]>): string[] {
function applyReplacements(
lines: string[],
replacements: Array<[number, number, string[]]>,
): string[] {
// Apply replacements in reverse order to avoid index shifting
const result = [...lines]
@@ -513,7 +534,10 @@ export namespace Patch {
}
// Async version of maybeParseApplyPatchVerified
export async function maybeParseApplyPatchVerified(argv: string[], cwd: string): Promise<
export async function maybeParseApplyPatchVerified(
argv: string[],
cwd: string,
): Promise<
| { type: MaybeApplyPatchVerified.Body; action: ApplyPatchAction }
| { type: MaybeApplyPatchVerified.CorrectnessError; error: Error }
| { type: MaybeApplyPatchVerified.NotApplyPatch }
@@ -540,7 +564,10 @@ export namespace Patch {
const changes = new Map<string, ApplyPatchFileChange>()
for (const hunk of args.hunks) {
const resolvedPath = path.resolve(effectiveCwd, hunk.type === "update" && hunk.move_path ? hunk.move_path : hunk.path)
const resolvedPath = path.resolve(
effectiveCwd,
hunk.type === "update" && hunk.move_path ? hunk.move_path : hunk.path,
)
switch (hunk.type) {
case "add":
@@ -574,7 +601,9 @@ export namespace Patch {
changes.set(resolvedPath, {
type: "update",
unified_diff: fileUpdate.unified_diff,
move_path: hunk.move_path ? path.resolve(effectiveCwd, hunk.move_path) : undefined,
move_path: hunk.move_path
? path.resolve(effectiveCwd, hunk.move_path)
: undefined,
new_content: fileUpdate.content,
})
} catch (error) {

View File

@@ -40,7 +40,8 @@ export namespace ProviderTransform {
}
for (const msg of unique([...system, ...final])) {
const shouldUseContentOptions = providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0
const shouldUseContentOptions =
providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0
if (shouldUseContentOptions) {
const lastContent = msg.content[msg.content.length - 1]
@@ -84,7 +85,11 @@ export namespace ProviderTransform {
return undefined
}
export function options(providerID: string, modelID: string, sessionID: string): Record<string, any> | undefined {
export function options(
providerID: string,
modelID: string,
sessionID: string,
): Record<string, any> | undefined {
const result: Record<string, any> = {}
if (providerID === "openai") {
@@ -109,7 +114,11 @@ export namespace ProviderTransform {
return result
}
export function providerOptions(npm: string | undefined, providerID: string, options: { [x: string]: any }) {
export function providerOptions(
npm: string | undefined,
providerID: string,
options: { [x: string]: any },
) {
switch (npm) {
case "@ai-sdk/openai":
case "@ai-sdk/azure":
@@ -142,7 +151,8 @@ export namespace ProviderTransform {
if (providerID === "anthropic") {
const thinking = options?.["thinking"]
const budgetTokens = typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0
const budgetTokens =
typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0
const enabled = thinking?.["type"] === "enabled"
if (enabled && budgetTokens > 0) {
// Return text tokens so that text + thinking <= model cap, preferring 32k text when possible.

View File

@@ -755,7 +755,7 @@ export namespace Server {
),
async (c) => {
const messages = await Session.messages(c.req.valid("param").id)
return c.json(messages)
return c.json(messages.slice(-100))
},
)
.get(

View File

@@ -1,4 +1,4 @@
import { streamText, type ModelMessage, LoadAPIKeyError, type StreamTextResult, type Tool as AITool } from "ai"
import { streamText, type ModelMessage, type StreamTextResult, type Tool as AITool } from "ai"
import { Session } from "."
import { Identifier } from "../id/id"
import { Instance } from "../project/instance"
@@ -30,12 +30,17 @@ export namespace SessionCompaction {
),
}
export function isOverflow(input: { tokens: MessageV2.Assistant["tokens"]; model: ModelsDev.Model }) {
export function isOverflow(input: {
tokens: MessageV2.Assistant["tokens"]
model: ModelsDev.Model
}) {
if (Flag.OPENCODE_DISABLE_AUTOCOMPACT) return false
const context = input.model.limit.context
if (context === 0) return false
const count = input.tokens.input + input.tokens.cache.read + input.tokens.output
const output = Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || SessionPrompt.OUTPUT_TOKEN_MAX
const output =
Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) ||
SessionPrompt.OUTPUT_TOKEN_MAX
const usable = context - output
return count > usable
}
@@ -87,9 +92,15 @@ export namespace SessionCompaction {
}
}
export async function run(input: { sessionID: string; providerID: string; modelID: string; signal?: AbortSignal }) {
export async function run(input: {
sessionID: string
providerID: string
modelID: string
signal?: AbortSignal
}) {
if (!input.signal) SessionLock.assertUnlocked(input.sessionID)
await using lock = input.signal === undefined ? SessionLock.acquire({ sessionID: input.sessionID }) : undefined
await using lock =
input.signal === undefined ? SessionLock.acquire({ sessionID: input.sessionID }) : undefined
const signal = input.signal ?? lock!.signal
await Session.update(input.sessionID, (draft) => {
@@ -113,7 +124,6 @@ export namespace SessionCompaction {
role: "assistant",
parentID: toSummarize.findLast((m) => m.info.role === "user")?.info.id!,
sessionID: input.sessionID,
system,
mode: "build",
path: {
cwd: Instance.directory,
@@ -150,7 +160,11 @@ export namespace SessionCompaction {
// set to 0, we handle loop
maxRetries: 0,
model: model.language,
providerOptions: ProviderTransform.providerOptions(model.npm, model.providerID, model.info.options),
providerOptions: ProviderTransform.providerOptions(
model.npm,
model.providerID,
model.info.options,
),
headers: model.info.headers,
abortSignal: signal,
onError(error) {
@@ -230,7 +244,11 @@ export namespace SessionCompaction {
error: e,
})
const error = MessageV2.fromError(e, { providerID: input.providerID })
if (retries.count < retries.max && MessageV2.APIError.isInstance(error) && error.data.isRetryable) {
if (
retries.count < retries.max &&
MessageV2.APIError.isInstance(error) &&
error.data.isRetryable
) {
shouldRetry = true
await Session.updatePart({
id: Identifier.ascending("part"),

View File

@@ -50,7 +50,10 @@ export namespace SessionLock {
export function acquire(input: { sessionID: string }) {
const lock = get(input.sessionID)
if (lock) {
throw new LockedError({ sessionID: input.sessionID, message: `Session ${input.sessionID} is locked` })
throw new LockedError({
sessionID: input.sessionID,
message: `Session ${input.sessionID} is locked`,
})
}
const controller = new AbortController()
state().locks.set(input.sessionID, {

View File

@@ -2,14 +2,23 @@ import z from "zod"
import { Bus } from "../bus"
import { NamedError } from "../util/error"
import { Message } from "./message"
import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai"
import {
APICallError,
convertToModelMessages,
LoadAPIKeyError,
type ModelMessage,
type UIMessage,
} from "ai"
import { Identifier } from "../id/id"
import { LSP } from "../lsp"
import { Snapshot } from "@/snapshot"
export namespace MessageV2 {
export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({}))
export const AbortedError = NamedError.create("MessageAbortedError", z.object({ message: z.string() }))
export const AbortedError = NamedError.create(
"MessageAbortedError",
z.object({ message: z.string() }),
)
export const AuthError = NamedError.create(
"ProviderAuthError",
z.object({
@@ -242,7 +251,12 @@ export namespace MessageV2 {
export type ToolStateError = z.infer<typeof ToolStateError>
export const ToolState = z
.discriminatedUnion("status", [ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError])
.discriminatedUnion("status", [
ToolStatePending,
ToolStateRunning,
ToolStateCompleted,
ToolStateError,
])
.meta({
ref: "ToolState",
})
@@ -313,7 +327,6 @@ export namespace MessageV2 {
APIError.Schema,
])
.optional(),
system: z.string().array(),
parentID: z.string(),
modelID: z.string(),
providerID: z.string(),
@@ -397,7 +410,6 @@ export namespace MessageV2 {
tokens: v1.metadata.assistant!.tokens,
modelID: v1.metadata.assistant!.modelID,
providerID: v1.metadata.assistant!.providerID,
system: v1.metadata.assistant!.system,
mode: "build",
error: v1.metadata.error,
}
@@ -440,7 +452,8 @@ export namespace MessageV2 {
}
}
const { title, time, ...metadata } = v1.metadata.tool[part.toolInvocation.toolCallId] ?? {}
const { title, time, ...metadata } =
v1.metadata.tool[part.toolInvocation.toolCallId] ?? {}
if (part.toolInvocation.state === "call") {
return {
status: "running",
@@ -541,7 +554,11 @@ export namespace MessageV2 {
},
]
// text/plain and directory files are converted into text parts, ignore them
if (part.type === "file" && part.mime !== "text/plain" && part.mime !== "application/x-directory")
if (
part.type === "file" &&
part.mime !== "text/plain" &&
part.mime !== "application/x-directory"
)
return [
{
type: "file",
@@ -600,7 +617,9 @@ export namespace MessageV2 {
state: "output-available",
toolCallId: part.callID,
input: part.state.input,
output: part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output,
output: part.state.time.compacted
? "[Old tool result content cleared]"
: part.state.output,
callProviderMetadata: part.metadata,
},
]

View File

@@ -51,9 +51,11 @@ export namespace Message {
})
export type ToolResult = z.infer<typeof ToolResult>
export const ToolInvocation = z.discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult]).meta({
ref: "ToolInvocation",
})
export const ToolInvocation = z
.discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult])
.meta({
ref: "ToolInvocation",
})
export type ToolInvocation = z.infer<typeof ToolInvocation>
export const TextPart = z
@@ -122,7 +124,14 @@ export namespace Message {
export type StepStartPart = z.infer<typeof StepStartPart>
export const MessagePart = z
.discriminatedUnion("type", [TextPart, ReasoningPart, ToolInvocationPart, SourceUrlPart, FilePart, StepStartPart])
.discriminatedUnion("type", [
TextPart,
ReasoningPart,
ToolInvocationPart,
SourceUrlPart,
FilePart,
StepStartPart,
])
.meta({
ref: "MessagePart",
})
@@ -140,7 +149,11 @@ export namespace Message {
completed: z.number().optional(),
}),
error: z
.discriminatedUnion("name", [AuthError.Schema, NamedError.Unknown.Schema, OutputLengthError.Schema])
.discriminatedUnion("name", [
AuthError.Schema,
NamedError.Unknown.Schema,
OutputLengthError.Schema,
])
.optional(),
sessionID: z.string(),
tool: z.record(

View File

@@ -961,7 +961,6 @@ export namespace SessionPrompt {
id: Identifier.ascending("message"),
parentID,
role: "assistant",
system: input.system,
mode: input.agent,
path: {
cwd: Instance.directory,
@@ -1412,7 +1411,6 @@ export namespace SessionPrompt {
id: Identifier.ascending("message"),
sessionID: input.sessionID,
parentID: userMsg.id,
system: [],
mode: input.agent,
cost: 0,
path: {
@@ -1709,7 +1707,6 @@ export namespace SessionPrompt {
id: Identifier.ascending("message"),
sessionID: input.sessionID,
parentID: userMsg.id,
system: [],
mode: agentName,
cost: 0,
path: {

View File

@@ -45,7 +45,9 @@ export namespace SessionRevert {
if (!revert) {
if ((msg.info.id === input.messageID && !input.partID) || part.id === input.partID) {
// if no useful parts left in message, same as reverting whole message
const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) ? input.partID : undefined
const partID = remaining.some((item) => ["text", "tool"].includes(item.type))
? input.partID
: undefined
revert = {
messageID: !partID && lastUser ? lastUser.id : msg.info.id,
partID,

View File

@@ -24,7 +24,8 @@ export namespace SystemPrompt {
export function provider(modelID: string) {
if (modelID.includes("gpt-5")) return [PROMPT_CODEX]
if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST]
if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3"))
return [PROMPT_BEAST]
if (modelID.includes("gemini-")) return [PROMPT_GEMINI]
if (modelID.includes("claude")) return [PROMPT_ANTHROPIC]
return [PROMPT_ANTHROPIC_WITHOUT_TODO]
@@ -99,7 +100,11 @@ export namespace SystemPrompt {
}),
).catch(() => [])
} else {
matches = await Filesystem.globUp(instruction, Instance.directory, Instance.worktree).catch(() => [])
matches = await Filesystem.globUp(
instruction,
Instance.directory,
Instance.worktree,
).catch(() => [])
}
matches.forEach((path) => paths.add(path))
}

View File

@@ -6,7 +6,9 @@ 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"),
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"),
})

View File

@@ -50,7 +50,10 @@ export namespace Share {
await sync("session/info/" + evt.properties.info.id, evt.properties.info)
})
Bus.subscribe(MessageV2.Event.Updated, async (evt) => {
await sync("session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, evt.properties.info)
await sync(
"session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id,
evt.properties.info,
)
})
Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
await sync(
@@ -67,7 +70,9 @@ export namespace Share {
export const URL =
process.env["OPENCODE_API"] ??
(Installation.isPreview() || Installation.isLocal() ? "https://api.dev.opencode.ai" : "https://api.opencode.ai")
(Installation.isPreview() || Installation.isLocal()
? "https://api.dev.opencode.ai"
: "https://api.opencode.ai")
export async function create(sessionID: string) {
return fetch(`${URL}/share_create`, {

View File

@@ -27,7 +27,11 @@ export namespace Snapshot {
log.info("initialized")
}
await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow()
const hash = await $`git --git-dir ${git} write-tree`.quiet().cwd(Instance.directory).nothrow().text()
const hash = await $`git --git-dir ${git} write-tree`
.quiet()
.cwd(Instance.directory)
.nothrow()
.text()
log.info("tracking", { hash, cwd: Instance.directory, git })
return hash.trim()
}
@@ -41,7 +45,10 @@ export namespace Snapshot {
export async function patch(hash: string): Promise<Patch> {
const git = gitdir()
await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow()
const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .`.quiet().cwd(Instance.directory).nothrow()
const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .`
.quiet()
.cwd(Instance.directory)
.nothrow()
// If git diff fails, return empty patch
if (result.exitCode !== 0) {
@@ -64,10 +71,11 @@ export namespace Snapshot {
export async function restore(snapshot: string) {
log.info("restore", { commit: snapshot })
const git = gitdir()
const result = await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f`
.quiet()
.cwd(Instance.worktree)
.nothrow()
const result =
await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f`
.quiet()
.cwd(Instance.worktree)
.nothrow()
if (result.exitCode !== 0) {
log.error("failed to restore snapshot", {
@@ -113,7 +121,10 @@ export namespace Snapshot {
export async function diff(hash: string) {
const git = gitdir()
await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow()
const result = await $`git --git-dir=${git} diff ${hash} -- .`.quiet().cwd(Instance.worktree).nothrow()
const result = await $`git --git-dir=${git} diff ${hash} -- .`
.quiet()
.cwd(Instance.worktree)
.nothrow()
if (result.exitCode !== 0) {
log.warn("failed to get diff", {

View File

@@ -23,8 +23,13 @@ export const EditTool = Tool.define("edit", {
parameters: z.object({
filePath: z.string().describe("The absolute path to the file to modify"),
oldString: z.string().describe("The text to replace"),
newString: z.string().describe("The text to replace it with (must be different from oldString)"),
replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"),
newString: z
.string()
.describe("The text to replace it with (must be different from oldString)"),
replaceAll: z
.boolean()
.optional()
.describe("Replace all occurrences of oldString (default false)"),
}),
async execute(params, ctx) {
if (!params.filePath) {
@@ -35,7 +40,9 @@ export const EditTool = Tool.define("edit", {
throw new Error("oldString and newString must be different")
}
const filePath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath)
const filePath = path.isAbsolute(params.filePath)
? params.filePath
: path.join(Instance.directory, params.filePath)
if (!Filesystem.contains(Instance.directory, filePath)) {
const parentDir = path.dirname(filePath)
await Permission.ask({
@@ -172,7 +179,11 @@ function levenshtein(a: string, b: string): number {
for (let i = 1; i <= a.length; i++) {
for (let j = 1; j <= b.length; j++) {
const cost = a[i - 1] === b[j - 1] ? 0 : 1
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost)
matrix[i][j] = Math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost,
)
}
}
return matrix[a.length][b.length]
@@ -374,7 +385,9 @@ export const WhitespaceNormalizedReplacer: Replacer = function* (content, find)
// Find the actual substring in the original line that matches
const words = find.trim().split(/\s+/)
if (words.length > 0) {
const pattern = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+")
const pattern = words
.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
.join("\\s+")
try {
const regex = new RegExp(pattern)
const match = line.match(regex)
@@ -612,7 +625,12 @@ export function trimDiff(diff: string): string {
return trimmedLines.join("\n")
}
export function replace(content: string, oldString: string, newString: string, replaceAll = false): string {
export function replace(
content: string,
oldString: string,
newString: string,
replaceAll = false,
): string {
if (oldString === newString) {
throw new Error("oldString and newString must be different")
}

Some files were not shown because too many files have changed in this diff Show More