mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-06 17:19:49 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c09c2f17f | |||
| c4132543c3 |
@@ -10,6 +10,7 @@ import { useGlobalSDK } from "@/context/global-sdk"
|
|||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
import { useLayout } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
|
import { projectDirectories } from "@/pages/layout/helpers"
|
||||||
|
|
||||||
interface DialogSelectDirectoryProps {
|
interface DialogSelectDirectoryProps {
|
||||||
title?: string
|
title?: string
|
||||||
@@ -284,7 +285,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
|||||||
|
|
||||||
for (const project of projects) {
|
for (const project of projects) {
|
||||||
let at = 0
|
let at = 0
|
||||||
const dirs = [project.worktree, ...(project.sandboxes ?? [])]
|
const dirs = projectDirectories(project)
|
||||||
for (const directory of dirs) {
|
for (const directory of dirs) {
|
||||||
const sessions = sync.child(directory, { bootstrap: false })[0].session
|
const sessions = sync.child(directory, { bootstrap: false })[0].session
|
||||||
for (const session of sessions) {
|
for (const session of sessions) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { useFile } from "@/context/file"
|
|||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { useSessionLayout } from "@/pages/session/session-layout"
|
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||||
import { createSessionTabs } from "@/pages/session/helpers"
|
import { createSessionTabs } from "@/pages/session/helpers"
|
||||||
|
import { projectDirectories } from "@/pages/layout/helpers"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
import { getRelativeTime } from "@/utils/time"
|
import { getRelativeTime } from "@/utils/time"
|
||||||
|
|
||||||
@@ -280,14 +281,14 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||||||
const project = createMemo(() => {
|
const project = createMemo(() => {
|
||||||
const directory = projectDirectory()
|
const directory = projectDirectory()
|
||||||
if (!directory) return
|
if (!directory) return
|
||||||
return layout.projects.list().find((p) => p.worktree === directory || p.sandboxes?.includes(directory))
|
return layout.projects.list().find((p) => p.worktree === directory || p.worktrees?.includes(directory))
|
||||||
})
|
})
|
||||||
const workspaces = createMemo(() => {
|
const workspaces = createMemo(() => {
|
||||||
const directory = projectDirectory()
|
const directory = projectDirectory()
|
||||||
const current = project()
|
const current = project()
|
||||||
if (!current) return directory ? [directory] : []
|
if (!current) return directory ? [directory] : []
|
||||||
|
|
||||||
const dirs = [current.worktree, ...(current.sandboxes ?? [])]
|
const dirs = projectDirectories(current)
|
||||||
if (directory && !dirs.includes(directory)) return [...dirs, directory]
|
if (directory && !dirs.includes(directory)) return [...dirs, directory]
|
||||||
return dirs
|
return dirs
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -342,6 +342,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
WorktreeState.pending(createdWorktree.directory)
|
WorktreeState.pending(createdWorktree.directory)
|
||||||
|
globalSync.project.addWorktree(projectDirectory, createdWorktree.directory)
|
||||||
sessionDirectory = createdWorktree.directory
|
sessionDirectory = createdWorktree.directory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export function SessionHeader() {
|
|||||||
const project = createMemo(() => {
|
const project = createMemo(() => {
|
||||||
const directory = projectDirectory()
|
const directory = projectDirectory()
|
||||||
if (!directory) return
|
if (!directory) return
|
||||||
return layout.projects.list().find((p) => p.worktree === directory || p.sandboxes?.includes(directory))
|
return layout.projects.list().find((p) => p.worktree === directory || p.worktrees?.includes(directory))
|
||||||
})
|
})
|
||||||
const name = createMemo(() => {
|
const name = createMemo(() => {
|
||||||
const current = project()
|
const current = project()
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export function NewSessionView(props: NewSessionViewProps) {
|
|||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
|
|
||||||
const sandboxes = createMemo(() => sync.project?.sandboxes ?? [])
|
const worktrees = createMemo(() => sync.project?.worktrees ?? [])
|
||||||
const options = createMemo(() => [MAIN_WORKTREE, ...sandboxes(), CREATE_WORKTREE])
|
const options = createMemo(() => [MAIN_WORKTREE, ...worktrees(), CREATE_WORKTREE])
|
||||||
const current = createMemo(() => {
|
const current = createMemo(() => {
|
||||||
const selection = props.worktree
|
const selection = props.worktree
|
||||||
if (options().includes(selection)) return selection
|
if (options().includes(selection)) return selection
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type {
|
|||||||
Config,
|
Config,
|
||||||
OpencodeClient,
|
OpencodeClient,
|
||||||
Path,
|
Path,
|
||||||
Project,
|
|
||||||
ProviderAuthResponse,
|
ProviderAuthResponse,
|
||||||
ProviderListResponse,
|
ProviderListResponse,
|
||||||
Todo,
|
Todo,
|
||||||
@@ -27,18 +26,19 @@ import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } fr
|
|||||||
import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch"
|
import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch"
|
||||||
import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load"
|
import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load"
|
||||||
import { trimSessions } from "./global-sync/session-trim"
|
import { trimSessions } from "./global-sync/session-trim"
|
||||||
import type { ProjectMeta } from "./global-sync/types"
|
import type { ProjectInfo, ProjectMeta } from "./global-sync/types"
|
||||||
import { SESSION_RECENT_LIMIT } from "./global-sync/types"
|
import { SESSION_RECENT_LIMIT } from "./global-sync/types"
|
||||||
import { formatServerError } from "@/utils/server-errors"
|
import { formatServerError } from "@/utils/server-errors"
|
||||||
import { queryOptions, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/solid-query"
|
import { queryOptions, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/solid-query"
|
||||||
import { createRefreshQueue } from "./global-sync/queue"
|
import { createRefreshQueue } from "./global-sync/queue"
|
||||||
import { directoryKey } from "./global-sync/utils"
|
import { directoryKey } from "./global-sync/utils"
|
||||||
|
import { pathKey } from "@/utils/path-key"
|
||||||
|
|
||||||
type GlobalStore = {
|
type GlobalStore = {
|
||||||
ready: boolean
|
ready: boolean
|
||||||
error?: InitError
|
error?: InitError
|
||||||
path: Path
|
path: Path
|
||||||
project: Project[]
|
project: ProjectInfo[]
|
||||||
session_todo: {
|
session_todo: {
|
||||||
[sessionID: string]: Todo[]
|
[sessionID: string]: Todo[]
|
||||||
}
|
}
|
||||||
@@ -122,18 +122,6 @@ function createGlobalSync() {
|
|||||||
if (eventTimer !== undefined) clearTimeout(eventTimer)
|
if (eventTimer !== undefined) clearTimeout(eventTimer)
|
||||||
})
|
})
|
||||||
|
|
||||||
const setProjects = (next: Project[] | ((draft: Project[]) => Project[])) => {
|
|
||||||
setGlobalStore("project", next)
|
|
||||||
}
|
|
||||||
|
|
||||||
const setBootStore = ((...input: unknown[]) => {
|
|
||||||
if (input[0] === "project" && Array.isArray(input[1])) {
|
|
||||||
setProjects(input[1] as Project[])
|
|
||||||
return input[1]
|
|
||||||
}
|
|
||||||
return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
|
|
||||||
}) as typeof setGlobalStore
|
|
||||||
|
|
||||||
const bootstrap = useQuery(() => ({
|
const bootstrap = useQuery(() => ({
|
||||||
queryKey: ["bootstrap"],
|
queryKey: ["bootstrap"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
@@ -142,7 +130,7 @@ function createGlobalSync() {
|
|||||||
requestFailedTitle: language.t("common.requestFailed"),
|
requestFailedTitle: language.t("common.requestFailed"),
|
||||||
translate: language.t,
|
translate: language.t,
|
||||||
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
|
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
|
||||||
setGlobalStore: setBootStore,
|
setGlobalStore,
|
||||||
queryClient,
|
queryClient,
|
||||||
})
|
})
|
||||||
bootedAt = Date.now()
|
bootedAt = Date.now()
|
||||||
@@ -150,13 +138,31 @@ function createGlobalSync() {
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const set = ((...input: unknown[]) => {
|
const set = setGlobalStore
|
||||||
if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) {
|
|
||||||
setProjects(input[1] as Project[] | ((draft: Project[]) => Project[]))
|
const addProjectWorktree = (projectWorktree: string, directory: string) => {
|
||||||
return input[1]
|
setGlobalStore(
|
||||||
}
|
"project",
|
||||||
return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
|
produce((draft) => {
|
||||||
}) as typeof setGlobalStore
|
const project = draft.find((item) => item.worktree === projectWorktree)
|
||||||
|
if (!project) return
|
||||||
|
const key = pathKey(directory)
|
||||||
|
project.worktrees = [directory, ...(project.worktrees ?? []).filter((item) => pathKey(item) !== key)]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeProjectWorktree = (projectWorktree: string, directory: string) => {
|
||||||
|
setGlobalStore(
|
||||||
|
"project",
|
||||||
|
produce((draft) => {
|
||||||
|
const project = draft.find((item) => item.worktree === projectWorktree)
|
||||||
|
if (!project) return
|
||||||
|
const key = pathKey(directory)
|
||||||
|
project.worktrees = (project.worktrees ?? []).filter((item) => pathKey(item) !== key)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const setSessionTodo = (sessionID: string, todos: Todo[] | undefined) => {
|
const setSessionTodo = (sessionID: string, todos: Todo[] | undefined) => {
|
||||||
if (!sessionID) return
|
if (!sessionID) return
|
||||||
@@ -344,7 +350,7 @@ function createGlobalSync() {
|
|||||||
if (recent) return
|
if (recent) return
|
||||||
bootstrap.refetch()
|
bootstrap.refetch()
|
||||||
},
|
},
|
||||||
setGlobalProject: setProjects,
|
setGlobalProject: (next) => setGlobalStore("project", next),
|
||||||
})
|
})
|
||||||
if (event.type === "server.connected" || event.type === "global.disposed") {
|
if (event.type === "server.connected" || event.type === "global.disposed") {
|
||||||
if (recent) return
|
if (recent) return
|
||||||
@@ -408,6 +414,8 @@ function createGlobalSync() {
|
|||||||
icon(directory: string, value: string | undefined) {
|
icon(directory: string, value: string | undefined) {
|
||||||
children.projectIcon(directory, value)
|
children.projectIcon(directory, value)
|
||||||
},
|
},
|
||||||
|
addWorktree: addProjectWorktree,
|
||||||
|
removeWorktree: removeProjectWorktree,
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateConfigMutation = useMutation(() => ({
|
const updateConfigMutation = useMutation(() => ({
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type {
|
|||||||
OpencodeClient,
|
OpencodeClient,
|
||||||
Path,
|
Path,
|
||||||
PermissionRequest,
|
PermissionRequest,
|
||||||
Project,
|
|
||||||
ProviderAuthResponse,
|
ProviderAuthResponse,
|
||||||
ProviderListResponse,
|
ProviderListResponse,
|
||||||
QuestionRequest,
|
QuestionRequest,
|
||||||
@@ -15,7 +14,7 @@ import { getFilename } from "@opencode-ai/core/util/path"
|
|||||||
import { retry } from "@opencode-ai/core/util/retry"
|
import { retry } from "@opencode-ai/core/util/retry"
|
||||||
import { batch } from "solid-js"
|
import { batch } from "solid-js"
|
||||||
import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
|
import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
|
||||||
import type { State, VcsCache } from "./types"
|
import type { ProjectInfo, State, VcsCache } from "./types"
|
||||||
import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
|
import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
|
||||||
import { formatServerError } from "@/utils/server-errors"
|
import { formatServerError } from "@/utils/server-errors"
|
||||||
import { QueryClient, queryOptions } from "@tanstack/solid-query"
|
import { QueryClient, queryOptions } from "@tanstack/solid-query"
|
||||||
@@ -24,7 +23,7 @@ import { loadMcpQuery } from "../global-sync"
|
|||||||
type GlobalStore = {
|
type GlobalStore = {
|
||||||
ready: boolean
|
ready: boolean
|
||||||
path: Path
|
path: Path
|
||||||
project: Project[]
|
project: ProjectInfo[]
|
||||||
session_todo: {
|
session_todo: {
|
||||||
[sessionID: string]: Todo[]
|
[sessionID: string]: Todo[]
|
||||||
}
|
}
|
||||||
@@ -94,12 +93,21 @@ export const loadProjectsQuery = (sdk: OpencodeClient) =>
|
|||||||
queryKey: ["project"],
|
queryKey: ["project"],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
retry(() =>
|
retry(() =>
|
||||||
sdk.project.list().then((x) => {
|
sdk.project.list().then(async (x) => {
|
||||||
return (x.data ?? [])
|
return Promise.all(
|
||||||
.filter((p) => !!p?.id)
|
(x.data ?? [])
|
||||||
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
|
.filter((p) => !!p?.id)
|
||||||
.slice()
|
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
|
||||||
.sort((a, b) => cmp(a.id, b.id))
|
.slice()
|
||||||
|
.sort((a, b) => cmp(a.id, b.id))
|
||||||
|
.map(async (project) => ({
|
||||||
|
...project,
|
||||||
|
worktrees: await sdk.worktree
|
||||||
|
.list({ directory: project.worktree })
|
||||||
|
.then((x) => x.data ?? [])
|
||||||
|
.catch(() => []),
|
||||||
|
})),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
@@ -140,8 +148,8 @@ function groupBySession<T extends { id: string; sessionID: string }>(input: T[])
|
|||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectID(directory: string, projects: Project[]) {
|
function projectID(directory: string, projects: ProjectInfo[]) {
|
||||||
return projects.find((project) => project.worktree === directory || project.sandboxes?.includes(directory))?.id
|
return projects.find((project) => project.worktree === directory || project.worktrees?.includes(directory))?.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeSession(setStore: SetStoreFunction<State>, session: Session) {
|
function mergeSession(setStore: SetStoreFunction<State>, session: Session) {
|
||||||
@@ -207,7 +215,7 @@ export async function bootstrapDirectory(input: {
|
|||||||
global: {
|
global: {
|
||||||
config: Config
|
config: Config
|
||||||
path: Path
|
path: Path
|
||||||
project: Project[]
|
project: ProjectInfo[]
|
||||||
provider: ProviderListResponse
|
provider: ProviderListResponse
|
||||||
}
|
}
|
||||||
queryClient: QueryClient
|
queryClient: QueryClient
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type {
|
|||||||
SnapshotFileDiff,
|
SnapshotFileDiff,
|
||||||
Todo,
|
Todo,
|
||||||
} from "@opencode-ai/sdk/v2/client"
|
} from "@opencode-ai/sdk/v2/client"
|
||||||
import type { State, VcsCache } from "./types"
|
import type { ProjectInfo, State, VcsCache } from "./types"
|
||||||
import { trimSessions } from "./session-trim"
|
import { trimSessions } from "./session-trim"
|
||||||
import { dropSessionCaches } from "./session-cache"
|
import { dropSessionCaches } from "./session-cache"
|
||||||
import { diffs as list, message as clean } from "@/utils/diffs"
|
import { diffs as list, message as clean } from "@/utils/diffs"
|
||||||
@@ -20,8 +20,8 @@ const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
|||||||
|
|
||||||
export function applyGlobalEvent(input: {
|
export function applyGlobalEvent(input: {
|
||||||
event: { type: string; properties?: unknown }
|
event: { type: string; properties?: unknown }
|
||||||
project: Project[]
|
project: ProjectInfo[]
|
||||||
setGlobalProject: (next: Project[] | ((draft: Project[]) => Project[])) => void
|
setGlobalProject: (next: ProjectInfo[] | ((draft: ProjectInfo[]) => ProjectInfo[])) => void
|
||||||
refresh: () => void
|
refresh: () => void
|
||||||
}) {
|
}) {
|
||||||
if (input.event.type === "global.disposed" || input.event.type === "server.connected") {
|
if (input.event.type === "global.disposed" || input.event.type === "server.connected") {
|
||||||
@@ -42,7 +42,7 @@ export function applyGlobalEvent(input: {
|
|||||||
}
|
}
|
||||||
input.setGlobalProject(
|
input.setGlobalProject(
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
draft.splice(result.index, 0, properties)
|
draft.splice(result.index, 0, { ...properties, worktrees: [] })
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type {
|
|||||||
Part,
|
Part,
|
||||||
Path,
|
Path,
|
||||||
PermissionRequest,
|
PermissionRequest,
|
||||||
|
Project,
|
||||||
ProviderListResponse,
|
ProviderListResponse,
|
||||||
QuestionRequest,
|
QuestionRequest,
|
||||||
Session,
|
Session,
|
||||||
@@ -30,6 +31,10 @@ export type ProjectMeta = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProjectInfo = Project & {
|
||||||
|
worktrees?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
status: "loading" | "partial" | "complete"
|
status: "loading" | "partial" | "complete"
|
||||||
agent: Agent[]
|
agent: Agent[]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
|
import type { Agent, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
|
||||||
|
import type { ProjectInfo } from "./types"
|
||||||
export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
|
export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
|
||||||
|
|
||||||
export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
||||||
@@ -27,7 +28,7 @@ export function normalizeProviderList(input: ProviderListResponse): ProviderList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeProject(project: Project) {
|
export function sanitizeProject(project: ProjectInfo) {
|
||||||
if (!project.icon?.url && !project.icon?.override) return project
|
if (!project.icon?.url && !project.icon?.override) return project
|
||||||
return {
|
return {
|
||||||
...project,
|
...project,
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { useGlobalSync } from "./global-sync"
|
|||||||
import { useGlobalSDK } from "./global-sdk"
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
import { useServer } from "./server"
|
import { useServer } from "./server"
|
||||||
import { usePlatform } from "./platform"
|
import { usePlatform } from "./platform"
|
||||||
import { Project } from "@opencode-ai/sdk/v2"
|
|
||||||
import { Persist, persisted, removePersisted } from "@/utils/persist"
|
import { Persist, persisted, removePersisted } from "@/utils/persist"
|
||||||
|
import type { ProjectInfo } from "./global-sync/types"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
import { same } from "@/utils/same"
|
import { same } from "@/utils/same"
|
||||||
import { createScrollPersistence, type SessionScroll } from "./layout-scroll"
|
import { createScrollPersistence, type SessionScroll } from "./layout-scroll"
|
||||||
@@ -51,7 +51,7 @@ type TabHandoff = {
|
|||||||
at: number
|
at: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean }
|
export type LocalProject = Partial<ProjectInfo> & { worktree: string; expanded: boolean }
|
||||||
|
|
||||||
export type ReviewDiffStyle = "unified" | "split"
|
export type ReviewDiffStyle = "unified" | "split"
|
||||||
|
|
||||||
@@ -404,9 +404,8 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
const roots = createMemo(() => {
|
const roots = createMemo(() => {
|
||||||
const map = new Map<string, string>()
|
const map = new Map<string, string>()
|
||||||
for (const project of globalSync.data.project) {
|
for (const project of globalSync.data.project) {
|
||||||
const sandboxes = project.sandboxes ?? []
|
for (const worktree of project.worktrees ?? []) {
|
||||||
for (const sandbox of sandboxes) {
|
map.set(worktree, project.worktree)
|
||||||
map.set(sandbox, project.worktree)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ import {
|
|||||||
effectiveWorkspaceOrder,
|
effectiveWorkspaceOrder,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
latestRootSession,
|
latestRootSession,
|
||||||
|
projectDirectories,
|
||||||
sortedRootSessions,
|
sortedRootSessions,
|
||||||
} from "./layout/helpers"
|
} from "./layout/helpers"
|
||||||
import {
|
import {
|
||||||
@@ -560,8 +561,8 @@ export default function Layout(props: ParentProps) {
|
|||||||
|
|
||||||
const projects = layout.projects.list()
|
const projects = layout.projects.list()
|
||||||
|
|
||||||
const sandbox = projects.find((p) => p.sandboxes?.some((item) => pathKey(item) === key))
|
const worktree = projects.find((p) => p.worktrees?.some((item) => pathKey(item) === key))
|
||||||
if (sandbox) return sandbox
|
if (worktree) return worktree
|
||||||
|
|
||||||
const direct = projects.find((p) => pathKey(p.worktree) === key)
|
const direct = projects.find((p) => pathKey(p.worktree) === key)
|
||||||
if (direct) return direct
|
if (direct) return direct
|
||||||
@@ -646,7 +647,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
if (!expanded) continue
|
if (!expanded) continue
|
||||||
const key = pathKey(directory)
|
const key = pathKey(directory)
|
||||||
const project = projects.find(
|
const project = projects.find(
|
||||||
(item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key),
|
(item) => pathKey(item.worktree) === key || item.worktrees?.some((worktree) => pathKey(worktree) === key),
|
||||||
)
|
)
|
||||||
if (!project) continue
|
if (!project) continue
|
||||||
if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue
|
if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue
|
||||||
@@ -1223,7 +1224,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const key = pathKey(directory)
|
const key = pathKey(directory)
|
||||||
const project = layout.projects
|
const project = layout.projects
|
||||||
.list()
|
.list()
|
||||||
.find((item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key))
|
.find((item) => pathKey(item.worktree) === key || item.worktrees?.some((worktree) => pathKey(worktree) === key))
|
||||||
if (project) return project.worktree
|
if (project) return project.worktree
|
||||||
|
|
||||||
const known = Object.entries(store.workspaceOrder).find(
|
const known = Object.entries(store.workspaceOrder).find(
|
||||||
@@ -1274,9 +1275,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const root = projectRoot(directory)
|
const root = projectRoot(directory)
|
||||||
server.projects.touch(root)
|
server.projects.touch(root)
|
||||||
const project = layout.projects.list().find((item) => item.worktree === root)
|
const project = layout.projects.list().find((item) => item.worktree === root)
|
||||||
let dirs = project
|
let dirs = project ? effectiveWorkspaceOrder(root, projectDirectories(project), store.workspaceOrder[root]) : [root]
|
||||||
? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root])
|
|
||||||
: [root]
|
|
||||||
const canOpen = (value: string | undefined) => {
|
const canOpen = (value: string | undefined) => {
|
||||||
if (!value) return false
|
if (!value) return false
|
||||||
return dirs.some((item) => pathKey(item) === pathKey(value))
|
return dirs.some((item) => pathKey(item) === pathKey(value))
|
||||||
@@ -1509,14 +1508,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
clearLastProjectSession(root)
|
clearLastProjectSession(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
globalSync.set(
|
globalSync.project.removeWorktree(root, directory)
|
||||||
"project",
|
|
||||||
produce((draft) => {
|
|
||||||
const project = draft.find((item) => item.worktree === root)
|
|
||||||
if (!project) return
|
|
||||||
project.sandboxes = (project.sandboxes ?? []).filter((sandbox) => sandbox !== directory)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
setStore("workspaceOrder", root, (order) => (order ?? []).filter((workspace) => workspace !== directory))
|
setStore("workspaceOrder", root, (order) => (order ?? []).filter((workspace) => workspace !== directory))
|
||||||
|
|
||||||
layout.projects.close(directory)
|
layout.projects.close(directory)
|
||||||
@@ -1528,7 +1520,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const nextKey = pathKey(nextCurrent)
|
const nextKey = pathKey(nextCurrent)
|
||||||
const project = layout.projects.list().find((item) => item.worktree === root)
|
const project = layout.projects.list().find((item) => item.worktree === root)
|
||||||
const dirs = project
|
const dirs = project
|
||||||
? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root])
|
? effectiveWorkspaceOrder(root, projectDirectories(project), store.workspaceOrder[root])
|
||||||
: [root]
|
: [root]
|
||||||
const valid = dirs.some((item) => pathKey(item) === nextKey)
|
const valid = dirs.some((item) => pathKey(item) === nextKey)
|
||||||
|
|
||||||
@@ -1862,7 +1854,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
function workspaceIds(project: LocalProject | undefined) {
|
function workspaceIds(project: LocalProject | undefined) {
|
||||||
if (!project) return []
|
if (!project) return []
|
||||||
const local = project.worktree
|
const local = project.worktree
|
||||||
const dirs = [local, ...(project.sandboxes ?? [])]
|
const dirs = projectDirectories(project)
|
||||||
const active = currentProject()
|
const active = currentProject()
|
||||||
const directory = pathKey(active?.worktree ?? "") === pathKey(project.worktree) ? currentDir() : undefined
|
const directory = pathKey(active?.worktree ?? "") === pathKey(project.worktree) ? currentDir() : undefined
|
||||||
const extra =
|
const extra =
|
||||||
@@ -1954,6 +1946,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
})
|
})
|
||||||
return [created.directory, ...next]
|
return [created.directory, ...next]
|
||||||
})
|
})
|
||||||
|
globalSync.project.addWorktree(project.worktree, created.directory)
|
||||||
|
|
||||||
globalSync.child(created.directory)
|
globalSync.child(created.directory)
|
||||||
navigateWithSidebarReset(`/${base64Encode(created.directory)}/session`)
|
navigateWithSidebarReset(`/${base64Encode(created.directory)}/session`)
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ export const childSessionOnPath = (sessions: Session[] | undefined, rootID: stri
|
|||||||
export const displayName = (project: { name?: string; worktree: string }) =>
|
export const displayName = (project: { name?: string; worktree: string }) =>
|
||||||
project.name || getFilename(project.worktree)
|
project.name || getFilename(project.worktree)
|
||||||
|
|
||||||
|
export const projectDirectories = (project: { worktree: string; worktrees?: string[] }) => [
|
||||||
|
project.worktree,
|
||||||
|
...(project.worktrees ?? []),
|
||||||
|
]
|
||||||
|
|
||||||
export const errorMessage = (err: unknown, fallback: string) => {
|
export const errorMessage = (err: unknown, fallback: string) => {
|
||||||
if (err && typeof err === "object" && "data" in err) {
|
if (err && typeof err === "object" && "data" in err) {
|
||||||
const data = (err as { data?: { message?: string } }).data
|
const data = (err as { data?: { message?: string } }).data
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { usePermission } from "@/context/permission"
|
|||||||
import { messageAgentColor } from "@/utils/agent"
|
import { messageAgentColor } from "@/utils/agent"
|
||||||
import { sessionTitle } from "@/utils/session-title"
|
import { sessionTitle } from "@/utils/session-title"
|
||||||
import { sessionPermissionRequest } from "../session/composer/session-request-tree"
|
import { sessionPermissionRequest } from "../session/composer/session-request-tree"
|
||||||
import { childSessionOnPath, hasProjectPermissions } from "./helpers"
|
import { childSessionOnPath, hasProjectPermissions, projectDirectories } from "./helpers"
|
||||||
|
|
||||||
const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
|
const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export const ProjectIcon = (props: {
|
|||||||
const globalSync = useGlobalSync()
|
const globalSync = useGlobalSync()
|
||||||
const notification = useNotification()
|
const notification = useNotification()
|
||||||
const permission = usePermission()
|
const permission = usePermission()
|
||||||
const dirs = createMemo(() => [props.project.worktree, ...(props.project.sandboxes ?? [])])
|
const dirs = createMemo(() => projectDirectories(props.project))
|
||||||
const unseenCount = createMemo(() =>
|
const unseenCount = createMemo(() =>
|
||||||
dirs().reduce((total, directory) => total + notification.project.unseenCount(directory), 0),
|
dirs().reduce((total, directory) => total + notification.project.unseenCount(directory), 0),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `project` DROP COLUMN `sandboxes`;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,5 @@ export const ProjectTable = sqliteTable("project", {
|
|||||||
icon_color: text(),
|
icon_color: text(),
|
||||||
...Timestamps,
|
...Timestamps,
|
||||||
time_initialized: integer(),
|
time_initialized: integer(),
|
||||||
sandboxes: text({ mode: "json" }).notNull().$type<string[]>(),
|
|
||||||
commands: text({ mode: "json" }).$type<{ start?: string }>(),
|
commands: text({ mode: "json" }).$type<{ start?: string }>(),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export const Info = Schema.Struct({
|
|||||||
icon: optionalOmitUndefined(ProjectIcon),
|
icon: optionalOmitUndefined(ProjectIcon),
|
||||||
commands: optionalOmitUndefined(ProjectCommands),
|
commands: optionalOmitUndefined(ProjectCommands),
|
||||||
time: ProjectTime,
|
time: ProjectTime,
|
||||||
sandboxes: Schema.Array(Schema.String),
|
|
||||||
})
|
})
|
||||||
.annotate({ identifier: "Project" })
|
.annotate({ identifier: "Project" })
|
||||||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||||
@@ -83,7 +82,6 @@ export function fromRow(row: Row): Info {
|
|||||||
updated: row.time_updated,
|
updated: row.time_updated,
|
||||||
initialized: row.time_initialized ?? undefined,
|
initialized: row.time_initialized ?? undefined,
|
||||||
},
|
},
|
||||||
sandboxes: row.sandboxes,
|
|
||||||
commands: row.commands ?? undefined,
|
commands: row.commands ?? undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,9 +121,6 @@ export interface Interface {
|
|||||||
readonly update: (input: UpdateInput) => Effect.Effect<Info>
|
readonly update: (input: UpdateInput) => Effect.Effect<Info>
|
||||||
readonly initGit: (input: { directory: string; project: Info }) => Effect.Effect<Info>
|
readonly initGit: (input: { directory: string; project: Info }) => Effect.Effect<Info>
|
||||||
readonly setInitialized: (id: ProjectID) => Effect.Effect<void>
|
readonly setInitialized: (id: ProjectID) => Effect.Effect<void>
|
||||||
readonly sandboxes: (id: ProjectID) => Effect.Effect<string[]>
|
|
||||||
readonly addSandbox: (id: ProjectID, directory: string) => Effect.Effect<void>
|
|
||||||
readonly removeSandbox: (id: ProjectID, directory: string) => Effect.Effect<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Project") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Project") {}
|
||||||
@@ -283,7 +278,6 @@ export const layer: Layer.Layer<
|
|||||||
id: data.id,
|
id: data.id,
|
||||||
worktree: data.worktree,
|
worktree: data.worktree,
|
||||||
vcs: data.vcs,
|
vcs: data.vcs,
|
||||||
sandboxes: [] as string[],
|
|
||||||
time: { created: Date.now(), updated: Date.now() },
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,17 +289,6 @@ export const layer: Layer.Layer<
|
|||||||
vcs: data.vcs,
|
vcs: data.vcs,
|
||||||
time: { ...existing.time, updated: Date.now() },
|
time: { ...existing.time, updated: Date.now() },
|
||||||
}
|
}
|
||||||
if (data.sandbox !== result.worktree && !result.sandboxes.includes(data.sandbox))
|
|
||||||
result.sandboxes.push(data.sandbox)
|
|
||||||
result.sandboxes = yield* Effect.forEach(
|
|
||||||
result.sandboxes,
|
|
||||||
(s) =>
|
|
||||||
fs.exists(s).pipe(
|
|
||||||
Effect.orDie,
|
|
||||||
Effect.map((exists) => (exists ? s : undefined)),
|
|
||||||
),
|
|
||||||
{ concurrency: "unbounded" },
|
|
||||||
).pipe(Effect.map((arr) => arr.filter((x): x is string => x !== undefined)))
|
|
||||||
|
|
||||||
yield* db((d) =>
|
yield* db((d) =>
|
||||||
d
|
d
|
||||||
@@ -321,7 +304,6 @@ export const layer: Layer.Layer<
|
|||||||
time_created: result.time.created,
|
time_created: result.time.created,
|
||||||
time_updated: result.time.updated,
|
time_updated: result.time.updated,
|
||||||
time_initialized: result.time.initialized,
|
time_initialized: result.time.initialized,
|
||||||
sandboxes: result.sandboxes,
|
|
||||||
commands: result.commands,
|
commands: result.commands,
|
||||||
})
|
})
|
||||||
.onConflictDoUpdate({
|
.onConflictDoUpdate({
|
||||||
@@ -335,7 +317,6 @@ export const layer: Layer.Layer<
|
|||||||
icon_color: result.icon?.color,
|
icon_color: result.icon?.color,
|
||||||
time_updated: result.time.updated,
|
time_updated: result.time.updated,
|
||||||
time_initialized: result.time.initialized,
|
time_initialized: result.time.initialized,
|
||||||
sandboxes: result.sandboxes,
|
|
||||||
commands: result.commands,
|
commands: result.commands,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -441,54 +422,6 @@ export const layer: Layer.Layer<
|
|||||||
yield* InstanceState.get(initState)
|
yield* InstanceState.get(initState)
|
||||||
})
|
})
|
||||||
|
|
||||||
const sandboxes = Effect.fn("Project.sandboxes")(function* (id: ProjectID) {
|
|
||||||
const row = yield* db((d) => d.select().from(ProjectTable).where(eq(ProjectTable.id, id)).get())
|
|
||||||
if (!row) return []
|
|
||||||
const data = fromRow(row)
|
|
||||||
return yield* Effect.forEach(
|
|
||||||
data.sandboxes,
|
|
||||||
(dir) =>
|
|
||||||
fs.isDir(dir).pipe(
|
|
||||||
Effect.orDie,
|
|
||||||
Effect.map((ok) => (ok ? dir : undefined)),
|
|
||||||
),
|
|
||||||
{ concurrency: "unbounded" },
|
|
||||||
).pipe(Effect.map((arr) => arr.filter((x): x is string => x !== undefined)))
|
|
||||||
})
|
|
||||||
|
|
||||||
const addSandbox = Effect.fn("Project.addSandbox")(function* (id: ProjectID, directory: string) {
|
|
||||||
const row = yield* db((d) => d.select().from(ProjectTable).where(eq(ProjectTable.id, id)).get())
|
|
||||||
if (!row) throw new Error(`Project not found: ${id}`)
|
|
||||||
const sboxes = [...row.sandboxes]
|
|
||||||
if (!sboxes.includes(directory)) sboxes.push(directory)
|
|
||||||
const result = yield* db((d) =>
|
|
||||||
d
|
|
||||||
.update(ProjectTable)
|
|
||||||
.set({ sandboxes: sboxes, time_updated: Date.now() })
|
|
||||||
.where(eq(ProjectTable.id, id))
|
|
||||||
.returning()
|
|
||||||
.get(),
|
|
||||||
)
|
|
||||||
if (!result) throw new Error(`Project not found: ${id}`)
|
|
||||||
yield* emitUpdated(fromRow(result))
|
|
||||||
})
|
|
||||||
|
|
||||||
const removeSandbox = Effect.fn("Project.removeSandbox")(function* (id: ProjectID, directory: string) {
|
|
||||||
const row = yield* db((d) => d.select().from(ProjectTable).where(eq(ProjectTable.id, id)).get())
|
|
||||||
if (!row) throw new Error(`Project not found: ${id}`)
|
|
||||||
const sboxes = row.sandboxes.filter((s) => s !== directory)
|
|
||||||
const result = yield* db((d) =>
|
|
||||||
d
|
|
||||||
.update(ProjectTable)
|
|
||||||
.set({ sandboxes: sboxes, time_updated: Date.now() })
|
|
||||||
.where(eq(ProjectTable.id, id))
|
|
||||||
.returning()
|
|
||||||
.get(),
|
|
||||||
)
|
|
||||||
if (!result) throw new Error(`Project not found: ${id}`)
|
|
||||||
yield* emitUpdated(fromRow(result))
|
|
||||||
})
|
|
||||||
|
|
||||||
return Service.of({
|
return Service.of({
|
||||||
init,
|
init,
|
||||||
fromDirectory,
|
fromDirectory,
|
||||||
@@ -498,9 +431,6 @@ export const layer: Layer.Layer<
|
|||||||
update,
|
update,
|
||||||
initGit,
|
initGit,
|
||||||
setInitialized,
|
setInitialized,
|
||||||
sandboxes,
|
|
||||||
addSandbox,
|
|
||||||
removeSandbox,
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export const ExperimentalApi = HttpApi.make("experimental")
|
|||||||
OpenApi.annotations({
|
OpenApi.annotations({
|
||||||
identifier: "worktree.list",
|
identifier: "worktree.list",
|
||||||
summary: "List worktrees",
|
summary: "List worktrees",
|
||||||
description: "List all sandbox worktrees for the current project.",
|
description: "List all git worktrees for the current project.",
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
HttpApiEndpoint.post("worktreeCreate", ExperimentalPaths.worktree, {
|
HttpApiEndpoint.post("worktreeCreate", ExperimentalPaths.worktree, {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { Account } from "@/account/account"
|
import { Account } from "@/account/account"
|
||||||
import { Agent } from "@/agent/agent"
|
import { Agent } from "@/agent/agent"
|
||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
|
||||||
import { MCP } from "@/mcp"
|
import { MCP } from "@/mcp"
|
||||||
import { Project } from "@/project/project"
|
|
||||||
import { Session } from "@/session/session"
|
import { Session } from "@/session/session"
|
||||||
import { ToolRegistry } from "@/tool/registry"
|
import { ToolRegistry } from "@/tool/registry"
|
||||||
import * as EffectZod from "@opencode-ai/core/effect-zod"
|
import * as EffectZod from "@opencode-ai/core/effect-zod"
|
||||||
@@ -20,7 +18,6 @@ export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "exper
|
|||||||
const agents = yield* Agent.Service
|
const agents = yield* Agent.Service
|
||||||
const config = yield* Config.Service
|
const config = yield* Config.Service
|
||||||
const mcp = yield* MCP.Service
|
const mcp = yield* MCP.Service
|
||||||
const project = yield* Project.Service
|
|
||||||
const registry = yield* ToolRegistry.Service
|
const registry = yield* ToolRegistry.Service
|
||||||
const worktreeSvc = yield* Worktree.Service
|
const worktreeSvc = yield* Worktree.Service
|
||||||
|
|
||||||
@@ -92,10 +89,10 @@ export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "exper
|
|||||||
return yield* registry.ids()
|
return yield* registry.ids()
|
||||||
})
|
})
|
||||||
|
|
||||||
const worktree = Effect.fn("ExperimentalHttpApi.worktree")(function* () {
|
const worktree = Effect.fn("ExperimentalHttpApi.worktree")(
|
||||||
const ctx = yield* InstanceState.context
|
() => worktreeSvc.list(),
|
||||||
return yield* project.sandboxes(ctx.project.id)
|
Effect.map((items) => items.map((item) => item.directory)),
|
||||||
})
|
)
|
||||||
|
|
||||||
const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: {
|
const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: {
|
||||||
payload: Worktree.CreateInput | undefined
|
payload: Worktree.CreateInput | undefined
|
||||||
@@ -106,9 +103,7 @@ export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "exper
|
|||||||
const worktreeRemove = Effect.fn("ExperimentalHttpApi.worktreeRemove")(function* (input: {
|
const worktreeRemove = Effect.fn("ExperimentalHttpApi.worktreeRemove")(function* (input: {
|
||||||
payload: Worktree.RemoveInput
|
payload: Worktree.RemoveInput
|
||||||
}) {
|
}) {
|
||||||
const ctx = yield* InstanceState.context
|
|
||||||
yield* worktreeSvc.remove(input.payload)
|
yield* worktreeSvc.remove(input.payload)
|
||||||
yield* project.removeSandbox(ctx.project.id, input.payload.directory)
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,6 @@ export async function run(db: SQLiteBunDatabase<any, any> | NodeSQLiteDatabase<a
|
|||||||
time_created: data.time?.created ?? now,
|
time_created: data.time?.created ?? now,
|
||||||
time_updated: data.time?.updated ?? now,
|
time_updated: data.time?.updated ?? now,
|
||||||
time_initialized: data.time?.initialized,
|
time_initialized: data.time?.initialized,
|
||||||
sandboxes: data.sandboxes ?? [],
|
|
||||||
commands: data.commands,
|
commands: data.commands,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,12 +158,7 @@ type GitResult = { code: number; text: string; stderr: string }
|
|||||||
export const layer: Layer.Layer<
|
export const layer: Layer.Layer<
|
||||||
Service,
|
Service,
|
||||||
never,
|
never,
|
||||||
| AppFileSystem.Service
|
AppFileSystem.Service | Path.Path | ChildProcessSpawner.ChildProcessSpawner | Git.Service | InstanceStore.Service
|
||||||
| Path.Path
|
|
||||||
| ChildProcessSpawner.ChildProcessSpawner
|
|
||||||
| Git.Service
|
|
||||||
| Project.Service
|
|
||||||
| InstanceStore.Service
|
|
||||||
> = Layer.effect(
|
> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
@@ -172,7 +167,6 @@ export const layer: Layer.Layer<
|
|||||||
const pathSvc = yield* Path.Path
|
const pathSvc = yield* Path.Path
|
||||||
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
||||||
const gitSvc = yield* Git.Service
|
const gitSvc = yield* Git.Service
|
||||||
const project = yield* Project.Service
|
|
||||||
const store = yield* InstanceStore.Service
|
const store = yield* InstanceStore.Service
|
||||||
|
|
||||||
const git = Effect.fnUntraced(
|
const git = Effect.fnUntraced(
|
||||||
@@ -233,8 +227,6 @@ export const layer: Layer.Layer<
|
|||||||
if (created.code !== 0) {
|
if (created.code !== 0) {
|
||||||
throw new CreateFailedError({ message: created.stderr || created.text || "Failed to create git worktree" })
|
throw new CreateFailedError({ message: created.stderr || created.text || "Failed to create git worktree" })
|
||||||
}
|
}
|
||||||
|
|
||||||
yield* project.addSandbox(ctx.project.id, info.directory).pipe(Effect.catch(() => Effect.void))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const boot = Effect.fnUntraced(function* (info: Info, startCommand?: string) {
|
const boot = Effect.fnUntraced(function* (info: Info, startCommand?: string) {
|
||||||
@@ -312,7 +304,7 @@ export const layer: Layer.Layer<
|
|||||||
return text
|
return text
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map((line) => line.trim())
|
.map((line) => line.trim())
|
||||||
.reduce<{ path?: string; branch?: string }[]>((acc, line) => {
|
.reduce<{ path?: string; branch?: string; prunable?: boolean }[]>((acc, line) => {
|
||||||
if (!line) return acc
|
if (!line) return acc
|
||||||
if (line.startsWith("worktree ")) {
|
if (line.startsWith("worktree ")) {
|
||||||
acc.push({ path: line.slice("worktree ".length).trim() })
|
acc.push({ path: line.slice("worktree ".length).trim() })
|
||||||
@@ -323,6 +315,9 @@ export const layer: Layer.Layer<
|
|||||||
if (line.startsWith("branch ")) {
|
if (line.startsWith("branch ")) {
|
||||||
current.branch = line.slice("branch ".length).trim()
|
current.branch = line.slice("branch ".length).trim()
|
||||||
}
|
}
|
||||||
|
if (line === "prunable" || line.startsWith("prunable ")) {
|
||||||
|
current.prunable = true
|
||||||
|
}
|
||||||
return acc
|
return acc
|
||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
@@ -352,18 +347,22 @@ export const layer: Layer.Layer<
|
|||||||
|
|
||||||
const primary = yield* canonical(ctx.worktree)
|
const primary = yield* canonical(ctx.worktree)
|
||||||
const primaryName = pathSvc.basename(primary).toLowerCase()
|
const primaryName = pathSvc.basename(primary).toLowerCase()
|
||||||
return yield* Effect.forEach(parseWorktreeList(result.text), (entry) =>
|
return yield* Effect.forEach(
|
||||||
Effect.gen(function* () {
|
parseWorktreeList(result.text),
|
||||||
if (!entry.path) return undefined
|
(entry) =>
|
||||||
const directory = yield* canonical(entry.path)
|
Effect.gen(function* () {
|
||||||
if (directory === primary) return undefined
|
if (entry.prunable) return undefined
|
||||||
const name = pathSvc.basename(directory).toLowerCase()
|
if (!entry.path) return undefined
|
||||||
return {
|
const directory = yield* canonical(entry.path)
|
||||||
name: name === primaryName ? pathSvc.basename(pathSvc.dirname(directory)) : name,
|
if (directory === primary) return undefined
|
||||||
directory,
|
const name = pathSvc.basename(directory).toLowerCase()
|
||||||
...(entry.branch ? { branch: entry.branch.replace(/^refs\/heads\//, "") } : {}),
|
return {
|
||||||
}
|
name: name === primaryName ? pathSvc.basename(pathSvc.dirname(directory)) : name,
|
||||||
}),
|
directory,
|
||||||
|
...(entry.branch ? { branch: entry.branch.replace(/^refs\/heads\//, "") } : {}),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ concurrency: "unbounded" },
|
||||||
).pipe(Effect.map((items) => items.filter((item) => item !== undefined)))
|
).pipe(Effect.map((items) => items.filter((item) => item !== undefined)))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -612,7 +611,6 @@ export const layer: Layer.Layer<
|
|||||||
export const appLayer = layer.pipe(
|
export const appLayer = layer.pipe(
|
||||||
Layer.provide(Git.defaultLayer),
|
Layer.provide(Git.defaultLayer),
|
||||||
Layer.provide(CrossSpawnSpawner.defaultLayer),
|
Layer.provide(CrossSpawnSpawner.defaultLayer),
|
||||||
Layer.provide(Project.defaultLayer),
|
|
||||||
Layer.provide(AppFileSystem.defaultLayer),
|
Layer.provide(AppFileSystem.defaultLayer),
|
||||||
Layer.provide(NodePath.layer),
|
Layer.provide(NodePath.layer),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -324,7 +324,6 @@ function insertProject(id: ProjectID, worktree: string) {
|
|||||||
name: null,
|
name: null,
|
||||||
time_created: Date.now(),
|
time_created: Date.now(),
|
||||||
time_updated: Date.now(),
|
time_updated: Date.now(),
|
||||||
sandboxes: [],
|
|
||||||
})
|
})
|
||||||
.run(),
|
.run(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ it.live("makeRuntime inherits InstanceRef from the current fiber", () =>
|
|||||||
id: ProjectID.global,
|
id: ProjectID.global,
|
||||||
worktree: testDirectory,
|
worktree: testDirectory,
|
||||||
time: { created: 0, updated: 0 },
|
time: { created: 0, updated: 0 },
|
||||||
sandboxes: [],
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ function ensureGlobal() {
|
|||||||
worktree: "/",
|
worktree: "/",
|
||||||
time_created: Date.now(),
|
time_created: Date.now(),
|
||||||
time_updated: Date.now(),
|
time_updated: Date.now(),
|
||||||
sandboxes: [],
|
|
||||||
})
|
})
|
||||||
.onConflictDoNothing()
|
.onConflictDoNothing()
|
||||||
.run(),
|
.run(),
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ describe("Project.fromDirectory with worktrees", () => {
|
|||||||
|
|
||||||
expect(project.worktree).toBe(tmp.path)
|
expect(project.worktree).toBe(tmp.path)
|
||||||
expect(sandbox).toBe(tmp.path)
|
expect(sandbox).toBe(tmp.path)
|
||||||
expect(project.sandboxes).not.toContain(tmp.path)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should set worktree to root when called from a worktree", async () => {
|
test("should set worktree to root when called from a worktree", async () => {
|
||||||
@@ -167,8 +166,6 @@ describe("Project.fromDirectory with worktrees", () => {
|
|||||||
|
|
||||||
expect(project.worktree).toBe(tmp.path)
|
expect(project.worktree).toBe(tmp.path)
|
||||||
expect(sandbox).toBe(worktreePath)
|
expect(sandbox).toBe(worktreePath)
|
||||||
expect(project.sandboxes).toContain(worktreePath)
|
|
||||||
expect(project.sandboxes).not.toContain(tmp.path)
|
|
||||||
} finally {
|
} finally {
|
||||||
await $`git worktree remove ${worktreePath}`
|
await $`git worktree remove ${worktreePath}`
|
||||||
.cwd(tmp.path)
|
.cwd(tmp.path)
|
||||||
@@ -220,34 +217,6 @@ describe("Project.fromDirectory with worktrees", () => {
|
|||||||
await $`rm -rf ${bare} ${clone}`.quiet().nothrow()
|
await $`rm -rf ${bare} ${clone}`.quiet().nothrow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should accumulate multiple worktrees in sandboxes", async () => {
|
|
||||||
await using tmp = await tmpdir({ git: true })
|
|
||||||
|
|
||||||
const worktree1 = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt1")
|
|
||||||
const worktree2 = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt2")
|
|
||||||
try {
|
|
||||||
await $`git worktree add ${worktree1} -b branch-${Date.now()}`.cwd(tmp.path).quiet()
|
|
||||||
await $`git worktree add ${worktree2} -b branch-${Date.now() + 1}`.cwd(tmp.path).quiet()
|
|
||||||
|
|
||||||
await run((svc) => svc.fromDirectory(worktree1))
|
|
||||||
const { project } = await run((svc) => svc.fromDirectory(worktree2))
|
|
||||||
|
|
||||||
expect(project.worktree).toBe(tmp.path)
|
|
||||||
expect(project.sandboxes).toContain(worktree1)
|
|
||||||
expect(project.sandboxes).toContain(worktree2)
|
|
||||||
expect(project.sandboxes).not.toContain(tmp.path)
|
|
||||||
} finally {
|
|
||||||
await $`git worktree remove ${worktree1}`
|
|
||||||
.cwd(tmp.path)
|
|
||||||
.quiet()
|
|
||||||
.catch(() => {})
|
|
||||||
await $`git worktree remove ${worktree2}`
|
|
||||||
.cwd(tmp.path)
|
|
||||||
.quiet()
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("Project.discover", () => {
|
describe("Project.discover", () => {
|
||||||
@@ -485,39 +454,6 @@ describe("Project.setInitialized", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("Project.addSandbox and Project.removeSandbox", () => {
|
|
||||||
test("addSandbox adds directory and removeSandbox removes it", async () => {
|
|
||||||
await using tmp = await tmpdir({ git: true })
|
|
||||||
const { project } = await run((svc) => svc.fromDirectory(tmp.path))
|
|
||||||
const sandboxDir = path.join(tmp.path, "sandbox-test")
|
|
||||||
|
|
||||||
await run((svc) => svc.addSandbox(project.id, sandboxDir))
|
|
||||||
|
|
||||||
let found = Project.get(project.id)
|
|
||||||
expect(found?.sandboxes).toContain(sandboxDir)
|
|
||||||
|
|
||||||
await run((svc) => svc.removeSandbox(project.id, sandboxDir))
|
|
||||||
|
|
||||||
found = Project.get(project.id)
|
|
||||||
expect(found?.sandboxes).not.toContain(sandboxDir)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("addSandbox emits GlobalBus event", async () => {
|
|
||||||
await using tmp = await tmpdir({ git: true })
|
|
||||||
const { project } = await run((svc) => svc.fromDirectory(tmp.path))
|
|
||||||
const sandboxDir = path.join(tmp.path, "sandbox-event")
|
|
||||||
|
|
||||||
const events: any[] = []
|
|
||||||
const on = (evt: any) => events.push(evt)
|
|
||||||
GlobalBus.on("event", on)
|
|
||||||
|
|
||||||
await run((svc) => svc.addSandbox(project.id, sandboxDir))
|
|
||||||
|
|
||||||
GlobalBus.off("event", on)
|
|
||||||
expect(events.some((e) => e.payload.type === Project.Event.Updated.type)).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("Project.fromDirectory with bare repos", () => {
|
describe("Project.fromDirectory with bare repos", () => {
|
||||||
test("worktree from bare repo should cache in bare repo, not parent", async () => {
|
test("worktree from bare repo should cache in bare repo, not parent", async () => {
|
||||||
await using tmp = await tmpdir({ git: true })
|
await using tmp = await tmpdir({ git: true })
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ describe("Worktree", () => {
|
|||||||
expect(list).toContainEqual({
|
expect(list).toContainEqual({
|
||||||
name: path.basename(parent),
|
name: path.basename(parent),
|
||||||
branch,
|
branch,
|
||||||
directory: directory.toLowerCase(),
|
directory: process.platform === "win32" ? directory.toLowerCase() : directory,
|
||||||
})
|
})
|
||||||
|
|
||||||
yield* svc.remove({ directory: target })
|
yield* svc.remove({ directory: target })
|
||||||
|
|||||||
@@ -57,12 +57,11 @@ describe("experimental HttpApi", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const headers = { "x-opencode-directory": tmp.path }
|
const headers = { "x-opencode-directory": tmp.path }
|
||||||
const [consoleState, consoleOrgs, toolList, toolIDs, worktrees, resources] = await Promise.all([
|
const [consoleState, consoleOrgs, toolList, toolIDs, resources] = await Promise.all([
|
||||||
app().request(ExperimentalPaths.console, { headers }),
|
app().request(ExperimentalPaths.console, { headers }),
|
||||||
app().request(ExperimentalPaths.consoleOrgs, { headers }),
|
app().request(ExperimentalPaths.consoleOrgs, { headers }),
|
||||||
app().request(`${ExperimentalPaths.tool}?provider=opencode&model=gpt-5`, { headers }),
|
app().request(`${ExperimentalPaths.tool}?provider=opencode&model=gpt-5`, { headers }),
|
||||||
app().request(ExperimentalPaths.toolIDs, { headers }),
|
app().request(ExperimentalPaths.toolIDs, { headers }),
|
||||||
app().request(ExperimentalPaths.worktree, { headers }),
|
|
||||||
app().request(ExperimentalPaths.resource, { headers }),
|
app().request(ExperimentalPaths.resource, { headers }),
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -87,9 +86,6 @@ describe("experimental HttpApi", () => {
|
|||||||
expect(toolIDs.status).toBe(200)
|
expect(toolIDs.status).toBe(200)
|
||||||
expect(await toolIDs.json()).toContain("bash")
|
expect(await toolIDs.json()).toContain("bash")
|
||||||
|
|
||||||
expect(worktrees.status).toBe(200)
|
|
||||||
expect(await worktrees.json()).toEqual([])
|
|
||||||
|
|
||||||
expect(resources.status).toBe(200)
|
expect(resources.status).toBe(200)
|
||||||
expect(await resources.json()).toEqual({})
|
expect(await resources.json()).toEqual({})
|
||||||
})
|
})
|
||||||
@@ -172,10 +168,6 @@ describe("experimental HttpApi", () => {
|
|||||||
expect(info).toMatchObject({ name: "api-test", branch: "opencode/api-test" })
|
expect(info).toMatchObject({ name: "api-test", branch: "opencode/api-test" })
|
||||||
await waitReady(info.directory)
|
await waitReady(info.directory)
|
||||||
|
|
||||||
const listed = await app().request(ExperimentalPaths.worktree, { headers })
|
|
||||||
expect(listed.status).toBe(200)
|
|
||||||
expect(await listed.json()).toContain(info.directory)
|
|
||||||
|
|
||||||
if (process.platform !== "win32") {
|
if (process.platform !== "win32") {
|
||||||
const reset = await app().request(ExperimentalPaths.worktreeReset, {
|
const reset = await app().request(ExperimentalPaths.worktreeReset, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -195,9 +187,6 @@ describe("experimental HttpApi", () => {
|
|||||||
|
|
||||||
expect(removed.status).toBe(200)
|
expect(removed.status).toBe(200)
|
||||||
expect(await removed.json()).toBe(true)
|
expect(await removed.json()).toBe(true)
|
||||||
|
expect(await Bun.file(info.directory).exists()).toBe(false)
|
||||||
const afterRemove = await app().request(ExperimentalPaths.worktree, { headers })
|
|
||||||
expect(afterRemove.status).toBe(200)
|
|
||||||
expect(await afterRemove.json()).toEqual([])
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -130,7 +130,6 @@ describe("JSON to SQLite migration", () => {
|
|||||||
expect(projects[0].id).toBe(ProjectID.make("proj_test123abc"))
|
expect(projects[0].id).toBe(ProjectID.make("proj_test123abc"))
|
||||||
expect(projects[0].worktree).toBe("/test/path")
|
expect(projects[0].worktree).toBe("/test/path")
|
||||||
expect(projects[0].name).toBe("Test Project")
|
expect(projects[0].name).toBe("Test Project")
|
||||||
expect(projects[0].sandboxes).toEqual(["/test/sandbox"])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("uses filename for project id when JSON has different value", async () => {
|
test("uses filename for project id when JSON has different value", async () => {
|
||||||
|
|||||||
@@ -1236,7 +1236,7 @@ export class Worktree extends HeyApiClient {
|
|||||||
/**
|
/**
|
||||||
* List worktrees
|
* List worktrees
|
||||||
*
|
*
|
||||||
* List all sandbox worktrees for the current project.
|
* List all git worktrees for the current project.
|
||||||
*/
|
*/
|
||||||
public list<ThrowOnError extends boolean = false>(
|
public list<ThrowOnError extends boolean = false>(
|
||||||
parameters?: {
|
parameters?: {
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ export type ClientOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
|
| EventTuiPromptAppend
|
||||||
|
| EventTuiCommandExecute
|
||||||
|
| EventTuiToastShow1
|
||||||
|
| EventTuiSessionSelect
|
||||||
|
| EventServerConnected
|
||||||
|
| EventGlobalDisposed
|
||||||
| EventServerInstanceDisposed
|
| EventServerInstanceDisposed
|
||||||
| EventFileEdited
|
| EventFileEdited
|
||||||
| EventFileWatcherUpdated
|
| EventFileWatcherUpdated
|
||||||
@@ -24,10 +30,6 @@ export type Event =
|
|||||||
| EventSessionStatus
|
| EventSessionStatus
|
||||||
| EventSessionIdle
|
| EventSessionIdle
|
||||||
| EventSessionCompacted
|
| EventSessionCompacted
|
||||||
| EventTuiPromptAppend
|
|
||||||
| EventTuiCommandExecute
|
|
||||||
| EventTuiToastShow1
|
|
||||||
| EventTuiSessionSelect
|
|
||||||
| EventMcpToolsChanged
|
| EventMcpToolsChanged
|
||||||
| EventMcpBrowserOpenFailed
|
| EventMcpBrowserOpenFailed
|
||||||
| EventCommandExecuted
|
| EventCommandExecuted
|
||||||
@@ -75,8 +77,6 @@ export type Event =
|
|||||||
| EventSessionNextCompactionStarted
|
| EventSessionNextCompactionStarted
|
||||||
| EventSessionNextCompactionDelta
|
| EventSessionNextCompactionDelta
|
||||||
| EventSessionNextCompactionEnded
|
| EventSessionNextCompactionEnded
|
||||||
| EventServerConnected
|
|
||||||
| EventGlobalDisposed
|
|
||||||
|
|
||||||
export type OAuth = {
|
export type OAuth = {
|
||||||
type: "oauth"
|
type: "oauth"
|
||||||
@@ -103,6 +103,61 @@ export type WellKnownAuth = {
|
|||||||
|
|
||||||
export type Auth = OAuth | ApiAuth | WellKnownAuth
|
export type Auth = OAuth | ApiAuth | WellKnownAuth
|
||||||
|
|
||||||
|
export type EventTuiPromptAppend = {
|
||||||
|
id: string
|
||||||
|
type: "tui.prompt.append"
|
||||||
|
properties: {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventTuiCommandExecute = {
|
||||||
|
id: string
|
||||||
|
type: "tui.command.execute"
|
||||||
|
properties: {
|
||||||
|
command:
|
||||||
|
| "session.list"
|
||||||
|
| "session.new"
|
||||||
|
| "session.share"
|
||||||
|
| "session.interrupt"
|
||||||
|
| "session.compact"
|
||||||
|
| "session.page.up"
|
||||||
|
| "session.page.down"
|
||||||
|
| "session.line.up"
|
||||||
|
| "session.line.down"
|
||||||
|
| "session.half.page.up"
|
||||||
|
| "session.half.page.down"
|
||||||
|
| "session.first"
|
||||||
|
| "session.last"
|
||||||
|
| "prompt.clear"
|
||||||
|
| "prompt.submit"
|
||||||
|
| "agent.cycle"
|
||||||
|
| string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventTuiToastShow = {
|
||||||
|
id: string
|
||||||
|
type: "tui.toast.show"
|
||||||
|
properties: {
|
||||||
|
title?: string
|
||||||
|
message: string
|
||||||
|
variant: "info" | "success" | "warning" | "error"
|
||||||
|
duration?: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventTuiSessionSelect = {
|
||||||
|
id: string
|
||||||
|
type: "tui.session.select"
|
||||||
|
properties: {
|
||||||
|
/**
|
||||||
|
* Session ID to navigate to
|
||||||
|
*/
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type PermissionRequest = {
|
export type PermissionRequest = {
|
||||||
id: string
|
id: string
|
||||||
sessionID: string
|
sessionID: string
|
||||||
@@ -280,61 +335,6 @@ export type SessionStatus =
|
|||||||
type: "busy"
|
type: "busy"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventTuiPromptAppend = {
|
|
||||||
id: string
|
|
||||||
type: "tui.prompt.append"
|
|
||||||
properties: {
|
|
||||||
text: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventTuiCommandExecute = {
|
|
||||||
id: string
|
|
||||||
type: "tui.command.execute"
|
|
||||||
properties: {
|
|
||||||
command:
|
|
||||||
| "session.list"
|
|
||||||
| "session.new"
|
|
||||||
| "session.share"
|
|
||||||
| "session.interrupt"
|
|
||||||
| "session.compact"
|
|
||||||
| "session.page.up"
|
|
||||||
| "session.page.down"
|
|
||||||
| "session.line.up"
|
|
||||||
| "session.line.down"
|
|
||||||
| "session.half.page.up"
|
|
||||||
| "session.half.page.down"
|
|
||||||
| "session.first"
|
|
||||||
| "session.last"
|
|
||||||
| "prompt.clear"
|
|
||||||
| "prompt.submit"
|
|
||||||
| "agent.cycle"
|
|
||||||
| string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventTuiToastShow = {
|
|
||||||
id: string
|
|
||||||
type: "tui.toast.show"
|
|
||||||
properties: {
|
|
||||||
title?: string
|
|
||||||
message: string
|
|
||||||
variant: "info" | "success" | "warning" | "error"
|
|
||||||
duration?: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventTuiSessionSelect = {
|
|
||||||
id: string
|
|
||||||
type: "tui.session.select"
|
|
||||||
properties: {
|
|
||||||
/**
|
|
||||||
* Session ID to navigate to
|
|
||||||
*/
|
|
||||||
sessionID: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Project = {
|
export type Project = {
|
||||||
id: string
|
id: string
|
||||||
worktree: string
|
worktree: string
|
||||||
@@ -356,7 +356,6 @@ export type Project = {
|
|||||||
updated: number
|
updated: number
|
||||||
initialized?: number
|
initialized?: number
|
||||||
}
|
}
|
||||||
sandboxes: Array<string>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Pty = {
|
export type Pty = {
|
||||||
@@ -779,6 +778,12 @@ export type GlobalEvent = {
|
|||||||
project?: string
|
project?: string
|
||||||
workspace?: string
|
workspace?: string
|
||||||
payload:
|
payload:
|
||||||
|
| EventTuiPromptAppend
|
||||||
|
| EventTuiCommandExecute
|
||||||
|
| EventTuiToastShow
|
||||||
|
| EventTuiSessionSelect
|
||||||
|
| EventServerConnected
|
||||||
|
| EventGlobalDisposed
|
||||||
| EventServerInstanceDisposed
|
| EventServerInstanceDisposed
|
||||||
| EventFileEdited
|
| EventFileEdited
|
||||||
| EventFileWatcherUpdated
|
| EventFileWatcherUpdated
|
||||||
@@ -798,10 +803,6 @@ export type GlobalEvent = {
|
|||||||
| EventSessionStatus
|
| EventSessionStatus
|
||||||
| EventSessionIdle
|
| EventSessionIdle
|
||||||
| EventSessionCompacted
|
| EventSessionCompacted
|
||||||
| EventTuiPromptAppend
|
|
||||||
| EventTuiCommandExecute
|
|
||||||
| EventTuiToastShow
|
|
||||||
| EventTuiSessionSelect
|
|
||||||
| EventMcpToolsChanged
|
| EventMcpToolsChanged
|
||||||
| EventMcpBrowserOpenFailed
|
| EventMcpBrowserOpenFailed
|
||||||
| EventCommandExecuted
|
| EventCommandExecuted
|
||||||
@@ -849,8 +850,6 @@ export type GlobalEvent = {
|
|||||||
| EventSessionNextCompactionStarted
|
| EventSessionNextCompactionStarted
|
||||||
| EventSessionNextCompactionDelta
|
| EventSessionNextCompactionDelta
|
||||||
| EventSessionNextCompactionEnded
|
| EventSessionNextCompactionEnded
|
||||||
| EventServerConnected
|
|
||||||
| EventGlobalDisposed
|
|
||||||
| SyncEventMessageUpdated
|
| SyncEventMessageUpdated
|
||||||
| SyncEventMessageRemoved
|
| SyncEventMessageRemoved
|
||||||
| SyncEventMessagePartUpdated
|
| SyncEventMessagePartUpdated
|
||||||
@@ -2331,6 +2330,22 @@ export type SyncEventSessionNextCompactionEnded = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EventServerConnected = {
|
||||||
|
id: string
|
||||||
|
type: "server.connected"
|
||||||
|
properties: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventGlobalDisposed = {
|
||||||
|
id: string
|
||||||
|
type: "global.disposed"
|
||||||
|
properties: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type EventServerInstanceDisposed = {
|
export type EventServerInstanceDisposed = {
|
||||||
id: string
|
id: string
|
||||||
type: "server.instance.disposed"
|
type: "server.instance.disposed"
|
||||||
@@ -3057,22 +3072,6 @@ export type EventSessionNextCompactionEnded = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventServerConnected = {
|
|
||||||
id: string
|
|
||||||
type: "server.connected"
|
|
||||||
properties: {
|
|
||||||
[key: string]: unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventGlobalDisposed = {
|
|
||||||
id: string
|
|
||||||
type: "global.disposed"
|
|
||||||
properties: {
|
|
||||||
[key: string]: unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SessionInfo = {
|
export type SessionInfo = {
|
||||||
id: string
|
id: string
|
||||||
parentID?: string
|
parentID?: string
|
||||||
|
|||||||
+208
-214
@@ -1015,7 +1015,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": "List all sandbox worktrees for the current project.",
|
"description": "List all git worktrees for the current project.",
|
||||||
"summary": "List worktrees",
|
"summary": "List worktrees",
|
||||||
"x-codeSamples": [
|
"x-codeSamples": [
|
||||||
{
|
{
|
||||||
@@ -8878,6 +8878,24 @@
|
|||||||
"schemas": {
|
"schemas": {
|
||||||
"Event": {
|
"Event": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.tui.prompt.append"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.tui.command.execute"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/EventTuiToastShow1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.tui.session.select"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/EventServerConnected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/EventGlobalDisposed"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventServerInstanceDisposed"
|
"$ref": "#/components/schemas/EventServerInstanceDisposed"
|
||||||
},
|
},
|
||||||
@@ -8935,18 +8953,6 @@
|
|||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventSessionCompacted"
|
"$ref": "#/components/schemas/EventSessionCompacted"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/Event.tui.prompt.append"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/Event.tui.command.execute"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/EventTuiToastShow1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/Event.tui.session.select"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventMcpToolsChanged"
|
"$ref": "#/components/schemas/EventMcpToolsChanged"
|
||||||
},
|
},
|
||||||
@@ -9087,12 +9093,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventSessionNextCompactionEnded"
|
"$ref": "#/components/schemas/EventSessionNextCompactionEnded"
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/EventServerConnected"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/EventGlobalDisposed"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -9173,6 +9173,140 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Event.tui.prompt.append": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["tui.prompt.append"]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["text"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "type", "properties"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"Event.tui.command.execute": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["tui.command.execute"]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"session.list",
|
||||||
|
"session.new",
|
||||||
|
"session.share",
|
||||||
|
"session.interrupt",
|
||||||
|
"session.compact",
|
||||||
|
"session.page.up",
|
||||||
|
"session.page.down",
|
||||||
|
"session.line.up",
|
||||||
|
"session.line.down",
|
||||||
|
"session.half.page.up",
|
||||||
|
"session.half.page.down",
|
||||||
|
"session.first",
|
||||||
|
"session.last",
|
||||||
|
"prompt.clear",
|
||||||
|
"prompt.submit",
|
||||||
|
"agent.cycle"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["command"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "type", "properties"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"Event.tui.toast.show": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["tui.toast.show"]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"variant": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["info", "success", "warning", "error"]
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"type": "integer",
|
||||||
|
"exclusiveMinimum": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["message", "variant"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "type", "properties"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"Event.tui.session.select": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["tui.session.select"]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"sessionID": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^ses",
|
||||||
|
"description": "Session ID to navigate to"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["sessionID"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "type", "properties"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
"PermissionRequest": {
|
"PermissionRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -9632,140 +9766,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Event.tui.prompt.append": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["tui.prompt.append"]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"text": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["text"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "type", "properties"],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"Event.tui.command.execute": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["tui.command.execute"]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"command": {
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"session.list",
|
|
||||||
"session.new",
|
|
||||||
"session.share",
|
|
||||||
"session.interrupt",
|
|
||||||
"session.compact",
|
|
||||||
"session.page.up",
|
|
||||||
"session.page.down",
|
|
||||||
"session.line.up",
|
|
||||||
"session.line.down",
|
|
||||||
"session.half.page.up",
|
|
||||||
"session.half.page.down",
|
|
||||||
"session.first",
|
|
||||||
"session.last",
|
|
||||||
"prompt.clear",
|
|
||||||
"prompt.submit",
|
|
||||||
"agent.cycle"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["command"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "type", "properties"],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"Event.tui.toast.show": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["tui.toast.show"]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"title": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["info", "success", "warning", "error"]
|
|
||||||
},
|
|
||||||
"duration": {
|
|
||||||
"type": "integer",
|
|
||||||
"exclusiveMinimum": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["message", "variant"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "type", "properties"],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"Event.tui.session.select": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["tui.session.select"]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"sessionID": {
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^ses",
|
|
||||||
"description": "Session ID to navigate to"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["sessionID"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "type", "properties"],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"Project": {
|
"Project": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -9825,15 +9825,9 @@
|
|||||||
},
|
},
|
||||||
"required": ["created", "updated"],
|
"required": ["created", "updated"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
|
||||||
"sandboxes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["id", "worktree", "time", "sandboxes"],
|
"required": ["id", "worktree", "time"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"Pty": {
|
"Pty": {
|
||||||
@@ -11145,6 +11139,24 @@
|
|||||||
},
|
},
|
||||||
"payload": {
|
"payload": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.tui.prompt.append"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.tui.command.execute"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.tui.toast.show"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.tui.session.select"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/EventServerConnected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/EventGlobalDisposed"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventServerInstanceDisposed"
|
"$ref": "#/components/schemas/EventServerInstanceDisposed"
|
||||||
},
|
},
|
||||||
@@ -11202,18 +11214,6 @@
|
|||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventSessionCompacted"
|
"$ref": "#/components/schemas/EventSessionCompacted"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/Event.tui.prompt.append"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/Event.tui.command.execute"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/Event.tui.toast.show"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/Event.tui.session.select"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventMcpToolsChanged"
|
"$ref": "#/components/schemas/EventMcpToolsChanged"
|
||||||
},
|
},
|
||||||
@@ -11355,12 +11355,6 @@
|
|||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EventSessionNextCompactionEnded"
|
"$ref": "#/components/schemas/EventSessionNextCompactionEnded"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/EventServerConnected"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/EventGlobalDisposed"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/SyncEventMessageUpdated"
|
"$ref": "#/components/schemas/SyncEventMessageUpdated"
|
||||||
},
|
},
|
||||||
@@ -15859,6 +15853,42 @@
|
|||||||
"required": ["type", "name", "id", "seq", "aggregateID", "data"],
|
"required": ["type", "name", "id", "seq", "aggregateID", "data"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
"EventServerConnected": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["server.connected"]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "type", "properties"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"EventGlobalDisposed": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["global.disposed"]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "type", "properties"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
"EventServerInstanceDisposed": {
|
"EventServerInstanceDisposed": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -18062,42 +18092,6 @@
|
|||||||
"required": ["id", "type", "properties"],
|
"required": ["id", "type", "properties"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"EventServerConnected": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["server.connected"]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "type", "properties"],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"EventGlobalDisposed": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["global.disposed"]
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "type", "properties"],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"SessionInfo": {
|
"SessionInfo": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
Reference in New Issue
Block a user