Compare commits

..

7 Commits

Author SHA1 Message Date
Luke Parker 2c292e1a99 fix(desktop): disable packaged console logging 2026-08-06 03:06:10 +00:00
opencode-agent[bot] b8bd88901a chore: generate 2026-08-06 01:52:15 +00:00
Luke Parker f1adabcddc feat(app): export session as json from ui (#40781) 2026-08-06 11:50:33 +10:00
Luke Parker 24470e52a5 fix(desktop): embed version in server sidecar (#40764) 2026-08-06 07:22:11 +08:00
mridul 23bbc5cd14 fix(server): allow blob attachments in web UI (#40692) 2026-08-05 22:23:32 +00:00
opencode ebf6fc07a1 sync release versions for v1.18.14 2026-08-05 20:58:52 +00:00
opencode-agent[bot] 04513d9692 chore: generate 2026-08-05 20:56:57 +00:00
43 changed files with 306 additions and 68 deletions
+1
View File
@@ -322,6 +322,7 @@ jobs:
working-directory: packages/desktop
env:
NODE_OPTIONS: --max-old-space-size=4096
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
+28 -28
View File
@@ -29,7 +29,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@corvu/drawer": "catalog:",
"@dnd-kit/abstract": "0.5.0",
@@ -96,7 +96,7 @@
},
"packages/cli": {
"name": "@opencode-ai/cli",
"version": "1.18.13",
"version": "1.18.14",
"bin": {
"lildax": "./bin/lildax.cjs",
},
@@ -144,7 +144,7 @@
},
"packages/codemode": {
"name": "@opencode-ai/codemode",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"acorn": "8.15.0",
"effect": "catalog:",
@@ -158,7 +158,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -194,7 +194,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -221,7 +221,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@ai-sdk/anthropic": "3.0.82",
"@ai-sdk/openai": "3.0.48",
@@ -243,7 +243,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -267,7 +267,7 @@
},
"packages/console/support": {
"name": "@opencode-ai/console-support",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@opencode-ai/console-core": "workspace:*",
@@ -287,7 +287,7 @@
},
"packages/core": {
"name": "@opencode-ai/core",
"version": "1.18.13",
"version": "1.18.14",
"bin": {
"opencode": "./bin/opencode",
},
@@ -381,7 +381,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@zip.js/zip.js": "2.7.62",
"drizzle-orm": "catalog:",
@@ -435,7 +435,7 @@
},
"packages/effect-drizzle-sqlite": {
"name": "@opencode-ai/effect-drizzle-sqlite",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:",
@@ -449,7 +449,7 @@
},
"packages/effect-sqlite-node": {
"name": "@opencode-ai/effect-sqlite-node",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"effect": "catalog:",
},
@@ -461,7 +461,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@hono/standard-validator": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -493,7 +493,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -509,7 +509,7 @@
},
"packages/http-recorder": {
"name": "@opencode-ai/http-recorder",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@effect/platform-node": "4.0.0-beta.83",
"@effect/platform-node-shared": "4.0.0-beta.83",
@@ -540,7 +540,7 @@
},
"packages/llm": {
"name": "@opencode-ai/llm",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@opencode-ai/schema": "workspace:*",
"@smithy/eventstream-codec": "4.2.14",
@@ -559,7 +559,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.18.13",
"version": "1.18.14",
"bin": {
"opencode": "./bin/opencode",
},
@@ -690,7 +690,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@ai-sdk/provider": "3.0.8",
"@opencode-ai/sdk": "workspace:*",
@@ -766,7 +766,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -781,7 +781,7 @@
},
"packages/server": {
"name": "@opencode-ai/server",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@opencode-ai/core": "workspace:*",
"@opencode-ai/protocol": "workspace:*",
@@ -796,7 +796,7 @@
},
"packages/session-ui": {
"name": "@opencode-ai/session-ui",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/client": "file:../app/vendor/opencode-ai-client-1.17.13-v2.tgz",
@@ -836,7 +836,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -849,7 +849,7 @@
},
"packages/stats/app": {
"name": "@opencode-ai/stats-app",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@ibm/plex": "6.4.1",
"@kobalte/core": "catalog:",
@@ -883,7 +883,7 @@
},
"packages/stats/core": {
"name": "@opencode-ai/stats-core",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@aws-sdk/client-athena": "3.933.0",
"@planetscale/database": "1.19.0",
@@ -902,7 +902,7 @@
},
"packages/stats/server": {
"name": "@opencode-ai/stats-server",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@aws-sdk/client-firehose": "3.933.0",
"@effect/platform-node": "catalog:",
@@ -944,7 +944,7 @@
},
"packages/tui": {
"name": "@opencode-ai/tui",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@opencode-ai/core": "workspace:*",
"@opencode-ai/plugin": "workspace:*",
@@ -971,7 +971,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@kobalte/core": "catalog:",
"@pierre/diffs": "catalog:",
@@ -1022,7 +1022,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.18.13",
"version": "1.18.14",
"description": "",
"type": "module",
"exports": {
@@ -5,12 +5,15 @@ import { checksum } from "@opencode-ai/core/util/encode"
import { findLast } from "@opencode-ai/core/util/array"
import { same } from "@/utils/same"
import { Icon } from "@opencode-ai/ui/icon"
import { Button } from "@opencode-ai/ui/button"
import { Accordion } from "@opencode-ai/ui/accordion"
import { StickyAccordionHeader } from "@opencode-ai/ui/sticky-accordion-header"
import { File } from "@opencode-ai/session-ui/file"
import { Markdown } from "@opencode-ai/session-ui/markdown"
import { ScrollView } from "@opencode-ai/ui/scroll-view"
import type { Message, Part, UserMessage } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@/utils/toast"
import { downloadSessionExport, fetchSessionExport, sessionExportFilename } from "@/utils/session-export"
import { useLanguage } from "@/context/language"
import { useProviders } from "@/hooks/use-providers"
import { useSDK } from "@/context/sdk"
@@ -220,6 +223,31 @@ export function SessionContextTab() {
{ label: "context.stats.lastActivity", value: () => formatter().time(ctx()?.message.time.created) },
] satisfies { label: string; value: () => JSX.Element }[]
const exportSession = async () => {
const sessionID = params.id
if (!sessionID) return
try {
const data = await fetchSessionExport({
sessionID,
client: sdk().client,
})
const filename = sessionExportFilename(data.info)
downloadSessionExport(filename, data)
showToast({
variant: "success",
icon: "circle-check",
title: language.t("toast.session.export.success.title"),
description: language.t("toast.session.export.success.description", { filename }),
})
} catch (err) {
showToast({
variant: "error",
title: language.t("toast.session.export.failed.title"),
description: err instanceof Error ? err.message : language.t("toast.session.export.failed.description"),
})
}
}
let scroll: HTMLDivElement | undefined
let frame: number | undefined
let pending: { x: number; y: number } | undefined
@@ -328,7 +356,18 @@ export function SessionContextTab() {
</Show>
<div class="flex flex-col gap-2">
<div class="text-12-regular text-text-weak">{language.t("context.rawMessages.title")}</div>
<div class="flex items-center justify-between">
<div class="text-12-regular text-text-weak">{language.t("context.rawMessages.title")}</div>
<Button
size="small"
variant="ghost"
class="gap-1.5 px-2 text-text-weak hover:text-text-base"
onClick={exportSession}
>
<Icon name="download" size="small" />
<span>{language.t("context.export.session")}</span>
</Button>
</div>
<Accordion multiple>
<For each={messages()}>
{(message) => (
+9
View File
@@ -95,6 +95,8 @@ export const dict = {
"command.session.share.description": "Share this session and copy the URL to clipboard",
"command.session.unshare": "Unshare session",
"command.session.unshare.description": "Stop sharing this session",
"command.session.export": "Export session",
"command.session.export.description": "Export the full session transcript as JSON",
"palette.search.placeholder": "Search files, commands, and sessions",
"palette.search.placeholder.home": "Search commands and sessions",
@@ -489,6 +491,7 @@ export const dict = {
"context.systemPrompt.title": "System Prompt",
"context.rawMessages.title": "Raw messages",
"context.export.session": "Export session",
"context.stats.session": "Session",
"context.stats.messages": "Messages",
@@ -568,6 +571,11 @@ export const dict = {
"toast.session.unshare.failed.title": "Failed to unshare session",
"toast.session.unshare.failed.description": "An error occurred while unsharing the session",
"toast.session.export.success.title": "Session exported",
"toast.session.export.success.description": "Saved session to {{filename}}",
"toast.session.export.failed.title": "Failed to export session",
"toast.session.export.failed.description": "An error occurred while exporting the session",
"toast.session.listFailed.title": "Failed to load sessions for {{project}}",
"toast.project.reloadFailed.title": "Failed to reload {{project}}",
@@ -802,6 +810,7 @@ export const dict = {
"common.moreOptions": "More options",
"common.learnMore": "Learn more",
"common.rename": "Rename",
"common.export": "Export",
"common.reset": "Reset",
"common.archive": "Archive",
"common.delete": "Delete",
@@ -53,6 +53,7 @@ import type {
UserMessage,
} from "@opencode-ai/sdk/v2"
import { showToast } from "@/utils/toast"
import { downloadSessionExport, fetchSessionExport, sessionExportFilename } from "@/utils/session-export"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
import { Popover as KobaltePopover } from "@kobalte/core/popover"
import { normalize } from "@opencode-ai/session-ui/session-diff"
@@ -806,6 +807,29 @@ export function MessageTimeline(props: {
navigate(`/${params.dir}/session`)
}
const exportSession = async (sessionID: string) => {
try {
const data = await fetchSessionExport({
sessionID,
client: sdk().client,
})
const filename = sessionExportFilename(data.info)
downloadSessionExport(filename, data)
showToast({
variant: "success",
icon: "circle-check",
title: language.t("toast.session.export.success.title"),
description: language.t("toast.session.export.success.description", { filename }),
})
} catch (err) {
showToast({
variant: "error",
title: language.t("toast.session.export.failed.title"),
description: err instanceof Error ? err.message : language.t("toast.session.export.failed.description"),
})
}
}
const archiveSession = async (sessionID: string) => {
const session = sync().session.get(sessionID)
if (!session) return
@@ -1564,6 +1588,9 @@ export function MessageTimeline(props: {
</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
</Show>
<DropdownMenu.Item onSelect={() => exportSession(id)}>
<DropdownMenu.ItemLabel>{language.t("common.export")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Item onSelect={() => void archiveSession(id)}>
<DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
@@ -1635,6 +1662,9 @@ export function MessageTimeline(props: {
{language.t("session.share.action.share")}...
</MenuV2.Item>
</Show>
<MenuV2.Item onSelect={() => exportSession(id)}>
{language.t("common.export")}...
</MenuV2.Item>
<MenuV2.Item onSelect={() => void archiveSession(id)}>
{language.t("common.archive")}
</MenuV2.Item>
@@ -12,10 +12,11 @@ import { useSettings } from "@/context/settings"
import { useSync } from "@/context/sync"
import { useTerminal } from "@/context/terminal"
import { showToast } from "@/utils/toast"
import { downloadSessionExport, fetchSessionExport, sessionExportFilename } from "@/utils/session-export"
import { findLast } from "@opencode-ai/core/util/array"
import { createSessionTabs } from "@/pages/session/helpers"
import { extractPromptFromParts } from "@/utils/prompt"
import { UserMessage } from "@opencode-ai/sdk/v2"
import { Message, Part, UserMessage } from "@opencode-ai/sdk/v2"
import { useSessionLayout } from "@/pages/session/session-layout"
import { createSessionOwnership } from "./session-ownership"
import { useLocal } from "@/context/local"
@@ -231,6 +232,31 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
)
}
const exportSession = async () => {
const sessionID = params.id
if (!sessionID) return
try {
const data = await fetchSessionExport({
sessionID,
client: sdk().client,
})
const filename = sessionExportFilename(data.info)
downloadSessionExport(filename, data)
showToast({
variant: "success",
icon: "circle-check",
title: language.t("toast.session.export.success.title"),
description: language.t("toast.session.export.success.description", { filename }),
})
} catch (err) {
showToast({
variant: "error",
title: language.t("toast.session.export.failed.title"),
description: err instanceof Error ? err.message : language.t("toast.session.export.failed.description"),
})
}
}
const openFile = () => {
void openDialog(
() => import("@/components/dialog-select-file"),
@@ -458,6 +484,14 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
disabled: !params.id || visibleUserMessages().length === 0,
onSelect: fork,
}),
sessionCommand({
id: "session.export",
title: language.t("command.session.export"),
description: language.t("command.session.export.description"),
slash: "export",
disabled: !params.id,
onSelect: exportSession,
}),
]
const fileCmds = () => {
@@ -0,0 +1,61 @@
import { describe, expect, test } from "bun:test"
import { fetchSessionExport, sessionExportFilename } from "./session-export"
import type { Message, Part, Session } from "@opencode-ai/sdk/v2/client"
describe("sessionExportFilename", () => {
test("generates filename from title", () => {
expect(sessionExportFilename({ id: "ses_123", title: "Clone PR in worktree from fork" })).toBe(
"clone-pr-in-worktree-from-fork.json",
)
})
test("generates filename from slug when title missing", () => {
expect(sessionExportFilename({ id: "ses_123", slug: "my-session-slug" })).toBe("my-session-slug.json")
})
test("falls back to id when title and slug are empty", () => {
expect(sessionExportFilename({ id: "ses_123" })).toBe("ses_123.json")
})
})
describe("fetchSessionExport", () => {
test("fetches full transcript from client", async () => {
const session = { id: "ses_1", title: "Test Session" } as Session
const msg = { id: "msg_1", role: "user" } as Message
const part = { id: "prt_1", type: "text", text: "hello" } as Part
const messages = [{ info: msg, parts: [part] }]
const client = {
session: {
get: async () => ({ data: session }),
messages: async () => ({ data: messages }),
},
}
const result = await fetchSessionExport({
sessionID: "ses_1",
client,
})
expect(result).toEqual({
info: session,
messages,
})
})
test("throws when session not found", async () => {
const client = {
session: {
get: async () => ({ data: null }),
messages: async () => ({ data: [] }),
},
}
expect(
fetchSessionExport({
sessionID: "ses_missing",
client,
}),
).rejects.toThrow("Session not found: ses_missing")
})
})
+61
View File
@@ -0,0 +1,61 @@
import type { Message, Part, Session } from "@opencode-ai/sdk/v2/client"
// Matches the exact `{ info, messages: [{ info, parts }] }` structure produced by `opencode export` CLI
export type SessionExportData = {
info: Session
messages: {
info: Message
parts: Part[]
}[]
}
export type SessionExportClient = {
session: {
get: (input: { sessionID: string }) => Promise<{ data?: Session | null }>
messages: (input: { sessionID: string }) => Promise<{ data?: SessionExportData["messages"] | null }>
}
}
export async function fetchSessionExport(input: {
sessionID: string
client: SessionExportClient
}): Promise<SessionExportData> {
const [sessionRes, messagesRes] = await Promise.all([
input.client.session.get({ sessionID: input.sessionID }),
input.client.session.messages({ sessionID: input.sessionID }),
])
if (!sessionRes?.data) {
throw new Error(`Session not found: ${input.sessionID}`)
}
if (!messagesRes?.data) {
throw new Error(`Failed to load messages for session: ${input.sessionID}`)
}
return {
info: sessionRes.data,
messages: messagesRes.data,
}
}
export function sessionExportFilename(session: { id: string; title?: string; slug?: string }) {
const name = session.title || session.slug || session.id
const clean = name
.toLowerCase()
.replace(/[^a-z0-9_-]+/gi, "-")
.replace(/^-+|-+$/g, "")
return `${clean || session.id}.json`
}
export function downloadSessionExport(filename: string, data: unknown) {
const json = JSON.stringify(data, null, 2)
const blob = new Blob([json], { type: "application/json" })
const url = URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/cli",
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"bin": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/codemode",
"version": "1.18.13",
"version": "1.18.14",
"description": "Effect-native confined code execution over schema-described tools",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.18.13",
"version": "1.18.14",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "1.18.13",
"version": "1.18.14",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-support",
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.13",
"version": "1.18.14",
"name": "@opencode-ai/core",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"homepage": "https://opencode.ai",
+5
View File
@@ -189,6 +189,11 @@ async function writeZip(output: string, entries: Entry[]) {
}
function initConsoleTransport() {
if (app.isPackaged) {
log.transports.console.level = false
return
}
const write = log.transports.console.writeFn.bind(log.transports.console)
log.transports.console.writeFn = (options) => {
try {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.13",
"version": "1.18.14",
"name": "@opencode-ai/effect-drizzle-sqlite",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.13",
"version": "1.18.14",
"name": "@opencode-ai/effect-sqlite-node",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "1.18.13",
"version": "1.18.14",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.13",
"version": "1.18.14",
"name": "@opencode-ai/http-recorder",
"description": "Record and replay Effect HTTP client traffic with deterministic cassettes",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.13",
"version": "1.18.14",
"name": "@opencode-ai/llm",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.13",
"version": "1.18.14",
"name": "opencode",
"type": "module",
"license": "MIT",
+1
View File
@@ -21,6 +21,7 @@ await Bun.build({
external: ["jsonc-parser", "@lydell/node-pty"],
define: {
OPENCODE_MODELS_DEV: generated.modelsData,
OPENCODE_VERSION: `'${Script.version}'`,
OPENCODE_CHANNEL: `'${Script.channel}'`,
},
files: {
+1 -1
View File
@@ -9,7 +9,7 @@ let embeddedUIPromise: Promise<Record<string, string> | null> | undefined
export const UI_UPSTREAM = new URL("https://app.opencode.ai")
export const csp = (hash = "") =>
`default-src 'self'; script-src 'self' 'wasm-unsafe-eval'${hash ? ` 'sha256-${hash}'` : ""}; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src * data:`
`default-src 'self'; script-src 'self' 'wasm-unsafe-eval'${hash ? ` 'sha256-${hash}'` : ""}; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: blob:; font-src 'self' data:; media-src 'self' data:; connect-src * data: blob:`
export const DEFAULT_CSP = csp()
export function themePreloadHash(body: string) {
@@ -326,7 +326,7 @@ describe("HttpApi UI fallback", () => {
}),
)
it.live("allows embedded UI terminal wasm and theme preload CSP", () =>
it.live("allows embedded UI terminal wasm, blob attachments, and theme preload CSP", () =>
Effect.gen(function* () {
const script = 'document.documentElement.dataset.theme = "dark"'
@@ -351,7 +351,8 @@ describe("HttpApi UI fallback", () => {
const csp = response.headers.get("content-security-policy") ?? ""
expect(csp).toContain("script-src 'self' 'wasm-unsafe-eval'")
expect(csp).toContain(`'sha256-${createHash("sha256").update(script).digest("base64")}'`)
expect(csp).toContain("connect-src * data:")
expect(csp).toContain("img-src 'self' data: https: blob:")
expect(csp).toContain("connect-src * data: blob:")
}),
)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/server",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/session-ui",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/slack",
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-app",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -2
View File
@@ -24,8 +24,7 @@ export default createHandler(
const event = getRequestEvent() as PageEvent | undefined
const locale = event ? localeFromRequest(event.request) : "en"
const stylesheet = (event?.assets as Asset[] | undefined)?.find(
(asset): asset is Extract<Asset, { tag: "link" }> =>
asset.tag === "link" && asset.attrs.rel === "stylesheet",
(asset): asset is Extract<Asset, { tag: "link" }> => asset.tag === "link" && asset.attrs.rel === "stylesheet",
)
const stylesheetHref = import.meta.env.DEV ? statsStylesheetUrl : stylesheet?.attrs.href
@@ -83,10 +83,7 @@ export function ComparisonCardsSection(props: {
function FeaturedComparisonCard(props: { pair: ComparisonPair }) {
return (
<a
data-component="compare-home-card"
href={canonicalComparisonHref(props.pair.first, props.pair.second)}
>
<a data-component="compare-home-card" href={canonicalComparisonHref(props.pair.first, props.pair.second)}>
<span data-slot="compare-home-card-head">
<span>
<strong>{props.pair.detail}</strong>
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-core",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-server",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/tui",
"version": "1.18.13",
"version": "1.18.14",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
"version": "1.18.13",
"version": "1.18.14",
"type": "module",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "@opencode-ai/web",
"type": "module",
"license": "MIT",
"version": "1.18.13",
"version": "1.18.14",
"scripts": {
"dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "opencode",
"displayName": "opencode",
"description": "opencode for VS Code",
"version": "1.18.13",
"version": "1.18.14",
"publisher": "sst-dev",
"repository": {
"type": "git",