mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-17 22:36:21 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fc1836d0c | ||
|
|
a2594ddefb | ||
|
|
3555cef1c8 | ||
|
|
0fa881efd0 | ||
|
|
37ff501cc6 | ||
|
|
fa126d68e8 |
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"packages/ai": {
|
||||
"name": "@opencode/ai",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-providers": "3.1057.0",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -54,7 +54,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode/app",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -113,7 +113,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode/cli",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"bin": {
|
||||
"opencode2": "./bin/opencode2.cjs",
|
||||
},
|
||||
@@ -177,7 +177,7 @@
|
||||
},
|
||||
"packages/client": {
|
||||
"name": "@opencode/client",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/protocol": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -203,7 +203,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode/codemode",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -217,7 +217,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode/console-app",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -253,7 +253,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode/console-core",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -280,7 +280,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode/console-function",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opencode/console-core": "workspace:*",
|
||||
@@ -297,7 +297,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode/console-mail",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -321,7 +321,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode/console-support",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode/console-core": "workspace:*",
|
||||
@@ -341,7 +341,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode/core",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@ai-sdk/cohere": "3.0.27",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
@@ -409,7 +409,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode/desktop",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"electron-context-menu": "4.1.2",
|
||||
@@ -461,7 +461,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode/enterprise",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/sdk": "1.18.21",
|
||||
@@ -498,7 +498,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode/function",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -514,7 +514,7 @@
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode/http-recorder",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@effect/platform-node-shared": "4.0.0-rc.112",
|
||||
},
|
||||
@@ -533,7 +533,7 @@
|
||||
},
|
||||
"packages/httpapi-codegen": {
|
||||
"name": "@opencode/httpapi-codegen",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
"prettier": "3.6.2",
|
||||
@@ -546,7 +546,7 @@
|
||||
},
|
||||
"packages/latex": {
|
||||
"name": "@opencode/latex",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -560,7 +560,7 @@
|
||||
},
|
||||
"packages/merman": {
|
||||
"name": "@opencode/merman",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -575,7 +575,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode/plugin",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode/ai": "workspace:*",
|
||||
@@ -614,7 +614,7 @@
|
||||
},
|
||||
"packages/plugin-browser": {
|
||||
"name": "@opencode/plugin-browser",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -644,7 +644,7 @@
|
||||
},
|
||||
"packages/protocol": {
|
||||
"name": "@opencode/protocol",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/schema": "workspace:*",
|
||||
"effect": "catalog:",
|
||||
@@ -659,7 +659,7 @@
|
||||
},
|
||||
"packages/schema": {
|
||||
"name": "@opencode/schema",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -683,7 +683,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@opencode/sdk",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -704,7 +704,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode/server",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@effect/platform-node-shared": "catalog:",
|
||||
@@ -726,7 +726,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode/session-ui",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode/client": "workspace:*",
|
||||
@@ -761,7 +761,7 @@
|
||||
},
|
||||
"packages/simulation": {
|
||||
"name": "@opencode/simulation",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/ai": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -781,7 +781,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode/stats-app",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -815,7 +815,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode/stats-core",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -834,7 +834,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode/stats-server",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -880,7 +880,7 @@
|
||||
},
|
||||
"packages/theme": {
|
||||
"name": "@opencode/theme",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opentui/core": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -894,7 +894,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode/tui",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -929,7 +929,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode/ui",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -964,7 +964,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode/util",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@effect/opentelemetry": "catalog:",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -997,7 +997,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode/web",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1038,7 +1038,7 @@
|
||||
},
|
||||
"services/update": {
|
||||
"name": "@opencode/update",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"jose": "6.0.11",
|
||||
"semver": "catalog:",
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "opencode",
|
||||
"description": "AI-powered development tool",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.4.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"name": "@opencode/ai",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/app",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[data-titlebar-tab] {
|
||||
[data-titlebar-tab],
|
||||
[data-titlebar-tab-action] {
|
||||
--tab-base: var(--v2-background-bg-deep);
|
||||
--tab-overlay: transparent;
|
||||
background: linear-gradient(var(--tab-overlay), var(--tab-overlay)), var(--tab-base);
|
||||
@@ -68,6 +69,14 @@
|
||||
--tab-overlay: var(--v2-overlay-simple-overlay-pressed);
|
||||
}
|
||||
|
||||
[data-titlebar-tab-action]:is(:hover, :focus-visible) {
|
||||
--tab-overlay: var(--v2-overlay-simple-overlay-hover);
|
||||
}
|
||||
|
||||
[data-titlebar-tab-action]:is(:active, [data-state="pressed"]) {
|
||||
--tab-overlay: var(--v2-overlay-simple-overlay-pressed);
|
||||
}
|
||||
|
||||
[data-titlebar-tab]:is(:hover, [data-state="pressed"]) [data-slot="tab-close"] {
|
||||
background: linear-gradient(var(--tab-overlay), var(--tab-overlay)), var(--tab-base);
|
||||
}
|
||||
|
||||
@@ -351,9 +351,10 @@ export function Titlebar(props: {
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-titlebar-tab-action
|
||||
data-action="vertical-tabs-home"
|
||||
data-state={layout.route().type === "home" ? "pressed" : undefined}
|
||||
class="group mb-1 flex h-7 w-full shrink-0 items-center gap-1.5 rounded-[6px] ps-1.5 pe-2 text-[13px] leading-4 text-v2-text-text-faint hover:bg-v2-background-bg-layer-02 hover:text-v2-text-text-base data-[state=pressed]:bg-v2-background-bg-layer-02 data-[state=pressed]:text-v2-text-text-base"
|
||||
class="group mb-1 flex h-7 w-full shrink-0 items-center gap-1.5 rounded-[6px] ps-1.5 pe-2 text-[13px] leading-4 text-v2-text-text-faint hover:text-v2-text-text-base data-[state=pressed]:text-v2-text-text-base"
|
||||
onClick={toggleHome}
|
||||
aria-label={language.t("home.title")}
|
||||
aria-pressed={layout.route().type === "home"}
|
||||
@@ -650,8 +651,9 @@ export function Titlebar(props: {
|
||||
{homeButton(true)}
|
||||
<button
|
||||
type="button"
|
||||
data-titlebar-tab-action
|
||||
data-action="vertical-tabs-new-session"
|
||||
class="group flex h-7 w-full shrink-0 items-center gap-1.5 rounded-[6px] ps-1.5 pe-2 text-[13px] leading-4 text-v2-text-text-faint hover:bg-v2-background-bg-layer-02 hover:text-v2-text-text-base"
|
||||
class="group flex h-7 w-full shrink-0 items-center gap-1.5 rounded-[6px] ps-1.5 pe-2 text-[13px] leading-4 text-v2-text-text-faint hover:text-v2-text-text-base"
|
||||
onClick={openNewTab}
|
||||
aria-label={language.t("command.session.new")}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/cli",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -13,7 +13,6 @@ import { HttpServer } from "effect/unstable/http"
|
||||
import { Env } from "./env"
|
||||
import { ServiceConfig } from "./services/service-config"
|
||||
import { ServiceRegistration } from "./services/service-registration"
|
||||
import { Updater } from "./services/updater"
|
||||
import { WebUi } from "./services/web-ui"
|
||||
import { databasePath } from "./database-path"
|
||||
|
||||
@@ -159,21 +158,6 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
|
||||
const url = HttpServer.formatAddress(server.address)
|
||||
console.log(options.mode === "stdio" ? JSON.stringify({ url }) : `server listening on ${url}`)
|
||||
if (foreground && !environmentPassword) console.log(`server password ${password}`)
|
||||
yield* Updater.Service.pipe(
|
||||
Effect.flatMap((updater) =>
|
||||
Updater.pollUpdates({
|
||||
check: updater.run().pipe(
|
||||
Effect.flatMap((result) => {
|
||||
if (!result) return Effect.void
|
||||
if (result.type === "available") return server.updateAvailable(result.version)
|
||||
return server.updated(result.version)
|
||||
}),
|
||||
),
|
||||
}),
|
||||
),
|
||||
Effect.provide(Updater.layer),
|
||||
Effect.forkScoped,
|
||||
)
|
||||
return yield* options.mode === "service"
|
||||
? server.shutdown
|
||||
: options.mode === "stdio"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Global } from "@opencode/util/global"
|
||||
import { AppProcess } from "@opencode/util/process"
|
||||
import { OPENCODE_ARTIFACT, OPENCODE_CHANNEL, OPENCODE_LOCAL, OPENCODE_VERSION } from "../version"
|
||||
import { Context, Duration, Effect, FileSystem, Layer, Ref, Schedule } from "effect"
|
||||
import { Context, Duration, Effect, FileSystem, Layer, Ref } from "effect"
|
||||
import { ChildProcess } from "effect/unstable/process"
|
||||
import { parse, type ParseError } from "jsonc-parser"
|
||||
import path from "node:path"
|
||||
@@ -24,18 +24,6 @@ export interface Interface {
|
||||
| undefined
|
||||
}
|
||||
|
||||
export const pollUpdates = Effect.fnUntraced(function* (input: {
|
||||
readonly check: Effect.Effect<unknown>
|
||||
readonly initialDelay?: Duration.Input
|
||||
readonly interval?: Duration.Input
|
||||
}) {
|
||||
const interval = input.interval ?? "10 minutes"
|
||||
return yield* input.check.pipe(
|
||||
Effect.repeat(Schedule.spaced(interval)),
|
||||
Effect.delay(input.initialDelay ?? "1 minute"),
|
||||
)
|
||||
})
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/cli/Updater") {}
|
||||
|
||||
export function decodePolicy(text: string): Policy | undefined {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { expect } from "bun:test"
|
||||
import { Effect, Layer, Queue } from "effect"
|
||||
import { TestClock } from "effect/testing"
|
||||
import { testEffect } from "../../core/test/lib/effect"
|
||||
import { Updater } from "../src/services/updater"
|
||||
|
||||
const it = testEffect(Layer.empty)
|
||||
|
||||
it.effect("polls after 1 minute and every 10 minutes after that", () =>
|
||||
Effect.gen(function* () {
|
||||
const checks = yield* Queue.unbounded<void>()
|
||||
yield* Updater.pollUpdates({ check: Queue.offer(checks, undefined).pipe(Effect.asVoid) }).pipe(Effect.forkScoped)
|
||||
|
||||
yield* Effect.yieldNow
|
||||
expect(yield* Queue.size(checks)).toBe(0)
|
||||
yield* TestClock.adjust("59 seconds")
|
||||
expect(yield* Queue.size(checks)).toBe(0)
|
||||
yield* TestClock.adjust("1 second")
|
||||
yield* Queue.take(checks)
|
||||
yield* Effect.yieldNow
|
||||
yield* TestClock.adjust("10 minutes")
|
||||
yield* Queue.take(checks)
|
||||
}),
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/client",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -2135,7 +2135,7 @@ export type ConfigEntry =
|
||||
experimental?: {
|
||||
portable_shell_scanner?: boolean
|
||||
subagent_depth?: number
|
||||
policies?: Array<{ action: "provider.use"; resource: string; effect: "allow" | "deny" }>
|
||||
policies?: Array<{ action: "provider.use" | "permission"; resource: string; effect: "allow" | "deny" }>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/codemode",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"description": "Effect-native confined code execution over schema-described tools",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/console-app",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/console-core",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/console-function",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/console-mail",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/console-support",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"name": "@opencode/core",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -12,16 +12,34 @@ export const Plugin = define({
|
||||
effect: Effect.fn(function* (ctx) {
|
||||
const config = yield* Config.Service
|
||||
const loaded = yield* ConfigEntryObserver.observe(config, ctx.event, ctx.provider.reload())
|
||||
yield* ctx.provider.transform((providers) => {
|
||||
// User-global policy takes priority over policy authored by a repository.
|
||||
const policies = loaded.entries
|
||||
const policies = () =>
|
||||
loaded.entries
|
||||
.filter((entry): entry is Document => entry.type === "document")
|
||||
.toReversed()
|
||||
.flatMap((entry) => entry.info.experimental?.policies ?? [])
|
||||
yield* ctx.provider.transform((providers) => {
|
||||
// User-global policy takes priority over policy authored by a repository.
|
||||
for (const record of providers.list()) {
|
||||
const policy = policies.findLast((policy) => Wildcard.match(record.provider.id, policy.resource))
|
||||
const policy = policies().findLast(
|
||||
(policy) => policy.action === "provider.use" && Wildcard.match(record.provider.id, policy.resource),
|
||||
)
|
||||
if (policy?.effect === "deny") providers.remove(record.provider.id)
|
||||
}
|
||||
})
|
||||
yield* ctx.permission.hook("evaluate", (event) =>
|
||||
Effect.sync(() => {
|
||||
const current = policies()
|
||||
const denied = event.resources.some((resource) => {
|
||||
const policy = current.findLast(
|
||||
(policy) =>
|
||||
policy.action === "permission" && Wildcard.match(`${event.action}:${resource}`, policy.resource),
|
||||
)
|
||||
return policy?.effect === "deny"
|
||||
})
|
||||
if (!denied) return
|
||||
event.effect = "deny"
|
||||
event.message = "Blocked by configuration policy"
|
||||
}),
|
||||
)
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode/desktop",
|
||||
"private": true,
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/enterprise",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/function",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"name": "@opencode/http-recorder",
|
||||
"description": "Record and replay Effect HTTP and WebSocket traffic with deterministic cassettes",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/httpapi-codegen",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/latex",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/merman",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/plugin-browser",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"description": "OpenCode's desktop browser plugin",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/plugin",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/protocol",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/schema",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -6,7 +6,7 @@ export const Effect = Schema.Literals(["allow", "deny"])
|
||||
export type Effect = typeof Effect.Type
|
||||
|
||||
export const Info = Schema.Struct({
|
||||
action: Schema.Literal("provider.use"),
|
||||
action: Schema.Literals(["provider.use", "permission"]),
|
||||
resource: Schema.String,
|
||||
effect: Effect,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"name": "@opencode/sdk",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/server",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/session-ui",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/simulation",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/stats-app",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/stats-core",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/stats-server",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/theme",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/tui",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -11,8 +11,10 @@ import { readJson, writeJsonAtomic } from "../util/persistence"
|
||||
import {
|
||||
createModelPreferenceRepository,
|
||||
cycleModelVariant,
|
||||
favoriteModels,
|
||||
modelPreferenceKey,
|
||||
normalizeModelVariant,
|
||||
recentModels,
|
||||
type ModelPreference,
|
||||
type ModelPreferenceModel,
|
||||
} from "../model-preference"
|
||||
@@ -24,19 +26,6 @@ import { usePermission } from "./permission"
|
||||
import { useLocation } from "./location"
|
||||
import { parse } from "../util/model"
|
||||
|
||||
export function recentModels(model: ModelPreferenceModel, recent: ModelPreferenceModel[]) {
|
||||
const seen = new Set<string>()
|
||||
return [model, ...recent]
|
||||
.filter((item) => {
|
||||
const key = modelPreferenceKey(item)
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
.slice(0, 10)
|
||||
.map((item) => ({ providerID: item.providerID, modelID: item.modelID }))
|
||||
}
|
||||
|
||||
export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
name: "Local",
|
||||
init: () => {
|
||||
@@ -167,37 +156,17 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
const pendingSelectionCommits = new Map<string, { agentID: string; selection: string }>()
|
||||
const selectionKey = (value: ModelSelection) =>
|
||||
`${modelPreferenceKey(value)}:${normalizeModelVariant(value.variant) ?? "default"}`
|
||||
const saveState = {
|
||||
pending: false,
|
||||
}
|
||||
|
||||
function savePreferences() {
|
||||
if (!preferences.ready) {
|
||||
saveState.pending = true
|
||||
return
|
||||
}
|
||||
saveState.pending = false
|
||||
void repository
|
||||
.patch({
|
||||
recent: preferences.recent,
|
||||
favorite: preferences.favorite,
|
||||
variant: preferences.variant,
|
||||
})
|
||||
.catch(() => undefined)
|
||||
}
|
||||
|
||||
repository
|
||||
.load()
|
||||
.then((value) => {
|
||||
function applyPreferences(value: ModelPreference) {
|
||||
batch(() => {
|
||||
setPreferences("recent", value.recent)
|
||||
setPreferences("favorite", value.favorite)
|
||||
setPreferences("variant", value.variant)
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
setPreferences("ready", true)
|
||||
if (saveState.pending) savePreferences()
|
||||
})
|
||||
}
|
||||
|
||||
onCleanup(repository.subscribe(applyPreferences))
|
||||
|
||||
const configuredModel = createMemo(() => {
|
||||
const entry = data.location.config
|
||||
@@ -479,7 +448,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
if (!next) return
|
||||
if (!selectModel({ ...next })) return
|
||||
setPreferences("recent", recentModels(next, preferences.recent))
|
||||
savePreferences()
|
||||
void repository.addRecent(next).catch(() => undefined)
|
||||
},
|
||||
set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) {
|
||||
batch(() => {
|
||||
@@ -487,7 +456,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
if (!selectModel(model)) return
|
||||
if (options?.recent) {
|
||||
setPreferences("recent", recentModels(model, preferences.recent))
|
||||
savePreferences()
|
||||
void repository.addRecent(model).catch(() => undefined)
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -497,14 +466,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
const exists = preferences.favorite.some(
|
||||
(x) => x.providerID === model.providerID && x.modelID === model.modelID,
|
||||
)
|
||||
const next = exists
|
||||
? preferences.favorite.filter((x) => x.providerID !== model.providerID || x.modelID !== model.modelID)
|
||||
: [model, ...preferences.favorite]
|
||||
setPreferences(
|
||||
"favorite",
|
||||
next.map((x) => ({ providerID: x.providerID, modelID: x.modelID })),
|
||||
)
|
||||
savePreferences()
|
||||
setPreferences("favorite", favoriteModels(model, preferences.favorite, !exists))
|
||||
void repository.setFavorite(model, !exists).catch(() => undefined)
|
||||
})
|
||||
},
|
||||
variant: {
|
||||
@@ -527,7 +490,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
setSessionDraft(route.data.sessionID, { ...m, variant: normalizeModelVariant(value) })
|
||||
}
|
||||
setPreferences("variant", modelPreferenceKey(m), value ?? "default")
|
||||
savePreferences()
|
||||
void repository.saveVariant(m, value).catch(() => undefined)
|
||||
},
|
||||
cycle() {
|
||||
const variants = this.list()
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { readJson, writeJsonAtomic } from "./util/persistence"
|
||||
import { isRecord } from "./util/record"
|
||||
import { Flock } from "@opencode/util/flock"
|
||||
import { watch } from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
export type ModelPreferenceModel = {
|
||||
providerID: string
|
||||
@@ -43,6 +46,24 @@ export function modelPreferenceKey(model: ModelPreferenceModel) {
|
||||
return `${model.providerID}/${model.modelID}`
|
||||
}
|
||||
|
||||
export function recentModels(model: ModelPreferenceModel, recent: ModelPreferenceModel[]) {
|
||||
const seen = new Set<string>()
|
||||
return [model, ...recent]
|
||||
.filter((item) => {
|
||||
const key = modelPreferenceKey(item)
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
.slice(0, 10)
|
||||
.map((item) => ({ providerID: item.providerID, modelID: item.modelID }))
|
||||
}
|
||||
|
||||
export function favoriteModels(model: ModelPreferenceModel, favorite: ModelPreferenceModel[], enabled: boolean) {
|
||||
const current = favorite.filter((item) => modelPreferenceKey(item) !== modelPreferenceKey(model))
|
||||
return enabled ? [model, ...current] : current
|
||||
}
|
||||
|
||||
export function cycleModelVariant(current: string | undefined, variants: string[]) {
|
||||
const named = variants.filter((variant) => variant !== "default")
|
||||
if (named.length === 0) return undefined
|
||||
@@ -80,32 +101,76 @@ function patch(value: Partial<ModelPreference>) {
|
||||
}
|
||||
|
||||
export function createModelPreferenceRepository(filePath: string) {
|
||||
const state = {
|
||||
pending: Promise.resolve(),
|
||||
}
|
||||
let pending = Promise.resolve()
|
||||
let revision = 0
|
||||
let watcher: ReturnType<typeof watch> | undefined
|
||||
let reload: ReturnType<typeof setTimeout> | undefined
|
||||
const listeners = new Set<(value: ModelPreference) => void>()
|
||||
const read = () =>
|
||||
readJson<unknown>(filePath)
|
||||
.then(decodeModelPreference)
|
||||
.catch(() => decodeModelPreference(undefined))
|
||||
|
||||
function update(change: (current: ModelPreference) => Partial<ModelPreference>) {
|
||||
const result = state.pending.then(async () => {
|
||||
const current = await read()
|
||||
const next = { ...current, ...patch(change(preference(current))) }
|
||||
await writeJsonAtomic(filePath, next)
|
||||
})
|
||||
state.pending = result.catch(() => undefined)
|
||||
const result = pending.then(() =>
|
||||
Flock.withLock(
|
||||
filePath,
|
||||
async () => {
|
||||
const current = await read()
|
||||
await writeJsonAtomic(filePath, { ...current, ...patch(change(preference(current))) })
|
||||
},
|
||||
{ dir: path.join(path.dirname(filePath), "locks") },
|
||||
),
|
||||
)
|
||||
pending = result.then(
|
||||
() => undefined,
|
||||
() => undefined,
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
function load() {
|
||||
return state.pending.then(read).then(preference)
|
||||
return pending.then(read).then(preference)
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
const current = ++revision
|
||||
const value = await load()
|
||||
if (current !== revision) return
|
||||
listeners.forEach((listener) => listener(value))
|
||||
}
|
||||
|
||||
return {
|
||||
load,
|
||||
patch(value: Partial<ModelPreference>) {
|
||||
return update(() => value)
|
||||
addRecent(model: ModelPreferenceModel) {
|
||||
return update((current) => ({ recent: recentModels(model, current.recent) }))
|
||||
},
|
||||
setFavorite(model: ModelPreferenceModel, enabled: boolean) {
|
||||
return update((current) => ({ favorite: favoriteModels(model, current.favorite, enabled) }))
|
||||
},
|
||||
subscribe(listener: (value: ModelPreference) => void) {
|
||||
listeners.add(listener)
|
||||
void refresh()
|
||||
if (!watcher) {
|
||||
watcher = watch(path.dirname(filePath), (_event, filename) => {
|
||||
const changed = filename?.toString()
|
||||
const name = path.basename(filePath)
|
||||
if (changed !== undefined && changed !== name && !changed.startsWith(name + ".")) return
|
||||
clearTimeout(reload)
|
||||
reload = setTimeout(() => void refresh(), 50)
|
||||
})
|
||||
watcher.on("error", () => {
|
||||
watcher?.close()
|
||||
watcher = undefined
|
||||
})
|
||||
}
|
||||
return () => {
|
||||
listeners.delete(listener)
|
||||
if (listeners.size > 0) return
|
||||
clearTimeout(reload)
|
||||
watcher?.close()
|
||||
watcher = undefined
|
||||
}
|
||||
},
|
||||
async resolveVariant(model: ModelPreferenceModel) {
|
||||
return normalizeModelVariant((await load()).variant[modelPreferenceKey(model)])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import { recentModels } from "../../src/context/local"
|
||||
import { recentModels } from "../../src/model-preference"
|
||||
|
||||
test("moves a model to the front, deduplicates, and limits recents", () => {
|
||||
const recent = Array.from({ length: 12 }, (_, index) => ({
|
||||
|
||||
@@ -19,7 +19,7 @@ test("repairs known model preferences and preserves unrelated fields", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("atomically serializes patches and variant updates", async () => {
|
||||
test("atomically serializes model preference updates", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "model.json")
|
||||
await Bun.write(file, JSON.stringify({ unrelated: "keep", favorite: [], variant: {} }))
|
||||
@@ -28,7 +28,7 @@ test("atomically serializes patches and variant updates", async () => {
|
||||
const anthropic = { providerID: "anthropic", modelID: "claude/sonnet" }
|
||||
|
||||
await Promise.all([
|
||||
repository.patch({ recent: [openai] }),
|
||||
repository.addRecent(openai),
|
||||
repository.saveVariant(openai, "high"),
|
||||
repository.saveVariant(anthropic, "low"),
|
||||
])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/ui",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/util",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@opencode/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -10,12 +10,6 @@ const dir = fileURLToPath(new URL("..", import.meta.url))
|
||||
process.chdir(dir)
|
||||
const tag = `v${Script.version}`
|
||||
|
||||
if (Script.channel === "beta" && Script.release) {
|
||||
console.log("\n=== desktop beta release ===\n")
|
||||
await $`bun ./packages/desktop/scripts/publish.ts`
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const pkgjsons = await Array.fromAsync(
|
||||
new Bun.Glob("**/package.json").scan({
|
||||
absolute: true,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/update",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user