Compare commits

...

6 Commits

Author SHA1 Message Date
Kit Langton 4d3901adf1 test(workspace): dispose instances after httpapi test
Align the workspace HttpApi server test with the shared instance cleanup pattern used by the other experimental slices.
2026-04-14 20:43:38 -04:00
Kit Langton ce035ec4d3 add experimental workspace HttpApi read slice
Add a parallel experimental workspace HttpApi surface for adaptor/list/status reads using route-local Effect schemas and cover the new endpoints with a server test.
2026-04-14 20:43:38 -04:00
opencode-agent[bot] 87b2a9d749 chore: generate 2026-04-15 00:30:27 +00:00
Frank 8df7ccc304 zen: rate limiter 2026-04-14 20:29:21 -04:00
Brendan Allan 2c36bf9490 fix(app): avoid bootstrap error popups during global sync init (#22426) 2026-04-15 08:24:52 +08:00
opencode bddf830083 release: v1.4.4 2026-04-15 00:03:43 +00:00
36 changed files with 7849 additions and 78 deletions
+17 -17
View File
@@ -27,7 +27,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -81,7 +81,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -115,7 +115,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -142,7 +142,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@ai-sdk/anthropic": "3.0.64",
"@ai-sdk/openai": "3.0.48",
@@ -166,7 +166,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -190,7 +190,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -223,7 +223,7 @@
},
"packages/desktop-electron": {
"name": "@opencode-ai/desktop-electron",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"effect": "catalog:",
"electron-context-menu": "4.1.2",
@@ -266,7 +266,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
@@ -295,7 +295,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -311,7 +311,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.4.3",
"version": "1.4.4",
"bin": {
"opencode": "./bin/opencode",
},
@@ -450,7 +450,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"effect": "catalog:",
@@ -485,7 +485,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -500,14 +500,14 @@
},
"packages/server": {
"name": "@opencode-ai/server",
"version": "1.4.3",
"version": "1.4.4",
"devDependencies": {
"typescript": "catalog:",
},
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -542,7 +542,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -591,7 +591,7 @@
},
"packages/util": {
"name": "@opencode-ai/util",
"version": "1.4.3",
"version": "1.4.4",
"dependencies": {
"zod": "catalog:",
},
@@ -602,7 +602,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.4.3",
"version": "1.4.4",
"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.4.3",
"version": "1.4.4",
"description": "",
"type": "module",
"exports": {
@@ -122,20 +122,21 @@ export async function bootstrapGlobal(input: {
}),
),
]
showErrors({
errors: errors(await runAll(fast)),
title: input.requestFailedTitle,
translate: input.translate,
formatMoreCount: input.formatMoreCount,
})
await runAll(fast)
// showErrors({
// errors: errors(await runAll(fast)),
// title: input.requestFailedTitle,
// translate: input.translate,
// formatMoreCount: input.formatMoreCount,
// })
await waitForPaint()
showErrors({
errors: errors(await runAll(slow)),
title: input.requestFailedTitle,
translate: input.translate,
formatMoreCount: input.formatMoreCount,
})
await runAll(slow)
// showErrors({
// errors: errors(),
// title: input.requestFailedTitle,
// translate: input.translate,
// formatMoreCount: input.formatMoreCount,
// })
input.setGlobalStore("ready", true)
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"license": "MIT",
"scripts": {
@@ -11,5 +11,6 @@ class LimitError extends Error {
this.retryAfter = retryAfter
}
}
export class RateLimitError extends LimitError {}
export class FreeUsageLimitError extends LimitError {}
export class SubscriptionUsageLimitError extends LimitError {}
@@ -21,6 +21,7 @@ import {
MonthlyLimitError,
UserLimitError,
ModelError,
RateLimitError,
FreeUsageLimitError,
SubscriptionUsageLimitError,
} from "./error"
@@ -35,7 +36,8 @@ import { anthropicHelper } from "./provider/anthropic"
import { googleHelper } from "./provider/google"
import { openaiHelper } from "./provider/openai"
import { oaCompatHelper } from "./provider/openai-compatible"
import { createRateLimiter } from "./rateLimiter"
import { createRateLimiter as createIpRateLimiter } from "./ipRateLimiter"
import { createRateLimiter as createKeyRateLimiter } from "./keyRateLimiter"
import { createDataDumper } from "./dataDumper"
import { createTrialLimiter } from "./trialLimiter"
import { createStickyTracker } from "./stickyProviderTracker"
@@ -92,6 +94,8 @@ export async function handler(
const isStream = opts.parseIsStream(url, body)
const rawIp = input.request.headers.get("x-real-ip") ?? ""
const ip = rawIp.includes(":") ? rawIp.split(":").slice(0, 4).join(":") : rawIp
const rawZenApiKey = opts.parseApiKey(input.request.headers)
const zenApiKey = rawZenApiKey === "public" ? undefined : rawZenApiKey
const sessionId = input.request.headers.get("x-opencode-session") ?? ""
const requestId = input.request.headers.get("x-opencode-request") ?? ""
const projectId = input.request.headers.get("x-opencode-project") ?? ""
@@ -108,17 +112,13 @@ export async function handler(
const dataDumper = createDataDumper(sessionId, requestId, projectId)
const trialLimiter = createTrialLimiter(modelInfo.trialProvider, ip)
const trialProviders = await trialLimiter?.check()
const rateLimiter = createRateLimiter(
modelInfo.id,
modelInfo.allowAnonymous,
modelInfo.rateLimit,
ip,
input.request,
)
const rateLimiter = modelInfo.allowAnonymous
? createIpRateLimiter(modelInfo.id, modelInfo.rateLimit, ip, input.request)
: createKeyRateLimiter(modelInfo.id, zenApiKey, input.request)
await rateLimiter?.check()
const stickyTracker = createStickyTracker(modelInfo.stickyProvider, sessionId)
const stickyProvider = await stickyTracker?.get()
const authInfo = await authenticate(modelInfo)
const authInfo = await authenticate(modelInfo, zenApiKey)
const billingSource = validateBilling(authInfo, modelInfo)
logger.metric({ source: billingSource })
@@ -363,7 +363,11 @@ export async function handler(
{ status: 401 },
)
if (error instanceof FreeUsageLimitError || error instanceof SubscriptionUsageLimitError) {
if (
error instanceof RateLimitError ||
error instanceof FreeUsageLimitError ||
error instanceof SubscriptionUsageLimitError
) {
const headers = new Headers()
if (error.retryAfter) {
headers.set("retry-after", String(error.retryAfter))
@@ -492,9 +496,8 @@ export async function handler(
}
}
async function authenticate(modelInfo: ModelInfo) {
const apiKey = opts.parseApiKey(input.request.headers)
if (!apiKey || apiKey === "public") {
async function authenticate(modelInfo: ModelInfo, zenApiKey?: string) {
if (!zenApiKey) {
if (modelInfo.allowAnonymous) return
throw new AuthError(t("zen.api.error.missingApiKey"))
}
@@ -573,7 +576,7 @@ export async function handler(
isNull(LiteTable.timeDeleted),
),
)
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
.where(and(eq(KeyTable.key, zenApiKey), isNull(KeyTable.timeDeleted)))
.then((rows) => rows[0]),
)
@@ -6,14 +6,7 @@ import { i18n } from "~/i18n"
import { localeFromRequest } from "~/lib/language"
import { Subscription } from "@opencode-ai/console-core/subscription.js"
export function createRateLimiter(
modelId: string,
allowAnonymous: boolean | undefined,
rateLimit: number | undefined,
rawIp: string,
request: Request,
) {
if (!allowAnonymous) return
export function createRateLimiter(modelId: string, rateLimit: number | undefined, rawIp: string, request: Request) {
const dict = i18n(localeFromRequest(request))
const limits = Subscription.getFreeLimits()
@@ -0,0 +1,39 @@
import { Database, eq, and, sql } from "@opencode-ai/console-core/drizzle/index.js"
import { KeyRateLimitTable } from "@opencode-ai/console-core/schema/ip.sql.js"
import { RateLimitError } from "./error"
import { i18n } from "~/i18n"
import { localeFromRequest } from "~/lib/language"
export function createRateLimiter(modelId: string, zenApiKey: string | undefined, request: Request) {
if (!zenApiKey) return
const dict = i18n(localeFromRequest(request))
const LIMIT = 100
const yyyyMMddHHmm = new Date(Date.now())
.toISOString()
.replace(/[^0-9]/g, "")
.substring(0, 12)
const interval = `${modelId.substring(0, 27)}-${yyyyMMddHHmm}`
return {
check: async () => {
const rows = await Database.use((tx) =>
tx
.select({ interval: KeyRateLimitTable.interval, count: KeyRateLimitTable.count })
.from(KeyRateLimitTable)
.where(and(eq(KeyRateLimitTable.key, zenApiKey), eq(KeyRateLimitTable.interval, interval))),
).then((rows) => rows[0])
const count = rows?.count ?? 0
if (count >= LIMIT) throw new RateLimitError(dict["zen.api.error.rateLimitExceeded"], 60)
},
track: async () => {
await Database.use((tx) =>
tx
.insert(KeyRateLimitTable)
.values({ key: zenApiKey, interval, count: 1 })
.onDuplicateKeyUpdate({ set: { count: sql`${KeyRateLimitTable.count} + 1` } }),
)
},
}
}
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test"
import { getRetryAfterDay } from "../src/routes/zen/util/rateLimiter"
import { getRetryAfterDay } from "../src/routes/zen/util/ipRateLimiter"
describe("getRetryAfterDay", () => {
test("returns full day at midnight UTC", () => {
@@ -0,0 +1,6 @@
CREATE TABLE `key_rate_limit` (
`key` varchar(255) NOT NULL,
`interval` varchar(12) NOT NULL,
`count` int NOT NULL,
CONSTRAINT PRIMARY KEY(`key`,`interval`)
);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
ALTER TABLE `key_rate_limit` MODIFY COLUMN `interval` varchar(20) NOT NULL;
@@ -0,0 +1 @@
ALTER TABLE `key_rate_limit` MODIFY COLUMN `interval` varchar(40) NOT NULL;
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.4.3",
"version": "1.4.4",
"private": true,
"type": "module",
"license": "MIT",
@@ -20,3 +20,13 @@ export const IpRateLimitTable = mysqlTable(
},
(table) => [primaryKey({ columns: [table.ip, table.interval] })],
)
export const KeyRateLimitTable = mysqlTable(
"key_rate_limit",
{
key: varchar("key", { length: 255 }).notNull(),
interval: varchar("interval", { length: 40 }).notNull(),
count: int("count").notNull(),
},
(table) => [primaryKey({ columns: [table.key, table.interval] })],
)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.4.3",
"version": "1.4.4",
"$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.4.3",
"version": "1.4.4",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop-electron",
"private": true,
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"license": "MIT",
"homepage": "https://opencode.ai",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.4.3",
"version": "1.4.4",
"private": true,
"type": "module",
"license": "MIT",
+6 -6
View File
@@ -1,7 +1,7 @@
id = "opencode"
name = "OpenCode"
description = "The open source coding agent."
version = "1.4.3"
version = "1.4.4"
schema_version = 1
authors = ["Anomaly"]
repository = "https://github.com/anomalyco/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.3/opencode-darwin-arm64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.4/opencode-darwin-arm64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.3/opencode-darwin-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.4/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.3/opencode-linux-arm64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.4/opencode-linux-arm64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.3/opencode-linux-x64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.4/opencode-linux-x64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.3/opencode-windows-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.4/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "1.4.3",
"version": "1.4.4",
"$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.4.3",
"version": "1.4.4",
"name": "opencode",
"type": "module",
"license": "MIT",
@@ -1,7 +1,12 @@
import { lazy } from "@/util/lazy"
import { Hono } from "hono"
import { QuestionHttpApiHandler } from "./question"
import { WorkspaceHttpApiHandler } from "./workspace"
export const HttpApiRoutes = lazy(() =>
new Hono().all("/question", QuestionHttpApiHandler).all("/question/*", QuestionHttpApiHandler),
new Hono()
.all("/question", QuestionHttpApiHandler)
.all("/question/*", QuestionHttpApiHandler)
.all("/workspace", WorkspaceHttpApiHandler)
.all("/workspace/*", WorkspaceHttpApiHandler),
)
@@ -0,0 +1,123 @@
import { listAdaptors } from "@/control-plane/adaptors"
import { Workspace } from "@/control-plane/workspace"
import { WorkspaceID } from "@/control-plane/schema"
import { AppLayer } from "@/effect/app-runtime"
import { memoMap } from "@/effect/run-service"
import { ProjectID } from "@/project/schema"
import { Instance } from "@/project/instance"
import { lazy } from "@/util/lazy"
import { Effect, Layer, Schema } from "effect"
import { HttpRouter, HttpServer } from "effect/unstable/http"
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import type { Handler } from "hono"
class Adaptor extends Schema.Class<Adaptor>("WorkspaceAdaptor")({
type: Schema.String,
name: Schema.String,
description: Schema.String,
}) {}
class Info extends Schema.Class<Info>("Workspace")({
id: WorkspaceID,
type: Schema.String,
name: Schema.NullOr(Schema.String),
branch: Schema.NullOr(Schema.String),
directory: Schema.NullOr(Schema.String),
extra: Schema.NullOr(Schema.Unknown),
projectID: ProjectID,
}) {}
class Status extends Schema.Class<Status>("WorkspaceConnectionStatus")({
workspaceID: WorkspaceID,
status: Schema.Union([
Schema.Literal("connected"),
Schema.Literal("connecting"),
Schema.Literal("disconnected"),
Schema.Literal("error"),
]),
error: Schema.optional(Schema.String),
}) {}
const root = "/experimental/httpapi/workspace"
const Api = HttpApi.make("workspace")
.add(
HttpApiGroup.make("workspace")
.add(
HttpApiEndpoint.get("adaptors", `${root}/adaptor`, {
success: Schema.Array(Adaptor),
}).annotateMerge(
OpenApi.annotations({
identifier: "experimental.workspace.adaptor.list",
summary: "List workspace adaptors",
description: "List all available workspace adaptors for the current project.",
}),
),
HttpApiEndpoint.get("list", root, {
success: Schema.Array(Info),
}).annotateMerge(
OpenApi.annotations({
identifier: "experimental.workspace.list",
summary: "List workspaces",
description: "List all workspaces.",
}),
),
HttpApiEndpoint.get("status", `${root}/status`, {
success: Schema.Array(Status),
}).annotateMerge(
OpenApi.annotations({
identifier: "experimental.workspace.status",
summary: "Workspace status",
description: "Get connection status for workspaces in the current project.",
}),
),
)
.annotateMerge(
OpenApi.annotations({
title: "workspace",
description: "Experimental HttpApi workspace routes.",
}),
),
)
.annotateMerge(
OpenApi.annotations({
title: "opencode experimental HttpApi",
version: "0.0.1",
description: "Experimental HttpApi surface for selected instance routes.",
}),
)
const adaptors = Effect.fn("WorkspaceHttpApi.adaptors")(function* () {
return Schema.decodeUnknownSync(Schema.Array(Adaptor))(yield* Effect.promise(() => listAdaptors(Instance.project.id)))
})
const list = Effect.fn("WorkspaceHttpApi.list")(function* () {
return Schema.decodeUnknownSync(Schema.Array(Info))(Workspace.list(Instance.project))
})
const status = Effect.fn("WorkspaceHttpApi.status")(function* () {
const ids = new Set(Workspace.list(Instance.project).map((item) => item.id))
return Schema.decodeUnknownSync(Schema.Array(Status))(Workspace.status().filter((item) => ids.has(item.workspaceID)))
})
const WorkspaceLive = HttpApiBuilder.group(Api, "workspace", (handlers) =>
handlers.handle("adaptors", adaptors).handle("list", list).handle("status", status),
)
const web = lazy(() =>
HttpRouter.toWebHandler(
Layer.mergeAll(
AppLayer,
HttpApiBuilder.layer(Api, { openapiPath: `${root}/doc` }).pipe(
Layer.provide(WorkspaceLive),
Layer.provide(HttpServer.layerServices),
),
),
{
disableLogger: true,
memoMap,
},
),
)
export const WorkspaceHttpApiHandler: Handler = (c, _next) => web().handler(c.req.raw)
@@ -0,0 +1,43 @@
import { afterEach, describe, expect, test } from "bun:test"
import { Instance } from "../../src/project/instance"
import { Server } from "../../src/server/server"
import { Log } from "../../src/util/log"
import { tmpdir } from "../fixture/fixture"
Log.init({ print: false })
afterEach(async () => {
await Instance.disposeAll()
})
describe("experimental workspace httpapi", () => {
test("lists adaptors, workspaces, status, and serves docs", async () => {
await using tmp = await tmpdir({ git: true })
const app = Server.Default().app
const headers = {
"content-type": "application/json",
"x-opencode-directory": tmp.path,
}
const adaptors = await app.request("/experimental/httpapi/workspace/adaptor", { headers })
expect(adaptors.status).toBe(200)
expect(Array.isArray(await adaptors.json())).toBe(true)
const list = await app.request("/experimental/httpapi/workspace", { headers })
expect(list.status).toBe(200)
expect(Array.isArray(await list.json())).toBe(true)
const status = await app.request("/experimental/httpapi/workspace/status", { headers })
expect(status.status).toBe(200)
expect(Array.isArray(await status.json())).toBe(true)
const doc = await app.request("/experimental/httpapi/workspace/doc", { headers })
expect(doc.status).toBe(200)
const spec = await doc.json()
expect(spec.paths["/experimental/httpapi/workspace/adaptor"]?.get?.operationId).toBe(
"experimental.workspace.adaptor.list",
)
expect(spec.paths["/experimental/httpapi/workspace"]?.get?.operationId).toBe("experimental.workspace.list")
expect(spec.paths["/experimental/httpapi/workspace/status"]?.get?.operationId).toBe("experimental.workspace.status")
})
})
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "1.4.3",
"version": "1.4.4",
"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.4.3",
"version": "1.4.4",
"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.4.3",
"version": "1.4.4",
"type": "module",
"license": "MIT",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/slack",
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"license": "MIT",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/util",
"version": "1.4.3",
"version": "1.4.4",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "@opencode-ai/web",
"type": "module",
"license": "MIT",
"version": "1.4.3",
"version": "1.4.4",
"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.4.3",
"version": "1.4.4",
"publisher": "sst-dev",
"repository": {
"type": "git",