mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-07 06:24:50 +00:00
Compare commits
5 Commits
v1.3.17
...
opencode-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a47b0ed90 | ||
|
|
722904fe4f | ||
|
|
93cef701c0 | ||
|
|
0c3ff84f44 | ||
|
|
ba2e3c16b2 |
32
bun.lock
32
bun.lock
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -80,7 +80,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -114,7 +114,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.64",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
@@ -165,7 +165,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -189,7 +189,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -222,7 +222,7 @@
|
||||
},
|
||||
"packages/desktop-electron": {
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -254,7 +254,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
@@ -283,7 +283,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -299,7 +299,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -430,7 +430,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -464,7 +464,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -479,7 +479,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -514,7 +514,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -562,7 +562,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -573,7 +573,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -90,8 +90,7 @@ export async function handler(
|
||||
const body = await input.request.json()
|
||||
const model = opts.parseModel(url, body)
|
||||
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 ip = input.request.headers.get("x-real-ip") ?? ""
|
||||
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") ?? ""
|
||||
|
||||
@@ -17,8 +17,9 @@ export function createRateLimiter(
|
||||
const dict = i18n(localeFromRequest(request))
|
||||
|
||||
const limits = Subscription.getFreeLimits()
|
||||
const dailyLimit = rateLimit ?? limits.dailyRequests
|
||||
const isDefaultModel = !rateLimit
|
||||
const headerExists = request.headers.has(limits.checkHeader)
|
||||
const dailyLimit = !headerExists ? limits.fallbackValue : (rateLimit ?? limits.dailyRequests)
|
||||
const isDefaultModel = headerExists && !rateLimit
|
||||
|
||||
const ip = !rawIp.length ? "unknown" : rawIp
|
||||
const now = Date.now()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -9,6 +9,8 @@ export namespace Subscription {
|
||||
free: z.object({
|
||||
promoTokens: z.number().int(),
|
||||
dailyRequests: z.number().int(),
|
||||
checkHeader: z.string(),
|
||||
fallbackValue: z.number().int(),
|
||||
}),
|
||||
lite: z.object({
|
||||
rollingLimit: z.number().int(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"private": true,
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.3.17"
|
||||
version = "1.3.15"
|
||||
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.3.17/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.17/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.17/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/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.3.17/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/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.3.17/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -21,9 +21,6 @@ import {
|
||||
type Role,
|
||||
type SessionInfo,
|
||||
type SetSessionModelRequest,
|
||||
type SessionConfigOption,
|
||||
type SetSessionConfigOptionRequest,
|
||||
type SetSessionConfigOptionResponse,
|
||||
type SetSessionModeRequest,
|
||||
type SetSessionModeResponse,
|
||||
type ToolCallContent,
|
||||
@@ -604,7 +601,6 @@ export namespace ACP {
|
||||
|
||||
return {
|
||||
sessionId,
|
||||
configOptions: load.configOptions,
|
||||
models: load.models,
|
||||
modes: load.modes,
|
||||
_meta: load._meta,
|
||||
@@ -664,11 +660,6 @@ export namespace ACP {
|
||||
result.modes.currentModeId = lastUser.agent
|
||||
this.sessionManager.setMode(sessionId, lastUser.agent)
|
||||
}
|
||||
result.configOptions = buildConfigOptions({
|
||||
currentModelId: result.models.currentModelId,
|
||||
availableModels: result.models.availableModels,
|
||||
modes: result.modes,
|
||||
})
|
||||
}
|
||||
|
||||
for (const msg of messages ?? []) {
|
||||
@@ -1275,11 +1266,6 @@ export namespace ACP {
|
||||
availableModels,
|
||||
},
|
||||
modes,
|
||||
configOptions: buildConfigOptions({
|
||||
currentModelId: formatModelIdWithVariant(model, currentVariant, availableVariants, true),
|
||||
availableModels,
|
||||
modes,
|
||||
}),
|
||||
_meta: buildVariantMeta({
|
||||
model,
|
||||
variant: this.sessionManager.getVariant(sessionId),
|
||||
@@ -1319,44 +1305,6 @@ export namespace ACP {
|
||||
this.sessionManager.setMode(params.sessionId, params.modeId)
|
||||
}
|
||||
|
||||
async setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse> {
|
||||
const session = this.sessionManager.get(params.sessionId)
|
||||
const providers = await this.sdk.config
|
||||
.providers({ directory: session.cwd }, { throwOnError: true })
|
||||
.then((x) => x.data!.providers)
|
||||
const entries = sortProvidersByName(providers)
|
||||
|
||||
if (params.configId === "model") {
|
||||
if (typeof params.value !== "string") throw RequestError.invalidParams("model value must be a string")
|
||||
const selection = parseModelSelection(params.value, providers)
|
||||
this.sessionManager.setModel(session.id, selection.model)
|
||||
this.sessionManager.setVariant(session.id, selection.variant)
|
||||
} else if (params.configId === "mode") {
|
||||
if (typeof params.value !== "string") throw RequestError.invalidParams("mode value must be a string")
|
||||
const availableModes = await this.loadAvailableModes(session.cwd)
|
||||
if (!availableModes.some((mode) => mode.id === params.value)) {
|
||||
throw RequestError.invalidParams(JSON.stringify({ error: `Mode not found: ${params.value}` }))
|
||||
}
|
||||
this.sessionManager.setMode(session.id, params.value)
|
||||
} else {
|
||||
throw RequestError.invalidParams(JSON.stringify({ error: `Unknown config option: ${params.configId}` }))
|
||||
}
|
||||
|
||||
const updatedSession = this.sessionManager.get(session.id)
|
||||
const model = updatedSession.model ?? (await defaultModel(this.config, session.cwd))
|
||||
const availableVariants = modelVariantsFromProviders(entries, model)
|
||||
const currentModelId = formatModelIdWithVariant(model, updatedSession.variant, availableVariants, true)
|
||||
const availableModels = buildAvailableModels(entries, { includeVariants: true })
|
||||
const modeState = await this.resolveModeState(session.cwd, session.id)
|
||||
const modes = modeState.currentModeId
|
||||
? { availableModes: modeState.availableModes, currentModeId: modeState.currentModeId }
|
||||
: undefined
|
||||
|
||||
return {
|
||||
configOptions: buildConfigOptions({ currentModelId, availableModels, modes }),
|
||||
}
|
||||
}
|
||||
|
||||
async prompt(params: PromptRequest) {
|
||||
const sessionID = params.sessionId
|
||||
const session = this.sessionManager.get(sessionID)
|
||||
@@ -1812,36 +1760,4 @@ export namespace ACP {
|
||||
|
||||
return { model: parsed, variant: undefined }
|
||||
}
|
||||
|
||||
function buildConfigOptions(input: {
|
||||
currentModelId: string
|
||||
availableModels: ModelOption[]
|
||||
modes?: { availableModes: ModeOption[]; currentModeId: string } | undefined
|
||||
}): SessionConfigOption[] {
|
||||
const options: SessionConfigOption[] = [
|
||||
{
|
||||
id: "model",
|
||||
name: "Model",
|
||||
category: "model",
|
||||
type: "select",
|
||||
currentValue: input.currentModelId,
|
||||
options: input.availableModels.map((m) => ({ value: m.modelId, name: m.name })),
|
||||
},
|
||||
]
|
||||
if (input.modes) {
|
||||
options.push({
|
||||
id: "mode",
|
||||
name: "Session Mode",
|
||||
category: "mode",
|
||||
type: "select",
|
||||
currentValue: input.modes.currentModeId,
|
||||
options: input.modes.availableModes.map((m) => ({
|
||||
value: m.id,
|
||||
name: m.name,
|
||||
...(m.description ? { description: m.description } : {}),
|
||||
})),
|
||||
})
|
||||
}
|
||||
return options
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ export namespace Auth {
|
||||
export class Api extends Schema.Class<Api>("ApiAuth")({
|
||||
type: Schema.Literal("api"),
|
||||
key: Schema.String,
|
||||
metadata: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
||||
}) {}
|
||||
|
||||
export class WellKnown extends Schema.Class<WellKnown>("WellKnownAuth")({
|
||||
|
||||
@@ -128,11 +128,10 @@ function rendererConfig(_config: TuiConfig.Info): CliRendererConfig {
|
||||
const mouseEnabled = !Flag.OPENCODE_DISABLE_MOUSE && (_config.mouse ?? true)
|
||||
|
||||
return {
|
||||
externalOutputMode: "passthrough",
|
||||
targetFps: 60,
|
||||
gatherStats: false,
|
||||
exitOnCtrlC: false,
|
||||
useKittyKeyboard: {},
|
||||
useKittyKeyboard: { events: process.platform === "win32" },
|
||||
autoFocus: false,
|
||||
openConsoleOnError: false,
|
||||
useMouse: mouseEnabled,
|
||||
@@ -761,7 +760,6 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
|
||||
keybind: "terminal_suspend",
|
||||
category: "System",
|
||||
hidden: true,
|
||||
enabled: tuiConfig.keybinds?.terminal_suspend !== "none",
|
||||
onSelect: () => {
|
||||
process.once("SIGCONT", () => {
|
||||
renderer.resume()
|
||||
|
||||
@@ -129,15 +129,7 @@ export function createDialogProviderOptions() {
|
||||
}
|
||||
}
|
||||
if (method.type === "api") {
|
||||
let metadata: Record<string, string> | undefined
|
||||
if (method.prompts?.length) {
|
||||
const value = await PromptsMethod({ dialog, prompts: method.prompts })
|
||||
if (!value) return
|
||||
metadata = value
|
||||
}
|
||||
return dialog.replace(() => (
|
||||
<ApiMethod providerID={provider.id} title={method.label} metadata={metadata} />
|
||||
))
|
||||
return dialog.replace(() => <ApiMethod providerID={provider.id} title={method.label} />)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -257,7 +249,6 @@ function CodeMethod(props: CodeMethodProps) {
|
||||
interface ApiMethodProps {
|
||||
providerID: string
|
||||
title: string
|
||||
metadata?: Record<string, string>
|
||||
}
|
||||
function ApiMethod(props: ApiMethodProps) {
|
||||
const dialog = useDialog()
|
||||
@@ -302,7 +293,6 @@ function ApiMethod(props: ApiMethodProps) {
|
||||
auth: {
|
||||
type: "api",
|
||||
key: value,
|
||||
...(props.metadata ? { metadata: props.metadata } : {}),
|
||||
},
|
||||
})
|
||||
await sdk.client.instance.dispose()
|
||||
|
||||
@@ -18,7 +18,7 @@ import { usePromptStash } from "./stash"
|
||||
import { DialogStash } from "../dialog-stash"
|
||||
import { type AutocompleteRef, Autocomplete } from "./autocomplete"
|
||||
import { useCommandDialog } from "../dialog-command"
|
||||
import { useRenderer, type JSX } from "@opentui/solid"
|
||||
import { useKeyboard, useRenderer, type JSX } from "@opentui/solid"
|
||||
import { Editor } from "@tui/util/editor"
|
||||
import { useExit } from "../../context/exit"
|
||||
import { Clipboard } from "../../util/clipboard"
|
||||
@@ -400,6 +400,20 @@ export function Prompt(props: PromptProps) {
|
||||
]
|
||||
})
|
||||
|
||||
// Windows Terminal 1.25+ handles Ctrl+V on keydown when kitty events are
|
||||
// enabled, but still reports the kitty key-release event. Probe on release.
|
||||
if (process.platform === "win32") {
|
||||
useKeyboard(
|
||||
(evt) => {
|
||||
if (!input.focused) return
|
||||
if (evt.name === "v" && evt.ctrl && evt.eventType === "release") {
|
||||
command.trigger("prompt.paste")
|
||||
}
|
||||
},
|
||||
{ release: true },
|
||||
)
|
||||
}
|
||||
|
||||
const ref: PromptRef = {
|
||||
get focused() {
|
||||
return input.focused
|
||||
@@ -827,8 +841,20 @@ export function Prompt(props: PromptProps) {
|
||||
return !!current
|
||||
})
|
||||
|
||||
const suggestion = createMemo(() => {
|
||||
if (!props.sessionID) return
|
||||
if (store.mode !== "normal") return
|
||||
if (store.prompt.input) return
|
||||
const current = status()
|
||||
if (current.type !== "idle") return
|
||||
const value = current.suggestion?.trim()
|
||||
if (!value) return
|
||||
return value
|
||||
})
|
||||
|
||||
const placeholderText = createMemo(() => {
|
||||
if (props.showPlaceholder === false) return undefined
|
||||
if (suggestion()) return suggestion()
|
||||
if (store.mode === "shell") {
|
||||
if (!shell().length) return undefined
|
||||
const example = shell()[store.placeholder % shell().length]
|
||||
@@ -919,6 +945,16 @@ export function Prompt(props: PromptProps) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
if (!store.prompt.input && e.name === "right" && !e.ctrl && !e.meta && !e.shift && !e.super) {
|
||||
const value = suggestion()
|
||||
if (value) {
|
||||
input.setText(value)
|
||||
setStore("prompt", "input", value)
|
||||
input.gotoBufferEnd()
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check clipboard for images before terminal-handled paste runs.
|
||||
// This helps terminals that forward Ctrl+V to the app; Windows
|
||||
// Terminal 1.25+ usually handles Ctrl+V before this path.
|
||||
|
||||
@@ -233,7 +233,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
}
|
||||
|
||||
case "session.status": {
|
||||
setStore("session_status", event.properties.sessionID, event.properties.status)
|
||||
setStore("session_status", event.properties.sessionID, reconcile(event.properties.status))
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,5 @@ const TIPS = [
|
||||
"Use {highlight}/review{/highlight} to review uncommitted changes, branches, or PRs",
|
||||
"Run {highlight}/help{/highlight} or {highlight}Ctrl+X H{/highlight} to show the help dialog",
|
||||
"Use {highlight}/rename{/highlight} to rename the current session",
|
||||
...(process.platform === "win32"
|
||||
? ["Press {highlight}Ctrl+Z{/highlight} to undo changes in your prompt"]
|
||||
: ["Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell"]),
|
||||
"Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell",
|
||||
]
|
||||
|
||||
@@ -111,15 +111,7 @@ export namespace TuiConfig {
|
||||
}
|
||||
}
|
||||
|
||||
const keybinds = { ...(acc.result.keybinds ?? {}) }
|
||||
if (process.platform === "win32") {
|
||||
// Native Windows terminals do not support POSIX suspend, so prefer prompt undo.
|
||||
keybinds.terminal_suspend = "none"
|
||||
keybinds.input_undo ??= unique(["ctrl+z", ...Config.Keybinds.shape.input_undo.parse(undefined).split(",")]).join(
|
||||
",",
|
||||
)
|
||||
}
|
||||
acc.result.keybinds = Config.Keybinds.parse(keybinds)
|
||||
acc.result.keybinds = Config.Keybinds.parse(acc.result.keybinds ?? {})
|
||||
|
||||
const deps: Promise<void>[] = []
|
||||
if (acc.result.plugin?.length) {
|
||||
|
||||
@@ -73,6 +73,7 @@ export namespace Flag {
|
||||
export const OPENCODE_EXPERIMENTAL_PLAN_MODE = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_PLAN_MODE")
|
||||
export const OPENCODE_EXPERIMENTAL_WORKSPACES = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_WORKSPACES")
|
||||
export const OPENCODE_EXPERIMENTAL_MARKDOWN = !falsy("OPENCODE_EXPERIMENTAL_MARKDOWN")
|
||||
export const OPENCODE_EXPERIMENTAL_NEXT_PROMPT = truthy("OPENCODE_EXPERIMENTAL_NEXT_PROMPT")
|
||||
export const OPENCODE_MODELS_URL = process.env["OPENCODE_MODELS_URL"]
|
||||
export const OPENCODE_MODELS_PATH = process.env["OPENCODE_MODELS_PATH"]
|
||||
export const OPENCODE_DISABLE_EMBEDDED_WEB_UI = truthy("OPENCODE_DISABLE_EMBEDDED_WEB_UI")
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
export async function CloudflareWorkersAuthPlugin(_input: PluginInput): Promise<Hooks> {
|
||||
const prompts = [
|
||||
...(!process.env.CLOUDFLARE_ACCOUNT_ID
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
key: "accountId",
|
||||
message: "Enter your Cloudflare Account ID",
|
||||
placeholder: "e.g. 1234567890abcdef1234567890abcdef",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
||||
return {
|
||||
auth: {
|
||||
provider: "cloudflare-workers-ai",
|
||||
methods: [
|
||||
{
|
||||
type: "api",
|
||||
label: "API key",
|
||||
prompts,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function CloudflareAIGatewayAuthPlugin(_input: PluginInput): Promise<Hooks> {
|
||||
const prompts = [
|
||||
...(!process.env.CLOUDFLARE_ACCOUNT_ID
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
key: "accountId",
|
||||
message: "Enter your Cloudflare Account ID",
|
||||
placeholder: "e.g. 1234567890abcdef1234567890abcdef",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(!process.env.CLOUDFLARE_GATEWAY_ID
|
||||
? [
|
||||
{
|
||||
type: "text" as const,
|
||||
key: "gatewayId",
|
||||
message: "Enter your Cloudflare AI Gateway ID",
|
||||
placeholder: "e.g. my-gateway",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
||||
return {
|
||||
auth: {
|
||||
provider: "cloudflare-ai-gateway",
|
||||
methods: [
|
||||
{
|
||||
type: "api",
|
||||
label: "Gateway API token",
|
||||
prompts,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import { NamedError } from "@opencode-ai/util/error"
|
||||
import { CopilotAuthPlugin } from "./github-copilot/copilot"
|
||||
import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth"
|
||||
import { PoeAuthPlugin } from "opencode-poe-auth"
|
||||
import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cloudflare"
|
||||
import { Effect, Layer, ServiceMap, Stream } from "effect"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { makeRuntime } from "@/effect/run-service"
|
||||
@@ -47,14 +46,7 @@ export namespace Plugin {
|
||||
export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Plugin") {}
|
||||
|
||||
// Built-in plugins that are directly imported (not installed from npm)
|
||||
const INTERNAL_PLUGINS: PluginInstance[] = [
|
||||
CodexAuthPlugin,
|
||||
CopilotAuthPlugin,
|
||||
GitlabAuthPlugin,
|
||||
PoeAuthPlugin,
|
||||
CloudflareWorkersAuthPlugin,
|
||||
CloudflareAIGatewayAuthPlugin,
|
||||
]
|
||||
const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin, PoeAuthPlugin]
|
||||
|
||||
function isServerPlugin(value: unknown): value is PluginInstance {
|
||||
return typeof value === "function"
|
||||
|
||||
@@ -672,26 +672,13 @@ export namespace Provider {
|
||||
}
|
||||
}),
|
||||
"cloudflare-workers-ai": Effect.fnUntraced(function* (input: Info) {
|
||||
// When baseURL is already configured (e.g. corporate config routing through a proxy/gateway),
|
||||
// skip the account ID check because the URL is already fully specified.
|
||||
if (input.options?.baseURL) return { autoload: false }
|
||||
|
||||
const auth = yield* dep.auth(input.id)
|
||||
const accountId =
|
||||
Env.get("CLOUDFLARE_ACCOUNT_ID") || (auth?.type === "api" ? auth.metadata?.accountId : undefined)
|
||||
if (!accountId)
|
||||
return {
|
||||
autoload: false,
|
||||
async getModel() {
|
||||
throw new Error(
|
||||
"CLOUDFLARE_ACCOUNT_ID is missing. Set it with: export CLOUDFLARE_ACCOUNT_ID=<your-account-id>",
|
||||
)
|
||||
},
|
||||
}
|
||||
const accountId = Env.get("CLOUDFLARE_ACCOUNT_ID")
|
||||
if (!accountId) return { autoload: false }
|
||||
|
||||
const apiKey = yield* Effect.gen(function* () {
|
||||
const envToken = Env.get("CLOUDFLARE_API_KEY")
|
||||
if (envToken) return envToken
|
||||
const auth = yield* dep.auth(input.id)
|
||||
if (auth?.type === "api") return auth.key
|
||||
return undefined
|
||||
})
|
||||
@@ -715,34 +702,16 @@ export namespace Provider {
|
||||
}
|
||||
}),
|
||||
"cloudflare-ai-gateway": Effect.fnUntraced(function* (input: Info) {
|
||||
// When baseURL is already configured (e.g. corporate config), skip the ID checks.
|
||||
if (input.options?.baseURL) return { autoload: false }
|
||||
const accountId = Env.get("CLOUDFLARE_ACCOUNT_ID")
|
||||
const gateway = Env.get("CLOUDFLARE_GATEWAY_ID")
|
||||
|
||||
const auth = yield* dep.auth(input.id)
|
||||
const accountId =
|
||||
Env.get("CLOUDFLARE_ACCOUNT_ID") || (auth?.type === "api" ? auth.metadata?.accountId : undefined)
|
||||
const gateway =
|
||||
Env.get("CLOUDFLARE_GATEWAY_ID") || (auth?.type === "api" ? auth.metadata?.gatewayId : undefined)
|
||||
|
||||
if (!accountId || !gateway) {
|
||||
const missing = [
|
||||
!accountId ? "CLOUDFLARE_ACCOUNT_ID" : undefined,
|
||||
!gateway ? "CLOUDFLARE_GATEWAY_ID" : undefined,
|
||||
].filter((x): x is string => Boolean(x))
|
||||
return {
|
||||
autoload: false,
|
||||
async getModel() {
|
||||
throw new Error(
|
||||
`${missing.join(" and ")} missing. Set with: ${missing.map((x) => `export ${x}=<value>`).join(" && ")}`,
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
if (!accountId || !gateway) return { autoload: false }
|
||||
|
||||
// Get API token from env or auth - required for authenticated gateways
|
||||
const apiToken = yield* Effect.gen(function* () {
|
||||
const envToken = Env.get("CLOUDFLARE_API_TOKEN") || Env.get("CF_AIG_TOKEN")
|
||||
if (envToken) return envToken
|
||||
const auth = yield* dep.auth(input.id)
|
||||
if (auth?.type === "api") return auth.key
|
||||
return undefined
|
||||
})
|
||||
|
||||
@@ -936,12 +936,6 @@ export namespace ProviderTransform {
|
||||
}
|
||||
|
||||
const key = sdkKey(model.api.npm) ?? model.providerID
|
||||
// @ai-sdk/azure delegates to OpenAIChatLanguageModel which reads from
|
||||
// providerOptions["openai"], but OpenAIResponsesLanguageModel checks
|
||||
// "azure" first. Pass both so model options work on either code path.
|
||||
if (model.api.npm === "@ai-sdk/azure") {
|
||||
return { openai: options, azure: options }
|
||||
}
|
||||
return { [key]: options }
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Plugin } from "../plugin"
|
||||
import PROMPT_PLAN from "../session/prompt/plan.txt"
|
||||
import BUILD_SWITCH from "../session/prompt/build-switch.txt"
|
||||
import MAX_STEPS from "../session/prompt/max-steps.txt"
|
||||
import PROMPT_SUGGEST_NEXT from "../session/prompt/suggest-next.txt"
|
||||
import { ToolRegistry } from "../tool/registry"
|
||||
import { Runner } from "@/effect/runner"
|
||||
import { MCP } from "../mcp"
|
||||
@@ -249,6 +250,80 @@ export namespace SessionPrompt {
|
||||
)
|
||||
})
|
||||
|
||||
const suggest = Effect.fn("SessionPrompt.suggest")(function* (input: {
|
||||
session: Session.Info
|
||||
sessionID: SessionID
|
||||
message: MessageV2.WithParts
|
||||
}) {
|
||||
if (input.session.parentID) return
|
||||
const message = input.message.info
|
||||
if (message.role !== "assistant") return
|
||||
if (message.error) return
|
||||
if (!message.finish) return
|
||||
if (["tool-calls", "unknown"].includes(message.finish)) return
|
||||
if ((yield* status.get(input.sessionID)).type !== "idle") return
|
||||
|
||||
// Use the same model for prompt-cache hit on the conversation prefix
|
||||
const model = yield* Effect.promise(async () =>
|
||||
Provider.getModel(message.providerID, message.modelID).catch(() => undefined),
|
||||
)
|
||||
if (!model) return
|
||||
|
||||
const ag = yield* agents.get(message.agent ?? "code")
|
||||
if (!ag) return
|
||||
|
||||
// Full message history so the cached KV from the main conversation is reused
|
||||
const msgs = yield* MessageV2.filterCompactedEffect(input.sessionID)
|
||||
const real = (item: MessageV2.WithParts) =>
|
||||
item.info.role === "user" && !item.parts.every((part) => "synthetic" in part && part.synthetic)
|
||||
const parent = msgs.find((item) => item.info.id === message.parentID)
|
||||
const user = parent && real(parent) ? parent.info : msgs.findLast((item) => real(item))?.info
|
||||
if (!user || user.role !== "user") return
|
||||
|
||||
// Rebuild system prompt identical to the main loop for cache hit
|
||||
const skills = yield* Effect.promise(() => SystemPrompt.skills(ag))
|
||||
const env = yield* Effect.promise(() => SystemPrompt.environment(model))
|
||||
const instructions = yield* instruction.system().pipe(Effect.orDie)
|
||||
const modelMsgs = yield* Effect.promise(() => MessageV2.toModelMessages(msgs, model))
|
||||
const system = [...env, ...(skills ? [skills] : []), ...instructions]
|
||||
|
||||
const text = yield* Effect.promise(async (signal) => {
|
||||
const result = await LLM.stream({
|
||||
agent: ag,
|
||||
user,
|
||||
system,
|
||||
small: false,
|
||||
tools: {},
|
||||
model,
|
||||
abort: signal,
|
||||
sessionID: input.sessionID,
|
||||
retries: 1,
|
||||
toolChoice: "none",
|
||||
// Append suggestion instruction after the full conversation
|
||||
messages: [...modelMsgs, { role: "user" as const, content: PROMPT_SUGGEST_NEXT }],
|
||||
})
|
||||
return result.text
|
||||
})
|
||||
|
||||
const line = text
|
||||
.replace(/<think>[\s\S]*?<\/think>\s*/g, "")
|
||||
.split("\n")
|
||||
.map((item) => item.trim())
|
||||
.find((item) => item.length > 0)
|
||||
?.replace(/^["'`]+|["'`]+$/g, "")
|
||||
if (!line) return
|
||||
|
||||
const tag = line
|
||||
.toUpperCase()
|
||||
.replace(/[\s-]+/g, "_")
|
||||
.replace(/[^A-Z_]/g, "")
|
||||
if (tag === "NO_SUGGESTION") return
|
||||
|
||||
const suggestion = line.length > 110 ? line.slice(0, 107) + "..." : line
|
||||
if ((yield* status.get(input.sessionID)).type !== "idle") return
|
||||
yield* status.suggest(input.sessionID, suggestion)
|
||||
})
|
||||
|
||||
const insertReminders = Effect.fn("SessionPrompt.insertReminders")(function* (input: {
|
||||
messages: MessageV2.WithParts[]
|
||||
agent: Agent.Info
|
||||
@@ -1319,7 +1394,13 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
}
|
||||
|
||||
if (input.noReply === true) return message
|
||||
return yield* loop({ sessionID: input.sessionID })
|
||||
const result = yield* loop({ sessionID: input.sessionID })
|
||||
yield* suggest({
|
||||
session,
|
||||
sessionID: input.sessionID,
|
||||
message: result,
|
||||
}).pipe(Effect.ignore, Effect.forkIn(scope))
|
||||
return result
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
21
packages/opencode/src/session/prompt/suggest-next.txt
Normal file
21
packages/opencode/src/session/prompt/suggest-next.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
You are generating a suggested next user message for the current conversation.
|
||||
|
||||
Goal:
|
||||
- Suggest a useful next step that keeps momentum.
|
||||
|
||||
Rules:
|
||||
- Output exactly one line, 110 characters max. Be concise.
|
||||
- Write as the user speaking to the assistant (for example: "Can you...", "Help me...", "Let's...").
|
||||
- Match the user's tone and language; keep it natural and human.
|
||||
- Prefer a concrete action over a broad question.
|
||||
- If the conversation is vague or small-talk, steer toward a practical starter request.
|
||||
- If there is no meaningful or appropriate next step to suggest, output exactly: NO_SUGGESTION
|
||||
- Avoid corporate or robotic phrasing.
|
||||
- Avoid asking multiple discovery questions in one sentence.
|
||||
- Do not include quotes, labels, markdown, or explanations.
|
||||
|
||||
Examples:
|
||||
- Greeting context -> "Can you scan this repo and suggest the best first task to tackle?"
|
||||
- Bug-fix context -> "Can you reproduce this bug and propose the smallest safe fix?"
|
||||
- Feature context -> "Let's implement this incrementally; start with the MVP version first."
|
||||
- Conversation is complete -> "NO_SUGGESTION"
|
||||
@@ -11,6 +11,7 @@ export namespace SessionStatus {
|
||||
.union([
|
||||
z.object({
|
||||
type: z.literal("idle"),
|
||||
suggestion: z.string().optional(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("retry"),
|
||||
@@ -48,6 +49,7 @@ export namespace SessionStatus {
|
||||
readonly get: (sessionID: SessionID) => Effect.Effect<Info>
|
||||
readonly list: () => Effect.Effect<Map<SessionID, Info>>
|
||||
readonly set: (sessionID: SessionID, status: Info) => Effect.Effect<void>
|
||||
readonly suggest: (sessionID: SessionID, suggestion: string) => Effect.Effect<void>
|
||||
}
|
||||
|
||||
export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionStatus") {}
|
||||
@@ -81,7 +83,17 @@ export namespace SessionStatus {
|
||||
data.set(sessionID, status)
|
||||
})
|
||||
|
||||
return Service.of({ get, list, set })
|
||||
const suggest = Effect.fn("SessionStatus.suggest")(function* (sessionID: SessionID, suggestion: string) {
|
||||
const data = yield* InstanceState.get(state)
|
||||
const current = data.get(sessionID)
|
||||
if (current && current.type !== "idle") return
|
||||
const status: Info = { type: "idle", suggestion }
|
||||
// only publish Status so the TUI sees the suggestion;
|
||||
// skip Event.Idle to avoid spurious plugin notifications
|
||||
yield* bus.publish(Event.Status, { sessionID, status })
|
||||
})
|
||||
|
||||
return Service.of({ get, list, set, suggest })
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -99,4 +111,8 @@ export namespace SessionStatus {
|
||||
export async function set(sessionID: SessionID, status: Info) {
|
||||
return runPromise((svc) => svc.set(sessionID, status))
|
||||
}
|
||||
|
||||
export async function suggest(sessionID: SessionID, suggestion: string) {
|
||||
return runPromise((svc) => svc.suggest(sessionID, suggestion))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { Global } from "../../src/global"
|
||||
import { Filesystem } from "../../src/util/filesystem"
|
||||
|
||||
const managedConfigDir = process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR!
|
||||
const wintest = process.platform === "win32" ? test : test.skip
|
||||
|
||||
beforeEach(async () => {
|
||||
await Config.invalidate(true)
|
||||
@@ -442,53 +441,6 @@ test("merges keybind overrides across precedence layers", async () => {
|
||||
})
|
||||
})
|
||||
|
||||
wintest("defaults Ctrl+Z to input undo on Windows", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const config = await TuiConfig.get()
|
||||
expect(config.keybinds?.terminal_suspend).toBe("none")
|
||||
expect(config.keybinds?.input_undo).toBe("ctrl+z,ctrl+-,super+z")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
wintest("keeps explicit input undo overrides on Windows", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { input_undo: "ctrl+y" } }))
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const config = await TuiConfig.get()
|
||||
expect(config.keybinds?.terminal_suspend).toBe("none")
|
||||
expect(config.keybinds?.input_undo).toBe("ctrl+y")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
wintest("ignores terminal suspend bindings on Windows", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { terminal_suspend: "alt+z" } }))
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const config = await TuiConfig.get()
|
||||
expect(config.keybinds?.terminal_suspend).toBe("none")
|
||||
expect(config.keybinds?.input_undo).toBe("ctrl+z,ctrl+-,super+z")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("OPENCODE_TUI_CONFIG provides settings when no project config exists", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -126,6 +126,7 @@ export type EventPermissionReplied = {
|
||||
export type SessionStatus =
|
||||
| {
|
||||
type: "idle"
|
||||
suggestion?: string
|
||||
}
|
||||
| {
|
||||
type: "retry"
|
||||
@@ -1639,9 +1640,6 @@ export type OAuth = {
|
||||
export type ApiAuth = {
|
||||
type: "api"
|
||||
key: string
|
||||
metadata?: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
|
||||
export type WellKnownAuth = {
|
||||
|
||||
@@ -11621,15 +11621,6 @@
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["type", "key"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.3.17",
|
||||
"version": "1.3.15",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user