Use server naming for shared API/events
This commit is contained in:
@@ -2,7 +2,7 @@ import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup
|
|||||||
import { ArrowUpLeft, Folder as FolderIcon, Loader2, X } from "lucide-solid"
|
import { ArrowUpLeft, Folder as FolderIcon, Loader2, X } from "lucide-solid"
|
||||||
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../server/src/api-types"
|
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../server/src/api-types"
|
||||||
import { WINDOWS_DRIVES_ROOT } from "../../../server/src/api-types"
|
import { WINDOWS_DRIVES_ROOT } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { serverApi } from "../lib/api-client"
|
||||||
|
|
||||||
function normalizePathKey(input?: string | null) {
|
function normalizePathKey(input?: string | null) {
|
||||||
if (!input || input === "." || input === "./") {
|
if (!input || input === "." || input === "./") {
|
||||||
@@ -144,7 +144,7 @@ const DirectoryBrowserDialog: Component<DirectoryBrowserDialogProps> = (props) =
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await cliApi.listFileSystem(targetPath, { includeFiles: false })
|
const response = await serverApi.listFileSystem(targetPath, { includeFiles: false })
|
||||||
const canonicalKey = normalizePathKey(response.metadata.currentPath)
|
const canonicalKey = normalizePathKey(response.metadata.currentPath)
|
||||||
const directories = response.entries
|
const directories = response.entries
|
||||||
.filter((entry) => entry.type === "directory")
|
.filter((entry) => entry.type === "directory")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
||||||
import { Folder as FolderIcon, File as FileIcon, Loader2, Search, X, ArrowUpLeft } from "lucide-solid"
|
import { Folder as FolderIcon, File as FileIcon, Loader2, Search, X, ArrowUpLeft } from "lucide-solid"
|
||||||
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../server/src/api-types"
|
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { serverApi } from "../lib/api-client"
|
||||||
|
|
||||||
const MAX_RESULTS = 200
|
const MAX_RESULTS = 200
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ const FileSystemBrowserDialog: Component<FileSystemBrowserDialogProps> = (props)
|
|||||||
|
|
||||||
const loadPromise = (async () => {
|
const loadPromise = (async () => {
|
||||||
setLoadingPath(normalized)
|
setLoadingPath(normalized)
|
||||||
const response = await cliApi.listFileSystem(normalized === "." ? "." : normalized, {
|
const response = await serverApi.listFileSystem(normalized === "." ? "." : normalized, {
|
||||||
includeFiles: props.mode === "files",
|
includeFiles: props.mode === "files",
|
||||||
})
|
})
|
||||||
directoryCache.set(normalized, response.entries)
|
directoryCache.set(normalized, response.entries)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, For, Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
import { Component, For, Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||||
import { FolderOpen, Trash2, Check, AlertCircle, Loader2, Plus } from "lucide-solid"
|
import { FolderOpen, Trash2, Check, AlertCircle, Loader2, Plus } from "lucide-solid"
|
||||||
import { useConfig } from "../stores/preferences"
|
import { useConfig } from "../stores/preferences"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { serverApi } from "../lib/api-client"
|
||||||
import FileSystemBrowserDialog from "./filesystem-browser-dialog"
|
import FileSystemBrowserDialog from "./filesystem-browser-dialog"
|
||||||
|
|
||||||
interface BinaryOption {
|
interface BinaryOption {
|
||||||
@@ -105,7 +105,7 @@ const OpenCodeBinarySelector: Component<OpenCodeBinarySelectorProps> = (props) =
|
|||||||
setValidating(true)
|
setValidating(true)
|
||||||
setValidationError(null)
|
setValidationError(null)
|
||||||
|
|
||||||
const result = await cliApi.validateBinary(path)
|
const result = await serverApi.validateBinary(path)
|
||||||
|
|
||||||
if (result.valid && result.version) {
|
if (result.valid && result.version) {
|
||||||
const updatedVersionInfo = new Map(versionInfo())
|
const updatedVersionInfo = new Map(versionInfo())
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, createSignal, createEffect, For, Show, onCleanup } from "solid-js"
|
import { Component, createSignal, createEffect, For, Show, onCleanup } from "solid-js"
|
||||||
import type { Agent } from "../types/session"
|
import type { Agent } from "../types/session"
|
||||||
import type { OpencodeClient } from "@opencode-ai/sdk/client"
|
import type { OpencodeClient } from "@opencode-ai/sdk/client"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { serverApi } from "../lib/api-client"
|
||||||
|
|
||||||
const SEARCH_RESULT_LIMIT = 100
|
const SEARCH_RESULT_LIMIT = 100
|
||||||
const SEARCH_DEBOUNCE_MS = 200
|
const SEARCH_DEBOUNCE_MS = 200
|
||||||
@@ -115,7 +115,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inflightWorkspaceId = workspaceId
|
inflightWorkspaceId = workspaceId
|
||||||
inflightSnapshotPromise = cliApi
|
inflightSnapshotPromise = serverApi
|
||||||
.listWorkspaceFiles(workspaceId)
|
.listWorkspaceFiles(workspaceId)
|
||||||
.then((entries) => mapEntriesToFileItems(entries))
|
.then((entries) => mapEntriesToFileItems(entries))
|
||||||
.then((snapshot) => {
|
.then((snapshot) => {
|
||||||
@@ -169,7 +169,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await cliApi.searchWorkspaceFiles(workspaceId, normalizedQuery, {
|
const results = await serverApi.searchWorkspaceFiles(workspaceId, normalizedQuery, {
|
||||||
limit: SEARCH_RESULT_LIMIT,
|
limit: SEARCH_RESULT_LIMIT,
|
||||||
})
|
})
|
||||||
if (!shouldApplyResults(requestId, workspaceId)) {
|
if (!shouldApplyResults(requestId, workspaceId)) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const cliApi = {
|
export const serverApi = {
|
||||||
fetchWorkspaces(): Promise<WorkspaceDescriptor[]> {
|
fetchWorkspaces(): Promise<WorkspaceDescriptor[]> {
|
||||||
return request<WorkspaceDescriptor[]>("/api/workspaces")
|
return request<WorkspaceDescriptor[]>("/api/workspaces")
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { WorkspaceEventPayload, WorkspaceEventType } from "../../../server/src/api-types"
|
import type { WorkspaceEventPayload, WorkspaceEventType } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "./api-client"
|
import { serverApi } from "./api-client"
|
||||||
|
|
||||||
const RETRY_BASE_DELAY = 1000
|
const RETRY_BASE_DELAY = 1000
|
||||||
const RETRY_MAX_DELAY = 10000
|
const RETRY_MAX_DELAY = 10000
|
||||||
@@ -13,7 +13,7 @@ function logSse(message: string, context?: Record<string, unknown>) {
|
|||||||
console.log(`${SSE_PREFIX} ${message}`)
|
console.log(`${SSE_PREFIX} ${message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
class CliEvents {
|
class ServerEvents {
|
||||||
private handlers = new Map<WorkspaceEventType | "*", Set<(event: WorkspaceEventPayload) => void>>()
|
private handlers = new Map<WorkspaceEventType | "*", Set<(event: WorkspaceEventPayload) => void>>()
|
||||||
private source: EventSource | null = null
|
private source: EventSource | null = null
|
||||||
private retryDelay = RETRY_BASE_DELAY
|
private retryDelay = RETRY_BASE_DELAY
|
||||||
@@ -27,7 +27,7 @@ class CliEvents {
|
|||||||
this.source.close()
|
this.source.close()
|
||||||
}
|
}
|
||||||
logSse("Connecting to backend events stream")
|
logSse("Connecting to backend events stream")
|
||||||
this.source = cliApi.connectEvents((event) => this.dispatch(event), () => this.scheduleReconnect())
|
this.source = serverApi.connectEvents((event) => this.dispatch(event), () => this.scheduleReconnect())
|
||||||
this.source.onopen = () => {
|
this.source.onopen = () => {
|
||||||
logSse("Events stream connected")
|
logSse("Events stream connected")
|
||||||
this.retryDelay = RETRY_BASE_DELAY
|
this.retryDelay = RETRY_BASE_DELAY
|
||||||
@@ -62,4 +62,4 @@ class CliEvents {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cliEvents = new CliEvents()
|
export const serverEvents = new ServerEvents()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ServerMeta } from "../../../server/src/api-types"
|
import type { ServerMeta } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "./api-client"
|
import { serverApi } from "./api-client"
|
||||||
|
|
||||||
let cachedMeta: ServerMeta | null = null
|
let cachedMeta: ServerMeta | null = null
|
||||||
let pendingMeta: Promise<ServerMeta> | null = null
|
let pendingMeta: Promise<ServerMeta> | null = null
|
||||||
@@ -11,7 +11,7 @@ export async function getServerMeta(): Promise<ServerMeta> {
|
|||||||
if (pendingMeta) {
|
if (pendingMeta) {
|
||||||
return pendingMeta
|
return pendingMeta
|
||||||
}
|
}
|
||||||
pendingMeta = cliApi.fetchServerMeta().then((meta) => {
|
pendingMeta = serverApi.fetchServerMeta().then((meta) => {
|
||||||
cachedMeta = meta
|
cachedMeta = meta
|
||||||
pendingMeta = null
|
pendingMeta = null
|
||||||
return meta
|
return meta
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AppConfig, InstanceData } from "../../../server/src/api-types"
|
import type { AppConfig, InstanceData } from "../../../server/src/api-types"
|
||||||
import { cliApi } from "./api-client"
|
import { serverApi } from "./api-client"
|
||||||
import { cliEvents } from "./cli-events"
|
import { serverEvents } from "./server-events"
|
||||||
|
|
||||||
export type ConfigData = AppConfig
|
export type ConfigData = AppConfig
|
||||||
|
|
||||||
@@ -35,12 +35,12 @@ export class ServerStorage {
|
|||||||
private instanceLoadPromises = new Map<string, Promise<InstanceData>>()
|
private instanceLoadPromises = new Map<string, Promise<InstanceData>>()
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
cliEvents.on("config.appChanged", (event) => {
|
serverEvents.on("config.appChanged", (event) => {
|
||||||
if (event.type !== "config.appChanged") return
|
if (event.type !== "config.appChanged") return
|
||||||
this.setConfigCache(event.config)
|
this.setConfigCache(event.config)
|
||||||
})
|
})
|
||||||
|
|
||||||
cliEvents.on("instance.dataChanged", (event) => {
|
serverEvents.on("instance.dataChanged", (event) => {
|
||||||
if (event.type !== "instance.dataChanged") return
|
if (event.type !== "instance.dataChanged") return
|
||||||
this.setInstanceDataCache(event.instanceId, event.data)
|
this.setInstanceDataCache(event.instanceId, event.data)
|
||||||
})
|
})
|
||||||
@@ -52,7 +52,7 @@ export class ServerStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.loadPromise) {
|
if (!this.loadPromise) {
|
||||||
this.loadPromise = cliApi
|
this.loadPromise = serverApi
|
||||||
.fetchConfig()
|
.fetchConfig()
|
||||||
.then((config) => {
|
.then((config) => {
|
||||||
this.setConfigCache(config)
|
this.setConfigCache(config)
|
||||||
@@ -67,7 +67,7 @@ export class ServerStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateConfig(next: ConfigData): Promise<ConfigData> {
|
async updateConfig(next: ConfigData): Promise<ConfigData> {
|
||||||
const nextConfig = await cliApi.updateConfig(next)
|
const nextConfig = await serverApi.updateConfig(next)
|
||||||
this.setConfigCache(nextConfig)
|
this.setConfigCache(nextConfig)
|
||||||
return nextConfig
|
return nextConfig
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ export class ServerStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.instanceLoadPromises.has(instanceId)) {
|
if (!this.instanceLoadPromises.has(instanceId)) {
|
||||||
const promise = cliApi
|
const promise = serverApi
|
||||||
.readInstanceData(instanceId)
|
.readInstanceData(instanceId)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const normalized = this.normalizeInstanceData(data)
|
const normalized = this.normalizeInstanceData(data)
|
||||||
@@ -98,12 +98,12 @@ export class ServerStorage {
|
|||||||
|
|
||||||
async saveInstanceData(instanceId: string, data: InstanceData): Promise<void> {
|
async saveInstanceData(instanceId: string, data: InstanceData): Promise<void> {
|
||||||
const normalized = this.normalizeInstanceData(data)
|
const normalized = this.normalizeInstanceData(data)
|
||||||
await cliApi.writeInstanceData(instanceId, normalized)
|
await serverApi.writeInstanceData(instanceId, normalized)
|
||||||
this.setInstanceDataCache(instanceId, normalized)
|
this.setInstanceDataCache(instanceId, normalized)
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteInstanceData(instanceId: string): Promise<void> {
|
async deleteInstanceData(instanceId: string): Promise<void> {
|
||||||
await cliApi.deleteInstanceData(instanceId)
|
await serverApi.deleteInstanceData(instanceId)
|
||||||
this.setInstanceDataCache(instanceId, DEFAULT_INSTANCE_DATA)
|
this.setInstanceDataCache(instanceId, DEFAULT_INSTANCE_DATA)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import type { LspStatus, Permission } from "@opencode-ai/sdk"
|
|||||||
import type { ClientPart, Message } from "../types/message"
|
import type { ClientPart, Message } from "../types/message"
|
||||||
import { sdkManager } from "../lib/sdk-manager"
|
import { sdkManager } from "../lib/sdk-manager"
|
||||||
import { sseManager } from "../lib/sse-manager"
|
import { sseManager } from "../lib/sse-manager"
|
||||||
import { cliApi } from "../lib/api-client"
|
import { serverApi } from "../lib/api-client"
|
||||||
import { cliEvents } from "../lib/cli-events"
|
import { serverEvents } from "../lib/server-events"
|
||||||
import type { WorkspaceDescriptor, WorkspaceEventPayload, WorkspaceLogEntry } from "../../../server/src/api-types"
|
import type { WorkspaceDescriptor, WorkspaceEventPayload, WorkspaceLogEntry } from "../../../server/src/api-types"
|
||||||
import { ensureInstanceConfigLoaded } from "./instance-config"
|
import { ensureInstanceConfigLoaded } from "./instance-config"
|
||||||
import {
|
import {
|
||||||
@@ -129,7 +129,7 @@ async function hydrateInstanceData(instanceId: string) {
|
|||||||
|
|
||||||
void (async function initializeWorkspaces() {
|
void (async function initializeWorkspaces() {
|
||||||
try {
|
try {
|
||||||
const workspaces = await cliApi.fetchWorkspaces()
|
const workspaces = await serverApi.fetchWorkspaces()
|
||||||
workspaces.forEach((workspace) => upsertWorkspace(workspace))
|
workspaces.forEach((workspace) => upsertWorkspace(workspace))
|
||||||
if (workspaces.length === 0) {
|
if (workspaces.length === 0) {
|
||||||
setHasInstances(false)
|
setHasInstances(false)
|
||||||
@@ -139,7 +139,7 @@ void (async function initializeWorkspaces() {
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
cliEvents.on("*", (event) => handleWorkspaceEvent(event))
|
serverEvents.on("*", (event) => handleWorkspaceEvent(event))
|
||||||
|
|
||||||
function handleWorkspaceEvent(event: WorkspaceEventPayload) {
|
function handleWorkspaceEvent(event: WorkspaceEventPayload) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
@@ -299,7 +299,7 @@ function removeInstance(id: string) {
|
|||||||
|
|
||||||
async function createInstance(folder: string, _binaryPath?: string): Promise<string> {
|
async function createInstance(folder: string, _binaryPath?: string): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const workspace = await cliApi.createWorkspace({ path: folder })
|
const workspace = await serverApi.createWorkspace({ path: folder })
|
||||||
upsertWorkspace(workspace)
|
upsertWorkspace(workspace)
|
||||||
setActiveInstanceId(workspace.id)
|
setActiveInstanceId(workspace.id)
|
||||||
return workspace.id
|
return workspace.id
|
||||||
@@ -316,7 +316,7 @@ async function stopInstance(id: string) {
|
|||||||
releaseInstanceResources(id)
|
releaseInstanceResources(id)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await cliApi.deleteWorkspace(id)
|
await serverApi.deleteWorkspace(id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to stop workspace", error)
|
console.error("Failed to stop workspace", error)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user