Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75cbba355e | ||
|
|
f9aa3d77cd | ||
|
|
5d47ea0918 | ||
|
|
c03fa36257 | ||
|
|
1089fa0415 | ||
|
|
715786bbf9 | ||
|
|
218eca7c2b | ||
|
|
30fc791480 | ||
|
|
e2d161dfdd | ||
|
|
23d48a7cf1 | ||
|
|
cb18f2ef40 | ||
|
|
dbe2ff52b2 | ||
|
|
9db40996cc | ||
|
|
9f201d6370 |
@@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"packages/app": {
|
"packages/app": {
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/app": {
|
"packages/console/app": {
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cloudflare/vite-plugin": "1.15.2",
|
"@cloudflare/vite-plugin": "1.15.2",
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/core": {
|
"packages/console/core": {
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sts": "3.782.0",
|
"@aws-sdk/client-sts": "3.782.0",
|
||||||
"@jsx-email/render": "1.1.1",
|
"@jsx-email/render": "1.1.1",
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/function": {
|
"packages/console/function": {
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "3.0.64",
|
"@ai-sdk/anthropic": "3.0.64",
|
||||||
"@ai-sdk/openai": "3.0.48",
|
"@ai-sdk/openai": "3.0.48",
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/mail": {
|
"packages/console/mail": {
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/app": "workspace:*",
|
"@opencode-ai/app": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop-electron": {
|
"packages/desktop-electron": {
|
||||||
"name": "@opencode-ai/desktop-electron",
|
"name": "@opencode-ai/desktop-electron",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
"electron-context-menu": "4.1.2",
|
"electron-context-menu": "4.1.2",
|
||||||
@@ -268,7 +268,7 @@
|
|||||||
},
|
},
|
||||||
"packages/enterprise": {
|
"packages/enterprise": {
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/shared": "workspace:*",
|
"@opencode-ai/shared": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
@@ -297,7 +297,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "catalog:",
|
"@octokit/rest": "catalog:",
|
||||||
@@ -313,7 +313,7 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -458,7 +458,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
@@ -493,7 +493,7 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "catalog:",
|
"cross-spawn": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -508,7 +508,7 @@
|
|||||||
},
|
},
|
||||||
"packages/shared": {
|
"packages/shared": {
|
||||||
"name": "@opencode-ai/shared",
|
"name": "@opencode-ai/shared",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -532,7 +532,7 @@
|
|||||||
},
|
},
|
||||||
"packages/slack": {
|
"packages/slack": {
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@slack/bolt": "^3.17.1",
|
"@slack/bolt": "^3.17.1",
|
||||||
@@ -567,7 +567,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -616,7 +616,7 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cloudflare": "12.6.3",
|
"@astrojs/cloudflare": "12.6.3",
|
||||||
"@astrojs/markdown-remark": "6.3.1",
|
"@astrojs/markdown-remark": "6.3.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/desktop-electron",
|
"name": "@opencode-ai/desktop-electron",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://opencode.ai",
|
"homepage": "https://opencode.ai",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
id = "opencode"
|
id = "opencode"
|
||||||
name = "OpenCode"
|
name = "OpenCode"
|
||||||
description = "The open source coding agent."
|
description = "The open source coding agent."
|
||||||
version = "1.4.6"
|
version = "1.4.7"
|
||||||
schema_version = 1
|
schema_version = 1
|
||||||
authors = ["Anomaly"]
|
authors = ["Anomaly"]
|
||||||
repository = "https://github.com/anomalyco/opencode"
|
repository = "https://github.com/anomalyco/opencode"
|
||||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
|||||||
icon = "./icons/opencode.svg"
|
icon = "./icons/opencode.svg"
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-aarch64]
|
[agent_servers.opencode.targets.darwin-aarch64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-darwin-arm64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-darwin-arm64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-x86_64]
|
[agent_servers.opencode.targets.darwin-x86_64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-darwin-x64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-darwin-x64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-aarch64]
|
[agent_servers.opencode.targets.linux-aarch64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-linux-arm64.tar.gz"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-linux-arm64.tar.gz"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-x86_64]
|
[agent_servers.opencode.targets.linux-x86_64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-linux-x64.tar.gz"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-linux-x64.tar.gz"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.windows-x86_64]
|
[agent_servers.opencode.targets.windows-x86_64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-windows-x64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-windows-x64.zip"
|
||||||
cmd = "./opencode.exe"
|
cmd = "./opencode.exe"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ for (const item of targets) {
|
|||||||
},
|
},
|
||||||
files: embeddedFileMap ? { "opencode-web-ui.gen.ts": embeddedFileMap } : {},
|
files: embeddedFileMap ? { "opencode-web-ui.gen.ts": embeddedFileMap } : {},
|
||||||
entrypoints: [
|
entrypoints: [
|
||||||
"./src/temporary.ts",
|
"./src/index.ts",
|
||||||
parserWorker,
|
parserWorker,
|
||||||
workerPath,
|
workerPath,
|
||||||
rgPath,
|
rgPath,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { useEvent } from "@tui/context/event"
|
|||||||
import { SDKProvider, useSDK } from "@tui/context/sdk"
|
import { SDKProvider, useSDK } from "@tui/context/sdk"
|
||||||
import { StartupLoading } from "@tui/component/startup-loading"
|
import { StartupLoading } from "@tui/component/startup-loading"
|
||||||
import { SyncProvider, useSync } from "@tui/context/sync"
|
import { SyncProvider, useSync } from "@tui/context/sync"
|
||||||
import { LocalProvider, parseModel, useLocal } from "@tui/context/local"
|
import { LocalProvider, useLocal } from "@tui/context/local"
|
||||||
import { DialogModel, useConnected } from "@tui/component/dialog-model"
|
import { DialogModel, useConnected } from "@tui/component/dialog-model"
|
||||||
import { DialogMcp } from "@tui/component/dialog-mcp"
|
import { DialogMcp } from "@tui/component/dialog-mcp"
|
||||||
import { DialogStatus } from "@tui/component/dialog-status"
|
import { DialogStatus } from "@tui/component/dialog-status"
|
||||||
@@ -49,8 +49,10 @@ import { DialogAlert } from "./ui/dialog-alert"
|
|||||||
import { DialogConfirm } from "./ui/dialog-confirm"
|
import { DialogConfirm } from "./ui/dialog-confirm"
|
||||||
import { ToastProvider, useToast } from "./ui/toast"
|
import { ToastProvider, useToast } from "./ui/toast"
|
||||||
import { ExitProvider, useExit } from "./context/exit"
|
import { ExitProvider, useExit } from "./context/exit"
|
||||||
|
import { Session as SessionApi } from "@/session"
|
||||||
import { TuiEvent } from "./event"
|
import { TuiEvent } from "./event"
|
||||||
import { KVProvider, useKV } from "./context/kv"
|
import { KVProvider, useKV } from "./context/kv"
|
||||||
|
import { Provider } from "@/provider"
|
||||||
import { ArgsProvider, useArgs, type Args } from "./context/args"
|
import { ArgsProvider, useArgs, type Args } from "./context/args"
|
||||||
import open from "open"
|
import open from "open"
|
||||||
import { PromptRefProvider, usePromptRef } from "./context/prompt"
|
import { PromptRefProvider, usePromptRef } from "./context/prompt"
|
||||||
@@ -302,7 +304,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
|
|||||||
|
|
||||||
if (route.data.type === "session") {
|
if (route.data.type === "session") {
|
||||||
const session = sync.session.get(route.data.sessionID)
|
const session = sync.session.get(route.data.sessionID)
|
||||||
if (!session) {
|
if (!session || SessionApi.isDefaultTitle(session.title)) {
|
||||||
renderer.setTerminalTitle("OpenCode")
|
renderer.setTerminalTitle("OpenCode")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -322,7 +324,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
|
|||||||
batch(() => {
|
batch(() => {
|
||||||
if (args.agent) local.agent.set(args.agent)
|
if (args.agent) local.agent.set(args.agent)
|
||||||
if (args.model) {
|
if (args.model) {
|
||||||
const { providerID, modelID } = parseModel(args.model)
|
const { providerID, modelID } = Provider.parseModel(args.model)
|
||||||
if (!providerID || !modelID)
|
if (!providerID || !modelID)
|
||||||
return toast.show({
|
return toast.show({
|
||||||
variant: "warning",
|
variant: "warning",
|
||||||
|
|||||||
@@ -463,6 +463,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
return store.status
|
return store.status
|
||||||
},
|
},
|
||||||
get ready() {
|
get ready() {
|
||||||
|
if (process.env.OPENCODE_FAST_BOOT) return true
|
||||||
return store.status !== "loading"
|
return store.status !== "loading"
|
||||||
},
|
},
|
||||||
get path() {
|
get path() {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
|
|
||||||
export const TuiEvent = {
|
export const TuiEvent = {
|
||||||
@@ -41,7 +42,7 @@ export const TuiEvent = {
|
|||||||
SessionSelect: BusEvent.define(
|
SessionSelect: BusEvent.define(
|
||||||
"tui.session.select",
|
"tui.session.select",
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string().describe("Session ID to navigate to"),
|
sessionID: SessionID.zod.describe("Session ID to navigate to"),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ function View(props: { api: TuiPluginApi }) {
|
|||||||
const [open, setOpen] = createSignal(true)
|
const [open, setOpen] = createSignal(true)
|
||||||
const theme = () => props.api.theme.current
|
const theme = () => props.api.theme.current
|
||||||
const list = createMemo(() => props.api.state.lsp())
|
const list = createMemo(() => props.api.state.lsp())
|
||||||
const off = createMemo(() => props.api.state.config.lsp)
|
const off = createMemo(() => props.api.state.config.lsp === false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box>
|
<box>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { TuiConfig } from "@/cli/cmd/tui/config/tui"
|
|||||||
import { Log } from "@/util"
|
import { Log } from "@/util"
|
||||||
import { errorData, errorMessage } from "@/util/error"
|
import { errorData, errorMessage } from "@/util/error"
|
||||||
import { isRecord } from "@/util/record"
|
import { isRecord } from "@/util/record"
|
||||||
|
import { Instance } from "@/project/instance"
|
||||||
import {
|
import {
|
||||||
readPackageThemes,
|
readPackageThemes,
|
||||||
readPluginId,
|
readPluginId,
|
||||||
@@ -789,7 +790,10 @@ async function addPluginBySpec(state: RuntimeState | undefined, raw: string) {
|
|||||||
state.pending.delete(spec)
|
state.pending.delete(spec)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const ready = await resolveExternalPlugins([cfg], () => TuiConfig.waitForDependencies()).catch((error) => {
|
const ready = await Instance.provide({
|
||||||
|
directory: state.directory,
|
||||||
|
fn: () => resolveExternalPlugins([cfg], () => TuiConfig.waitForDependencies()),
|
||||||
|
}).catch((error) => {
|
||||||
fail("failed to add tui plugin", { path: next, error })
|
fail("failed to add tui plugin", { path: next, error })
|
||||||
return [] as PluginLoad[]
|
return [] as PluginLoad[]
|
||||||
})
|
})
|
||||||
@@ -983,37 +987,42 @@ export namespace TuiPluginRuntime {
|
|||||||
}
|
}
|
||||||
runtime = next
|
runtime = next
|
||||||
try {
|
try {
|
||||||
const records = Flag.OPENCODE_PURE ? [] : (config.plugin_origins ?? [])
|
await Instance.provide({
|
||||||
if (Flag.OPENCODE_PURE && config.plugin_origins?.length) {
|
directory: cwd,
|
||||||
log.info("skipping external tui plugins in pure mode", { count: config.plugin_origins.length })
|
fn: async () => {
|
||||||
}
|
const records = Flag.OPENCODE_PURE ? [] : (config.plugin_origins ?? [])
|
||||||
|
if (Flag.OPENCODE_PURE && config.plugin_origins?.length) {
|
||||||
|
log.info("skipping external tui plugins in pure mode", { count: config.plugin_origins.length })
|
||||||
|
}
|
||||||
|
|
||||||
for (const item of INTERNAL_TUI_PLUGINS) {
|
for (const item of INTERNAL_TUI_PLUGINS) {
|
||||||
log.info("loading internal tui plugin", { id: item.id })
|
log.info("loading internal tui plugin", { id: item.id })
|
||||||
const entry = loadInternalPlugin(item)
|
const entry = loadInternalPlugin(item)
|
||||||
const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id)
|
const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id)
|
||||||
addPluginEntry(next, {
|
addPluginEntry(next, {
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
load: entry,
|
load: entry,
|
||||||
meta,
|
meta,
|
||||||
themes: {},
|
themes: {},
|
||||||
plugin: entry.module.tui,
|
plugin: entry.module.tui,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const ready = await resolveExternalPlugins(records, () => TuiConfig.waitForDependencies())
|
const ready = await resolveExternalPlugins(records, () => TuiConfig.waitForDependencies())
|
||||||
await addExternalPluginEntries(next, ready)
|
await addExternalPluginEntries(next, ready)
|
||||||
|
|
||||||
applyInitialPluginEnabledState(next, config)
|
applyInitialPluginEnabledState(next, config)
|
||||||
for (const plugin of next.plugins) {
|
for (const plugin of next.plugins) {
|
||||||
if (!plugin.enabled) continue
|
if (!plugin.enabled) continue
|
||||||
// Keep plugin execution sequential for deterministic side effects:
|
// Keep plugin execution sequential for deterministic side effects:
|
||||||
// command registration order affects keybind/command precedence,
|
// command registration order affects keybind/command precedence,
|
||||||
// route registration is last-wins when ids collide,
|
// route registration is last-wins when ids collide,
|
||||||
// and hook chains rely on stable plugin ordering.
|
// and hook chains rely on stable plugin ordering.
|
||||||
await activatePluginEntry(next, plugin, false)
|
await activatePluginEntry(next, plugin, false)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
fail("failed to load tui plugins", { directory: cwd, error })
|
fail("failed to load tui plugins", { directory: cwd, error })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export const memoMap = Layer.makeMemoMapUnsafe()
|
|||||||
export function makeRuntime<I, S, E>(service: Context.Service<I, S>, layer: Layer.Layer<I, E>) {
|
export function makeRuntime<I, S, E>(service: Context.Service<I, S>, layer: Layer.Layer<I, E>) {
|
||||||
let rt: ManagedRuntime.ManagedRuntime<I, E> | undefined
|
let rt: ManagedRuntime.ManagedRuntime<I, E> | undefined
|
||||||
const getRuntime = () =>
|
const getRuntime = () =>
|
||||||
(rt ??= ManagedRuntime.make(Layer.merge(layer, Observability.layer) as Layer.Layer<I, E>, { memoMap }))
|
(rt ??= ManagedRuntime.make(Layer.provideMerge(layer, Observability.layer) as Layer.Layer<I, E>, { memoMap }))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
runSync: <A, Err>(fn: (svc: S) => Effect.Effect<A, Err, I>) => getRuntime().runSync(service.use(fn)),
|
runSync: <A, Err>(fn: (svc: S) => Effect.Effect<A, Err, I>) => getRuntime().runSync(service.use(fn)),
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ export const Info = z
|
|||||||
.optional(),
|
.optional(),
|
||||||
lsp: z
|
lsp: z
|
||||||
.union([
|
.union([
|
||||||
z.literal(true),
|
z.literal(false),
|
||||||
z.record(
|
z.record(
|
||||||
z.string(),
|
z.string(),
|
||||||
z.union([
|
z.union([
|
||||||
|
|||||||
@@ -1,70 +1,68 @@
|
|||||||
import z from "zod"
|
import z from "zod"
|
||||||
|
|
||||||
export namespace ConfigMCP {
|
export const Local = z
|
||||||
export const Local = z
|
.object({
|
||||||
.object({
|
type: z.literal("local").describe("Type of MCP server connection"),
|
||||||
type: z.literal("local").describe("Type of MCP server connection"),
|
command: z.string().array().describe("Command and arguments to run the MCP server"),
|
||||||
command: z.string().array().describe("Command and arguments to run the MCP server"),
|
environment: z
|
||||||
environment: z
|
.record(z.string(), z.string())
|
||||||
.record(z.string(), z.string())
|
.optional()
|
||||||
.optional()
|
.describe("Environment variables to set when running the MCP server"),
|
||||||
.describe("Environment variables to set when running the MCP server"),
|
enabled: z.boolean().optional().describe("Enable or disable the MCP server on startup"),
|
||||||
enabled: z.boolean().optional().describe("Enable or disable the MCP server on startup"),
|
timeout: z
|
||||||
timeout: z
|
.number()
|
||||||
.number()
|
.int()
|
||||||
.int()
|
.positive()
|
||||||
.positive()
|
.optional()
|
||||||
.optional()
|
.describe("Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified."),
|
||||||
.describe("Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified."),
|
})
|
||||||
})
|
.strict()
|
||||||
.strict()
|
.meta({
|
||||||
.meta({
|
ref: "McpLocalConfig",
|
||||||
ref: "McpLocalConfig",
|
})
|
||||||
})
|
|
||||||
|
|
||||||
export const OAuth = z
|
export const OAuth = z
|
||||||
.object({
|
.object({
|
||||||
clientId: z
|
clientId: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("OAuth client ID. If not provided, dynamic client registration (RFC 7591) will be attempted."),
|
.describe("OAuth client ID. If not provided, dynamic client registration (RFC 7591) will be attempted."),
|
||||||
clientSecret: z.string().optional().describe("OAuth client secret (if required by the authorization server)"),
|
clientSecret: z.string().optional().describe("OAuth client secret (if required by the authorization server)"),
|
||||||
scope: z.string().optional().describe("OAuth scopes to request during authorization"),
|
scope: z.string().optional().describe("OAuth scopes to request during authorization"),
|
||||||
redirectUri: z
|
redirectUri: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("OAuth redirect URI (default: http://127.0.0.1:19876/mcp/oauth/callback)."),
|
.describe("OAuth redirect URI (default: http://127.0.0.1:19876/mcp/oauth/callback)."),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
.meta({
|
.meta({
|
||||||
ref: "McpOAuthConfig",
|
ref: "McpOAuthConfig",
|
||||||
})
|
})
|
||||||
export type OAuth = z.infer<typeof OAuth>
|
export type OAuth = z.infer<typeof OAuth>
|
||||||
|
|
||||||
export const Remote = z
|
export const Remote = z
|
||||||
.object({
|
.object({
|
||||||
type: z.literal("remote").describe("Type of MCP server connection"),
|
type: z.literal("remote").describe("Type of MCP server connection"),
|
||||||
url: z.string().describe("URL of the remote MCP server"),
|
url: z.string().describe("URL of the remote MCP server"),
|
||||||
enabled: z.boolean().optional().describe("Enable or disable the MCP server on startup"),
|
enabled: z.boolean().optional().describe("Enable or disable the MCP server on startup"),
|
||||||
headers: z.record(z.string(), z.string()).optional().describe("Headers to send with the request"),
|
headers: z.record(z.string(), z.string()).optional().describe("Headers to send with the request"),
|
||||||
oauth: z
|
oauth: z
|
||||||
.union([OAuth, z.literal(false)])
|
.union([OAuth, z.literal(false)])
|
||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe("OAuth authentication configuration for the MCP server. Set to false to disable OAuth auto-detection."),
|
||||||
"OAuth authentication configuration for the MCP server. Set to false to disable OAuth auto-detection.",
|
timeout: z
|
||||||
),
|
.number()
|
||||||
timeout: z
|
.int()
|
||||||
.number()
|
.positive()
|
||||||
.int()
|
.optional()
|
||||||
.positive()
|
.describe("Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified."),
|
||||||
.optional()
|
})
|
||||||
.describe("Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified."),
|
.strict()
|
||||||
})
|
.meta({
|
||||||
.strict()
|
ref: "McpRemoteConfig",
|
||||||
.meta({
|
})
|
||||||
ref: "McpRemoteConfig",
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Info = z.discriminatedUnion("type", [Local, Remote])
|
export const Info = z.discriminatedUnion("type", [Local, Remote])
|
||||||
export type Info = z.infer<typeof Info>
|
export type Info = z.infer<typeof Info>
|
||||||
}
|
|
||||||
|
export * as ConfigMCP from "./mcp"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import os from "os"
|
|||||||
import { Filesystem } from "@/util"
|
import { Filesystem } from "@/util"
|
||||||
import { InvalidError } from "./error"
|
import { InvalidError } from "./error"
|
||||||
|
|
||||||
export type ParseSource =
|
type ParseSource =
|
||||||
| {
|
| {
|
||||||
type: "path"
|
type: "path"
|
||||||
path: string
|
path: string
|
||||||
|
|||||||
@@ -19,145 +19,145 @@ import { Log } from "../util"
|
|||||||
|
|
||||||
declare const OPENCODE_LIBC: string | undefined
|
declare const OPENCODE_LIBC: string | undefined
|
||||||
|
|
||||||
const log = Log.create({ service: "file.watcher" })
|
export namespace FileWatcher {
|
||||||
const SUBSCRIBE_TIMEOUT_MS = 10_000
|
const log = Log.create({ service: "file.watcher" })
|
||||||
|
const SUBSCRIBE_TIMEOUT_MS = 10_000
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Updated: BusEvent.define(
|
Updated: BusEvent.define(
|
||||||
"file.watcher.updated",
|
"file.watcher.updated",
|
||||||
z.object({
|
z.object({
|
||||||
file: z.string(),
|
file: z.string(),
|
||||||
event: z.union([z.literal("add"), z.literal("change"), z.literal("unlink")]),
|
event: z.union([z.literal("add"), z.literal("change"), z.literal("unlink")]),
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
const watcher = lazy((): typeof import("@parcel/watcher") | undefined => {
|
|
||||||
try {
|
|
||||||
const binding = require(
|
|
||||||
`@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${OPENCODE_LIBC || "glibc"}` : ""}`,
|
|
||||||
)
|
|
||||||
return createWrapper(binding) as typeof import("@parcel/watcher")
|
|
||||||
} catch (error) {
|
|
||||||
log.error("failed to load watcher binding", { error })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function getBackend() {
|
|
||||||
if (process.platform === "win32") return "windows"
|
|
||||||
if (process.platform === "darwin") return "fs-events"
|
|
||||||
if (process.platform === "linux") return "inotify"
|
|
||||||
}
|
|
||||||
|
|
||||||
function protecteds(dir: string) {
|
|
||||||
return Protected.paths().filter((item) => {
|
|
||||||
const rel = path.relative(dir, item)
|
|
||||||
return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hasNativeBinding = () => !!watcher()
|
|
||||||
|
|
||||||
export interface Interface {
|
|
||||||
readonly init: () => Effect.Effect<void>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/FileWatcher") {}
|
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
|
||||||
Service,
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const config = yield* Config.Service
|
|
||||||
const git = yield* Git.Service
|
|
||||||
|
|
||||||
const state = yield* InstanceState.make(
|
|
||||||
Effect.fn("FileWatcher.state")(
|
|
||||||
function* () {
|
|
||||||
if (yield* Flag.OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER) return
|
|
||||||
|
|
||||||
log.info("init", { directory: Instance.directory })
|
|
||||||
|
|
||||||
const backend = getBackend()
|
|
||||||
if (!backend) {
|
|
||||||
log.error("watcher backend not supported", { directory: Instance.directory, platform: process.platform })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const w = watcher()
|
|
||||||
if (!w) return
|
|
||||||
|
|
||||||
log.info("watcher backend", { directory: Instance.directory, platform: process.platform, backend })
|
|
||||||
|
|
||||||
const subs: ParcelWatcher.AsyncSubscription[] = []
|
|
||||||
yield* Effect.addFinalizer(() =>
|
|
||||||
Effect.promise(() => Promise.allSettled(subs.map((sub) => sub.unsubscribe()))),
|
|
||||||
)
|
|
||||||
|
|
||||||
const cb: ParcelWatcher.SubscribeCallback = Instance.bind((err, evts) => {
|
|
||||||
if (err) return
|
|
||||||
for (const evt of evts) {
|
|
||||||
if (evt.type === "create") void Bus.publish(Event.Updated, { file: evt.path, event: "add" })
|
|
||||||
if (evt.type === "update") void Bus.publish(Event.Updated, { file: evt.path, event: "change" })
|
|
||||||
if (evt.type === "delete") void Bus.publish(Event.Updated, { file: evt.path, event: "unlink" })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const subscribe = (dir: string, ignore: string[]) => {
|
|
||||||
const pending = w.subscribe(dir, cb, { ignore, backend })
|
|
||||||
return Effect.gen(function* () {
|
|
||||||
const sub = yield* Effect.promise(() => pending)
|
|
||||||
subs.push(sub)
|
|
||||||
}).pipe(
|
|
||||||
Effect.timeout(SUBSCRIBE_TIMEOUT_MS),
|
|
||||||
Effect.catchCause((cause) => {
|
|
||||||
log.error("failed to subscribe", { dir, cause: Cause.pretty(cause) })
|
|
||||||
pending.then((s) => s.unsubscribe()).catch(() => {})
|
|
||||||
return Effect.void
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const cfg = yield* config.get()
|
|
||||||
const cfgIgnores = cfg.watcher?.ignore ?? []
|
|
||||||
|
|
||||||
if (yield* Flag.OPENCODE_EXPERIMENTAL_FILEWATCHER) {
|
|
||||||
yield* subscribe(Instance.directory, [
|
|
||||||
...FileIgnore.PATTERNS,
|
|
||||||
...cfgIgnores,
|
|
||||||
...protecteds(Instance.directory),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Instance.project.vcs === "git") {
|
|
||||||
const result = yield* git.run(["rev-parse", "--git-dir"], {
|
|
||||||
cwd: Instance.project.worktree,
|
|
||||||
})
|
|
||||||
const vcsDir =
|
|
||||||
result.exitCode === 0 ? path.resolve(Instance.project.worktree, result.text().trim()) : undefined
|
|
||||||
if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir)) {
|
|
||||||
const ignore = (yield* Effect.promise(() => readdir(vcsDir).catch(() => []))).filter(
|
|
||||||
(entry) => entry !== "HEAD",
|
|
||||||
)
|
|
||||||
yield* subscribe(vcsDir, ignore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Effect.catchCause((cause) => {
|
|
||||||
log.error("failed to init watcher service", { cause: Cause.pretty(cause) })
|
|
||||||
return Effect.void
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return Service.of({
|
|
||||||
init: Effect.fn("FileWatcher.init")(function* () {
|
|
||||||
yield* InstanceState.get(state)
|
|
||||||
}),
|
}),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
const watcher = lazy((): typeof import("@parcel/watcher") | undefined => {
|
||||||
|
try {
|
||||||
|
const binding = require(
|
||||||
|
`@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${OPENCODE_LIBC || "glibc"}` : ""}`,
|
||||||
|
)
|
||||||
|
return createWrapper(binding) as typeof import("@parcel/watcher")
|
||||||
|
} catch (error) {
|
||||||
|
log.error("failed to load watcher binding", { error })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function getBackend() {
|
||||||
|
if (process.platform === "win32") return "windows"
|
||||||
|
if (process.platform === "darwin") return "fs-events"
|
||||||
|
if (process.platform === "linux") return "inotify"
|
||||||
|
}
|
||||||
|
|
||||||
|
function protecteds(dir: string) {
|
||||||
|
return Protected.paths().filter((item) => {
|
||||||
|
const rel = path.relative(dir, item)
|
||||||
|
return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel)
|
||||||
})
|
})
|
||||||
}),
|
}
|
||||||
)
|
|
||||||
|
|
||||||
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(Git.defaultLayer))
|
export const hasNativeBinding = () => !!watcher()
|
||||||
|
|
||||||
export * as FileWatcher from "./watcher"
|
export interface Interface {
|
||||||
|
readonly init: () => Effect.Effect<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Service extends Context.Service<Service, Interface>()("@opencode/FileWatcher") {}
|
||||||
|
|
||||||
|
export const layer = Layer.effect(
|
||||||
|
Service,
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const config = yield* Config.Service
|
||||||
|
const git = yield* Git.Service
|
||||||
|
|
||||||
|
const state = yield* InstanceState.make(
|
||||||
|
Effect.fn("FileWatcher.state")(
|
||||||
|
function* () {
|
||||||
|
if (yield* Flag.OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER) return
|
||||||
|
|
||||||
|
log.info("init", { directory: Instance.directory })
|
||||||
|
|
||||||
|
const backend = getBackend()
|
||||||
|
if (!backend) {
|
||||||
|
log.error("watcher backend not supported", { directory: Instance.directory, platform: process.platform })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const w = watcher()
|
||||||
|
if (!w) return
|
||||||
|
|
||||||
|
log.info("watcher backend", { directory: Instance.directory, platform: process.platform, backend })
|
||||||
|
|
||||||
|
const subs: ParcelWatcher.AsyncSubscription[] = []
|
||||||
|
yield* Effect.addFinalizer(() =>
|
||||||
|
Effect.promise(() => Promise.allSettled(subs.map((sub) => sub.unsubscribe()))),
|
||||||
|
)
|
||||||
|
|
||||||
|
const cb: ParcelWatcher.SubscribeCallback = Instance.bind((err, evts) => {
|
||||||
|
if (err) return
|
||||||
|
for (const evt of evts) {
|
||||||
|
if (evt.type === "create") void Bus.publish(Event.Updated, { file: evt.path, event: "add" })
|
||||||
|
if (evt.type === "update") void Bus.publish(Event.Updated, { file: evt.path, event: "change" })
|
||||||
|
if (evt.type === "delete") void Bus.publish(Event.Updated, { file: evt.path, event: "unlink" })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const subscribe = (dir: string, ignore: string[]) => {
|
||||||
|
const pending = w.subscribe(dir, cb, { ignore, backend })
|
||||||
|
return Effect.gen(function* () {
|
||||||
|
const sub = yield* Effect.promise(() => pending)
|
||||||
|
subs.push(sub)
|
||||||
|
}).pipe(
|
||||||
|
Effect.timeout(SUBSCRIBE_TIMEOUT_MS),
|
||||||
|
Effect.catchCause((cause) => {
|
||||||
|
log.error("failed to subscribe", { dir, cause: Cause.pretty(cause) })
|
||||||
|
pending.then((s) => s.unsubscribe()).catch(() => {})
|
||||||
|
return Effect.void
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cfg = yield* config.get()
|
||||||
|
const cfgIgnores = cfg.watcher?.ignore ?? []
|
||||||
|
|
||||||
|
if (yield* Flag.OPENCODE_EXPERIMENTAL_FILEWATCHER) {
|
||||||
|
yield* subscribe(Instance.directory, [
|
||||||
|
...FileIgnore.PATTERNS,
|
||||||
|
...cfgIgnores,
|
||||||
|
...protecteds(Instance.directory),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Instance.project.vcs === "git") {
|
||||||
|
const result = yield* git.run(["rev-parse", "--git-dir"], {
|
||||||
|
cwd: Instance.project.worktree,
|
||||||
|
})
|
||||||
|
const vcsDir =
|
||||||
|
result.exitCode === 0 ? path.resolve(Instance.project.worktree, result.text().trim()) : undefined
|
||||||
|
if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir)) {
|
||||||
|
const ignore = (yield* Effect.promise(() => readdir(vcsDir).catch(() => []))).filter(
|
||||||
|
(entry) => entry !== "HEAD",
|
||||||
|
)
|
||||||
|
yield* subscribe(vcsDir, ignore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Effect.catchCause((cause) => {
|
||||||
|
log.error("failed to init watcher service", { cause: Cause.pretty(cause) })
|
||||||
|
return Effect.void
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Service.of({
|
||||||
|
init: Effect.fn("FileWatcher.init")(function* () {
|
||||||
|
yield* InstanceState.get(state)
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(Git.defaultLayer))
|
||||||
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ export const layer = Layer.effect(
|
|||||||
|
|
||||||
const servers: Record<string, LSPServer.Info> = {}
|
const servers: Record<string, LSPServer.Info> = {}
|
||||||
|
|
||||||
if (!cfg.lsp) {
|
if (cfg.lsp === false) {
|
||||||
log.info("all LSPs are disabled")
|
log.info("all LSPs are disabled")
|
||||||
} else {
|
} else {
|
||||||
for (const server of Object.values(LSPServer)) {
|
for (const server of Object.values(LSPServer)) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||||
import type { Model } from "@opencode-ai/sdk/v2"
|
import type { Model } from "@opencode-ai/sdk/v2"
|
||||||
|
import { Installation } from "@/installation"
|
||||||
import { InstallationVersion } from "@/installation/version"
|
import { InstallationVersion } from "@/installation/version"
|
||||||
import { iife } from "@/util/iife"
|
import { iife } from "@/util/iife"
|
||||||
import { Log } from "../../util"
|
import { Log } from "../../util"
|
||||||
|
|||||||
@@ -11,11 +11,6 @@ export namespace CopilotModels {
|
|||||||
// every version looks like: `{model.id}-YYYY-MM-DD`
|
// every version looks like: `{model.id}-YYYY-MM-DD`
|
||||||
version: z.string(),
|
version: z.string(),
|
||||||
supported_endpoints: z.array(z.string()).optional(),
|
supported_endpoints: z.array(z.string()).optional(),
|
||||||
policy: z
|
|
||||||
.object({
|
|
||||||
state: z.string().optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
capabilities: z.object({
|
capabilities: z.object({
|
||||||
family: z.string(),
|
family: z.string(),
|
||||||
limits: z.object({
|
limits: z.object({
|
||||||
@@ -128,9 +123,7 @@ export namespace CopilotModels {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = { ...existing }
|
const result = { ...existing }
|
||||||
const remote = new Map(
|
const remote = new Map(data.data.filter((m) => m.model_picker_enabled).map((m) => [m.id, m] as const))
|
||||||
data.data.filter((m) => m.model_picker_enabled && m.policy?.state !== "disabled").map((m) => [m.id, m] as const),
|
|
||||||
)
|
|
||||||
|
|
||||||
// prune existing models whose api.id isn't in the endpoint response
|
// prune existing models whose api.id isn't in the endpoint response
|
||||||
for (const [key, model] of Object.entries(result)) {
|
for (const [key, model] of Object.entries(result)) {
|
||||||
|
|||||||
@@ -11,164 +11,164 @@ import {
|
|||||||
import { ConfigPlugin } from "@/config/plugin"
|
import { ConfigPlugin } from "@/config/plugin"
|
||||||
import { InstallationVersion } from "@/installation/version"
|
import { InstallationVersion } from "@/installation/version"
|
||||||
|
|
||||||
export namespace PluginLoader {
|
export type Plan = {
|
||||||
export type Plan = {
|
spec: string
|
||||||
spec: string
|
options: ConfigPlugin.Options | undefined
|
||||||
options: ConfigPlugin.Options | undefined
|
deprecated: boolean
|
||||||
deprecated: boolean
|
|
||||||
}
|
|
||||||
export type Resolved = Plan & {
|
|
||||||
source: PluginSource
|
|
||||||
target: string
|
|
||||||
entry: string
|
|
||||||
pkg?: PluginPackage
|
|
||||||
}
|
|
||||||
export type Missing = Plan & {
|
|
||||||
source: PluginSource
|
|
||||||
target: string
|
|
||||||
pkg?: PluginPackage
|
|
||||||
message: string
|
|
||||||
}
|
|
||||||
export type Loaded = Resolved & {
|
|
||||||
mod: Record<string, unknown>
|
|
||||||
}
|
|
||||||
|
|
||||||
type Candidate = { origin: ConfigPlugin.Origin; plan: Plan }
|
|
||||||
type Report = {
|
|
||||||
start?: (candidate: Candidate, retry: boolean) => void
|
|
||||||
missing?: (candidate: Candidate, retry: boolean, message: string, resolved: Missing) => void
|
|
||||||
error?: (
|
|
||||||
candidate: Candidate,
|
|
||||||
retry: boolean,
|
|
||||||
stage: "install" | "entry" | "compatibility" | "load",
|
|
||||||
error: unknown,
|
|
||||||
resolved?: Resolved,
|
|
||||||
) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function plan(item: ConfigPlugin.Spec): Plan {
|
|
||||||
const spec = ConfigPlugin.pluginSpecifier(item)
|
|
||||||
return { spec, options: ConfigPlugin.pluginOptions(item), deprecated: isDeprecatedPlugin(spec) }
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function resolve(
|
|
||||||
plan: Plan,
|
|
||||||
kind: PluginKind,
|
|
||||||
): Promise<
|
|
||||||
| { ok: true; value: Resolved }
|
|
||||||
| { ok: false; stage: "missing"; value: Missing }
|
|
||||||
| { ok: false; stage: "install" | "entry" | "compatibility"; error: unknown }
|
|
||||||
> {
|
|
||||||
let target = ""
|
|
||||||
try {
|
|
||||||
target = await resolvePluginTarget(plan.spec)
|
|
||||||
} catch (error) {
|
|
||||||
return { ok: false, stage: "install", error }
|
|
||||||
}
|
|
||||||
if (!target) return { ok: false, stage: "install", error: new Error(`Plugin ${plan.spec} target is empty`) }
|
|
||||||
|
|
||||||
let base
|
|
||||||
try {
|
|
||||||
base = await createPluginEntry(plan.spec, target, kind)
|
|
||||||
} catch (error) {
|
|
||||||
return { ok: false, stage: "entry", error }
|
|
||||||
}
|
|
||||||
if (!base.entry)
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
stage: "missing",
|
|
||||||
value: {
|
|
||||||
...plan,
|
|
||||||
source: base.source,
|
|
||||||
target: base.target,
|
|
||||||
pkg: base.pkg,
|
|
||||||
message: `Plugin ${plan.spec} does not expose a ${kind} entrypoint`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base.source === "npm") {
|
|
||||||
try {
|
|
||||||
await checkPluginCompatibility(base.target, InstallationVersion, base.pkg)
|
|
||||||
} catch (error) {
|
|
||||||
return { ok: false, stage: "compatibility", error }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { ok: true, value: { ...plan, source: base.source, target: base.target, entry: base.entry, pkg: base.pkg } }
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function load(row: Resolved): Promise<{ ok: true; value: Loaded } | { ok: false; error: unknown }> {
|
|
||||||
let mod
|
|
||||||
try {
|
|
||||||
mod = await import(row.entry)
|
|
||||||
} catch (error) {
|
|
||||||
return { ok: false, error }
|
|
||||||
}
|
|
||||||
if (!mod) return { ok: false, error: new Error(`Plugin ${row.spec} module is empty`) }
|
|
||||||
return { ok: true, value: { ...row, mod } }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function attempt<R>(
|
|
||||||
candidate: Candidate,
|
|
||||||
kind: PluginKind,
|
|
||||||
retry: boolean,
|
|
||||||
finish: ((load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
|
|
||||||
missing: ((value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
|
|
||||||
report: Report | undefined,
|
|
||||||
): Promise<R | undefined> {
|
|
||||||
const plan = candidate.plan
|
|
||||||
if (plan.deprecated) return
|
|
||||||
report?.start?.(candidate, retry)
|
|
||||||
const resolved = await resolve(plan, kind)
|
|
||||||
if (!resolved.ok) {
|
|
||||||
if (resolved.stage === "missing") {
|
|
||||||
if (missing) {
|
|
||||||
const value = await missing(resolved.value, candidate.origin, retry)
|
|
||||||
if (value !== undefined) return value
|
|
||||||
}
|
|
||||||
report?.missing?.(candidate, retry, resolved.value.message, resolved.value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
report?.error?.(candidate, retry, resolved.stage, resolved.error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const loaded = await load(resolved.value)
|
|
||||||
if (!loaded.ok) {
|
|
||||||
report?.error?.(candidate, retry, "load", loaded.error, resolved.value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!finish) return loaded.value as R
|
|
||||||
return finish(loaded.value, candidate.origin, retry)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Input<R> = {
|
|
||||||
items: ConfigPlugin.Origin[]
|
|
||||||
kind: PluginKind
|
|
||||||
wait?: () => Promise<void>
|
|
||||||
finish?: (load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
|
|
||||||
missing?: (value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
|
|
||||||
report?: Report
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadExternal<R = Loaded>(input: Input<R>): Promise<R[]> {
|
|
||||||
const candidates = input.items.map((origin) => ({ origin, plan: plan(origin.spec) }))
|
|
||||||
const list: Array<Promise<R | undefined>> = []
|
|
||||||
for (const candidate of candidates) {
|
|
||||||
list.push(attempt(candidate, input.kind, false, input.finish, input.missing, input.report))
|
|
||||||
}
|
|
||||||
const out = await Promise.all(list)
|
|
||||||
if (input.wait) {
|
|
||||||
let deps: Promise<void> | undefined
|
|
||||||
for (let i = 0; i < candidates.length; i++) {
|
|
||||||
if (out[i] !== undefined) continue
|
|
||||||
const candidate = candidates[i]
|
|
||||||
if (!candidate || pluginSource(candidate.plan.spec) !== "file") continue
|
|
||||||
deps ??= input.wait()
|
|
||||||
await deps
|
|
||||||
out[i] = await attempt(candidate, input.kind, true, input.finish, input.missing, input.report)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const ready: R[] = []
|
|
||||||
for (const item of out) if (item !== undefined) ready.push(item)
|
|
||||||
return ready
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
export type Resolved = Plan & {
|
||||||
|
source: PluginSource
|
||||||
|
target: string
|
||||||
|
entry: string
|
||||||
|
pkg?: PluginPackage
|
||||||
|
}
|
||||||
|
export type Missing = Plan & {
|
||||||
|
source: PluginSource
|
||||||
|
target: string
|
||||||
|
pkg?: PluginPackage
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
export type Loaded = Resolved & {
|
||||||
|
mod: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
type Candidate = { origin: ConfigPlugin.Origin; plan: Plan }
|
||||||
|
type Report = {
|
||||||
|
start?: (candidate: Candidate, retry: boolean) => void
|
||||||
|
missing?: (candidate: Candidate, retry: boolean, message: string, resolved: Missing) => void
|
||||||
|
error?: (
|
||||||
|
candidate: Candidate,
|
||||||
|
retry: boolean,
|
||||||
|
stage: "install" | "entry" | "compatibility" | "load",
|
||||||
|
error: unknown,
|
||||||
|
resolved?: Resolved,
|
||||||
|
) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function plan(item: ConfigPlugin.Spec): Plan {
|
||||||
|
const spec = ConfigPlugin.pluginSpecifier(item)
|
||||||
|
return { spec, options: ConfigPlugin.pluginOptions(item), deprecated: isDeprecatedPlugin(spec) }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolve(
|
||||||
|
plan: Plan,
|
||||||
|
kind: PluginKind,
|
||||||
|
): Promise<
|
||||||
|
| { ok: true; value: Resolved }
|
||||||
|
| { ok: false; stage: "missing"; value: Missing }
|
||||||
|
| { ok: false; stage: "install" | "entry" | "compatibility"; error: unknown }
|
||||||
|
> {
|
||||||
|
let target = ""
|
||||||
|
try {
|
||||||
|
target = await resolvePluginTarget(plan.spec)
|
||||||
|
} catch (error) {
|
||||||
|
return { ok: false, stage: "install", error }
|
||||||
|
}
|
||||||
|
if (!target) return { ok: false, stage: "install", error: new Error(`Plugin ${plan.spec} target is empty`) }
|
||||||
|
|
||||||
|
let base
|
||||||
|
try {
|
||||||
|
base = await createPluginEntry(plan.spec, target, kind)
|
||||||
|
} catch (error) {
|
||||||
|
return { ok: false, stage: "entry", error }
|
||||||
|
}
|
||||||
|
if (!base.entry)
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
stage: "missing",
|
||||||
|
value: {
|
||||||
|
...plan,
|
||||||
|
source: base.source,
|
||||||
|
target: base.target,
|
||||||
|
pkg: base.pkg,
|
||||||
|
message: `Plugin ${plan.spec} does not expose a ${kind} entrypoint`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base.source === "npm") {
|
||||||
|
try {
|
||||||
|
await checkPluginCompatibility(base.target, InstallationVersion, base.pkg)
|
||||||
|
} catch (error) {
|
||||||
|
return { ok: false, stage: "compatibility", error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { ok: true, value: { ...plan, source: base.source, target: base.target, entry: base.entry, pkg: base.pkg } }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load(row: Resolved): Promise<{ ok: true; value: Loaded } | { ok: false; error: unknown }> {
|
||||||
|
let mod
|
||||||
|
try {
|
||||||
|
mod = await import(row.entry)
|
||||||
|
} catch (error) {
|
||||||
|
return { ok: false, error }
|
||||||
|
}
|
||||||
|
if (!mod) return { ok: false, error: new Error(`Plugin ${row.spec} module is empty`) }
|
||||||
|
return { ok: true, value: { ...row, mod } }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function attempt<R>(
|
||||||
|
candidate: Candidate,
|
||||||
|
kind: PluginKind,
|
||||||
|
retry: boolean,
|
||||||
|
finish: ((load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
|
||||||
|
missing: ((value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
|
||||||
|
report: Report | undefined,
|
||||||
|
): Promise<R | undefined> {
|
||||||
|
const plan = candidate.plan
|
||||||
|
if (plan.deprecated) return
|
||||||
|
report?.start?.(candidate, retry)
|
||||||
|
const resolved = await resolve(plan, kind)
|
||||||
|
if (!resolved.ok) {
|
||||||
|
if (resolved.stage === "missing") {
|
||||||
|
if (missing) {
|
||||||
|
const value = await missing(resolved.value, candidate.origin, retry)
|
||||||
|
if (value !== undefined) return value
|
||||||
|
}
|
||||||
|
report?.missing?.(candidate, retry, resolved.value.message, resolved.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
report?.error?.(candidate, retry, resolved.stage, resolved.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const loaded = await load(resolved.value)
|
||||||
|
if (!loaded.ok) {
|
||||||
|
report?.error?.(candidate, retry, "load", loaded.error, resolved.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!finish) return loaded.value as R
|
||||||
|
return finish(loaded.value, candidate.origin, retry)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Input<R> = {
|
||||||
|
items: ConfigPlugin.Origin[]
|
||||||
|
kind: PluginKind
|
||||||
|
wait?: () => Promise<void>
|
||||||
|
finish?: (load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
|
||||||
|
missing?: (value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
|
||||||
|
report?: Report
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadExternal<R = Loaded>(input: Input<R>): Promise<R[]> {
|
||||||
|
const candidates = input.items.map((origin) => ({ origin, plan: plan(origin.spec) }))
|
||||||
|
const list: Array<Promise<R | undefined>> = []
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
list.push(attempt(candidate, input.kind, false, input.finish, input.missing, input.report))
|
||||||
|
}
|
||||||
|
const out = await Promise.all(list)
|
||||||
|
if (input.wait) {
|
||||||
|
let deps: Promise<void> | undefined
|
||||||
|
for (let i = 0; i < candidates.length; i++) {
|
||||||
|
if (out[i] !== undefined) continue
|
||||||
|
const candidate = candidates[i]
|
||||||
|
if (!candidate || pluginSource(candidate.plan.spec) !== "file") continue
|
||||||
|
deps ??= input.wait()
|
||||||
|
await deps
|
||||||
|
out[i] = await attempt(candidate, input.kind, true, input.finish, input.missing, input.report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ready: R[] = []
|
||||||
|
for (const item of out) if (item !== undefined) ready.push(item)
|
||||||
|
return ready
|
||||||
|
}
|
||||||
|
|
||||||
|
export * as PluginLoader from "./loader"
|
||||||
|
|||||||
@@ -798,6 +798,7 @@ export function options(input: {
|
|||||||
|
|
||||||
if (input.model.api.npm === "@ai-sdk/azure") {
|
if (input.model.api.npm === "@ai-sdk/azure") {
|
||||||
result["store"] = true
|
result["store"] = true
|
||||||
|
result["promptCacheKey"] = input.sessionID
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.model.api.npm === "@openrouter/ai-sdk-provider") {
|
if (input.model.api.npm === "@openrouter/ai-sdk-provider") {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { AppRuntime } from "@/effect/app-runtime"
|
|||||||
import { AsyncQueue } from "../../util/queue"
|
import { AsyncQueue } from "../../util/queue"
|
||||||
import { errors } from "../error"
|
import { errors } from "../error"
|
||||||
import { lazy } from "../../util/lazy"
|
import { lazy } from "../../util/lazy"
|
||||||
import { SessionID } from "@/session/schema"
|
|
||||||
|
|
||||||
const TuiRequest = z.object({
|
const TuiRequest = z.object({
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
@@ -372,7 +371,7 @@ export const TuiRoutes = lazy(() =>
|
|||||||
validator("json", TuiEvent.SessionSelect.properties),
|
validator("json", TuiEvent.SessionSelect.properties),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const { sessionID } = c.req.valid("json")
|
const { sessionID } = c.req.valid("json")
|
||||||
await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(SessionID.make(sessionID))))
|
await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(sessionID)))
|
||||||
await Bus.publish(TuiEvent.SessionSelect, { sessionID })
|
await Bus.publish(TuiEvent.SessionSelect, { sessionID })
|
||||||
return c.json(true)
|
return c.json(true)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -103,12 +103,7 @@ const app = (upgrade: UpgradeWebSocket) =>
|
|||||||
|
|
||||||
const log = Log.Default.clone().tag("service", "server-proxy")
|
const log = Log.Default.clone().tag("service", "server-proxy")
|
||||||
|
|
||||||
export async function http(
|
export async function http(url: string | URL, extra: HeadersInit | undefined, req: Request, workspaceID: WorkspaceID) {
|
||||||
url: string | URL,
|
|
||||||
extra: HeadersInit | undefined,
|
|
||||||
req: Request,
|
|
||||||
workspaceID: WorkspaceID,
|
|
||||||
) {
|
|
||||||
if (!Workspace.isSyncing(workspaceID)) {
|
if (!Workspace.isSyncing(workspaceID)) {
|
||||||
return new Response(`broken sync connection for workspace: ${workspaceID}`, {
|
return new Response(`broken sync connection for workspace: ${workspaceID}`, {
|
||||||
status: 503,
|
status: 503,
|
||||||
|
|||||||
@@ -1,10 +1,33 @@
|
|||||||
|
import yargs from "yargs"
|
||||||
import { TuiThreadCommand } from "./cli/cmd/tui/thread"
|
import { TuiThreadCommand } from "./cli/cmd/tui/thread"
|
||||||
|
import { InstallationVersion } from "./installation/version"
|
||||||
|
import { hideBin } from "yargs/helpers"
|
||||||
import { Log } from "./node"
|
import { Log } from "./node"
|
||||||
|
|
||||||
Log.init({
|
Log.init({
|
||||||
print: false,
|
print: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(TuiThreadCommand)
|
const cli = yargs(hideBin(process.argv))
|
||||||
|
.parserConfiguration({ "populate--": true })
|
||||||
console.log(performance.now())
|
.scriptName("opencode")
|
||||||
|
.wrap(100)
|
||||||
|
.help("help", "show help")
|
||||||
|
.alias("help", "h")
|
||||||
|
.version("version", "show version number", InstallationVersion)
|
||||||
|
.alias("version", "v")
|
||||||
|
.option("print-logs", {
|
||||||
|
describe: "print logs to stderr",
|
||||||
|
type: "boolean",
|
||||||
|
})
|
||||||
|
.option("log-level", {
|
||||||
|
describe: "log level",
|
||||||
|
type: "string",
|
||||||
|
choices: ["DEBUG", "INFO", "WARN", "ERROR"],
|
||||||
|
})
|
||||||
|
.option("pure", {
|
||||||
|
describe: "run without external plugins",
|
||||||
|
type: "boolean",
|
||||||
|
})
|
||||||
|
.command(TuiThreadCommand)
|
||||||
|
.parse()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1601,7 +1601,7 @@ export type Config = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lsp?:
|
lsp?:
|
||||||
| true
|
| false
|
||||||
| {
|
| {
|
||||||
[key: string]:
|
[key: string]:
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -6938,7 +6938,8 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"sessionID": {
|
"sessionID": {
|
||||||
"description": "Session ID to navigate to",
|
"description": "Session ID to navigate to",
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"pattern": "^ses.*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["sessionID"]
|
"required": ["sessionID"]
|
||||||
@@ -8510,7 +8511,8 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"sessionID": {
|
"sessionID": {
|
||||||
"description": "Session ID to navigate to",
|
"description": "Session ID to navigate to",
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"pattern": "^ses.*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["sessionID"]
|
"required": ["sessionID"]
|
||||||
@@ -11759,7 +11761,7 @@
|
|||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"const": true
|
"const": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"name": "@opencode-ai/shared",
|
"name": "@opencode-ai/shared",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"displayName": "opencode",
|
"displayName": "opencode",
|
||||||
"description": "opencode for VS Code",
|
"description": "opencode for VS Code",
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"publisher": "sst-dev",
|
"publisher": "sst-dev",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
Reference in New Issue
Block a user