refactor(tui): wire generated client reads (#34381)
This commit is contained in:
parent
e8ac44430b
commit
381d67572e
28 changed files with 509 additions and 427 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import type { ConnectionInfo, IntegrationInfo, IntegrationOAuthMethod, IntegrationAttempt } from "@opencode-ai/sdk/v2"
|
||||
import type { IntegrationsConnectOauthOutput } from "@opencode-ai/client"
|
||||
import type { ConnectionInfo, IntegrationInfo, IntegrationOAuthMethod } from "@opencode-ai/sdk/v2"
|
||||
import { createMemo, createSignal, onCleanup, onMount, Show } from "solid-js"
|
||||
import { useClipboard } from "../context/clipboard"
|
||||
import { useData } from "../context/data"
|
||||
|
|
@ -22,6 +23,7 @@ const INTEGRATION_PRIORITY: Record<string, number> = {
|
|||
}
|
||||
|
||||
type ConnectMethod = Exclude<IntegrationInfo["methods"][number], { type: "env" }>
|
||||
type IntegrationAttempt = IntegrationsConnectOauthOutput["data"]
|
||||
|
||||
export function integrationOptions(list: IntegrationInfo[]) {
|
||||
return list.toSorted(
|
||||
|
|
@ -109,11 +111,8 @@ function manageConnections(
|
|||
title: `Disconnect ${connection.label}`,
|
||||
value: connection.id,
|
||||
onSelect: () => {
|
||||
void sdk.client.v2.credential
|
||||
.remove(
|
||||
{ credentialID: connection.id, location: location(data) },
|
||||
{ throwOnError: true },
|
||||
)
|
||||
void sdk.api.credentials
|
||||
.remove({ credentialID: connection.id, location: location(data) })
|
||||
.then(() => disconnected(integration.name, data, dialog, toast))
|
||||
.catch(toast.error)
|
||||
},
|
||||
|
|
@ -124,11 +123,7 @@ function manageConnections(
|
|||
})
|
||||
}
|
||||
|
||||
function selectMethod(
|
||||
integration: IntegrationInfo,
|
||||
methods: ConnectMethod[],
|
||||
dialog: ReturnType<typeof useDialog>,
|
||||
) {
|
||||
function selectMethod(integration: IntegrationInfo, methods: ConnectMethod[], dialog: ReturnType<typeof useDialog>) {
|
||||
if (methods.length === 1) return openMethod(integration, methods[0], dialog)
|
||||
dialog.replace(() => (
|
||||
<DialogSelect
|
||||
|
|
@ -142,11 +137,7 @@ function selectMethod(
|
|||
))
|
||||
}
|
||||
|
||||
function openMethod(
|
||||
integration: IntegrationInfo,
|
||||
method: ConnectMethod,
|
||||
dialog: ReturnType<typeof useDialog>,
|
||||
) {
|
||||
function openMethod(integration: IntegrationInfo, method: ConnectMethod, dialog: ReturnType<typeof useDialog>) {
|
||||
if (method.type === "key") {
|
||||
dialog.replace(() => <KeyMethod integration={integration} method={method} />)
|
||||
return
|
||||
|
|
@ -168,21 +159,16 @@ function KeyMethod(props: { integration: IntegrationInfo; method: Extract<Connec
|
|||
placeholder="API key"
|
||||
onConfirm={(key) => {
|
||||
if (!key) return
|
||||
void sdk.client.v2.integration.connect
|
||||
.key(
|
||||
{
|
||||
integrationID: props.integration.id,
|
||||
location: location(data),
|
||||
key,
|
||||
},
|
||||
{ throwOnError: true },
|
||||
)
|
||||
void sdk.api.integrations
|
||||
.connectKey({
|
||||
integrationID: props.integration.id,
|
||||
location: location(data),
|
||||
key,
|
||||
})
|
||||
.then(() => connected(props.integration.name, data, dialog, toast))
|
||||
.catch((cause) => setError(message(cause)))
|
||||
}}
|
||||
description={() => (
|
||||
<Show when={error()}>{(value) => <text fg={theme.error}>{value()}</text>}</Show>
|
||||
)}
|
||||
description={() => <Show when={error()}>{(value) => <text fg={theme.error}>{value()}</text>}</Show>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -208,25 +194,22 @@ function OAuthStarting(props: {
|
|||
const toast = useToast()
|
||||
|
||||
onMount(() => {
|
||||
void sdk.client.v2.integration.connect
|
||||
.oauth(
|
||||
{
|
||||
integrationID: props.integration.id,
|
||||
location: location(data),
|
||||
methodID: props.method.id,
|
||||
inputs: props.inputs,
|
||||
},
|
||||
{ throwOnError: true },
|
||||
)
|
||||
void sdk.api.integrations
|
||||
.connectOauth({
|
||||
integrationID: props.integration.id,
|
||||
location: location(data),
|
||||
methodID: props.method.id,
|
||||
inputs: props.inputs,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.data.data.mode === "code") {
|
||||
if (result.data.mode === "code") {
|
||||
dialog.replace(() => (
|
||||
<OAuthCode integration={props.integration} title={props.method.label} attempt={result.data.data} />
|
||||
<OAuthCode integration={props.integration} title={props.method.label} attempt={result.data} />
|
||||
))
|
||||
return
|
||||
}
|
||||
dialog.replace(() => (
|
||||
<OAuthAuto integration={props.integration} title={props.method.label} attempt={result.data.data} />
|
||||
<OAuthAuto integration={props.integration} title={props.method.label} attempt={result.data} />
|
||||
))
|
||||
})
|
||||
.catch((cause) => {
|
||||
|
|
@ -265,10 +248,10 @@ function OAuthAuto(props: { integration: IntegrationInfo; title: string; attempt
|
|||
}))
|
||||
|
||||
const poll = () => {
|
||||
void sdk.client.v2.integration.attempt
|
||||
.status({ attemptID: props.attempt.attemptID, location: location(data) }, { throwOnError: true })
|
||||
void sdk.api.integrations
|
||||
.attemptStatus({ attemptID: props.attempt.attemptID, location: location(data) })
|
||||
.then((result) => {
|
||||
const status = result.data.data
|
||||
const status = result.data
|
||||
if (status.status === "pending") {
|
||||
timer = setTimeout(poll, 500)
|
||||
return
|
||||
|
|
@ -292,7 +275,7 @@ function OAuthAuto(props: { integration: IntegrationInfo; title: string; attempt
|
|||
onCleanup(() => {
|
||||
if (timer) clearTimeout(timer)
|
||||
if (settled) return
|
||||
void sdk.client.v2.integration.attempt.cancel({ attemptID: props.attempt.attemptID, location: location(data) })
|
||||
void sdk.api.integrations.attemptCancel({ attemptID: props.attempt.attemptID, location: location(data) })
|
||||
})
|
||||
|
||||
return (
|
||||
|
|
@ -317,7 +300,7 @@ function OAuthCode(props: { integration: IntegrationInfo; title: string; attempt
|
|||
|
||||
onCleanup(() => {
|
||||
if (settled) return
|
||||
void sdk.client.v2.integration.attempt.cancel({ attemptID: props.attempt.attemptID, location: location(data) })
|
||||
void sdk.api.integrations.attemptCancel({ attemptID: props.attempt.attemptID, location: location(data) })
|
||||
})
|
||||
|
||||
return (
|
||||
|
|
@ -326,11 +309,8 @@ function OAuthCode(props: { integration: IntegrationInfo; title: string; attempt
|
|||
placeholder="Authorization code"
|
||||
onConfirm={(code) => {
|
||||
if (!code) return
|
||||
void sdk.client.v2.integration.attempt
|
||||
.complete(
|
||||
{ attemptID: props.attempt.attemptID, location: location(data), code },
|
||||
{ throwOnError: true },
|
||||
)
|
||||
void sdk.api.integrations
|
||||
.attemptComplete({ attemptID: props.attempt.attemptID, location: location(data), code })
|
||||
.then(() => {
|
||||
settled = true
|
||||
return connected(props.integration.name, data, dialog, toast)
|
||||
|
|
@ -348,13 +328,7 @@ function OAuthCode(props: { integration: IntegrationInfo; title: string; attempt
|
|||
)
|
||||
}
|
||||
|
||||
function OAuthView(props: {
|
||||
title: string
|
||||
url?: string
|
||||
instructions?: string
|
||||
message: string
|
||||
copy?: boolean
|
||||
}) {
|
||||
function OAuthView(props: { title: string; url?: string; instructions?: string; message: string; copy?: boolean }) {
|
||||
const dialog = useDialog()
|
||||
const { theme } = useTheme()
|
||||
return (
|
||||
|
|
@ -371,7 +345,9 @@ function OAuthView(props: {
|
|||
{(url) => (
|
||||
<box gap={1}>
|
||||
<Link href={url()} fg={theme.primary} />
|
||||
<Show when={props.instructions}>{(instructions) => <text fg={theme.textMuted}>{instructions()}</text>}</Show>
|
||||
<Show when={props.instructions}>
|
||||
{(instructions) => <text fg={theme.textMuted}>{instructions()}</text>}
|
||||
</Show>
|
||||
</box>
|
||||
)}
|
||||
</Show>
|
||||
|
|
@ -436,7 +412,11 @@ async function connected(
|
|||
dialog: ReturnType<typeof useDialog>,
|
||||
toast: ReturnType<typeof useToast>,
|
||||
) {
|
||||
await Promise.all([data.location.integration.refresh(), data.location.model.refresh(), data.location.provider.refresh()])
|
||||
await Promise.all([
|
||||
data.location.integration.refresh(),
|
||||
data.location.model.refresh(),
|
||||
data.location.provider.refresh(),
|
||||
])
|
||||
toast.show({ variant: "success", message: `Connected ${name}` })
|
||||
dialog.clear()
|
||||
}
|
||||
|
|
@ -447,7 +427,11 @@ async function disconnected(
|
|||
dialog: ReturnType<typeof useDialog>,
|
||||
toast: ReturnType<typeof useToast>,
|
||||
) {
|
||||
await Promise.all([data.location.integration.refresh(), data.location.model.refresh(), data.location.provider.refresh()])
|
||||
await Promise.all([
|
||||
data.location.integration.refresh(),
|
||||
data.location.model.refresh(),
|
||||
data.location.provider.refresh(),
|
||||
])
|
||||
toast.show({ variant: "success", message: `Disconnected ${name}` })
|
||||
dialog.clear()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { abbreviateHome } from "../runtime"
|
|||
import { useTuiPaths } from "../context/runtime"
|
||||
import { Locale } from "../util/locale"
|
||||
import { errorMessage } from "../util/error"
|
||||
import { isRecord } from "../util/record"
|
||||
import { useToast } from "../ui/toast"
|
||||
import { useCommandShortcut } from "../keymap"
|
||||
import { useProject } from "../context/project"
|
||||
|
|
@ -74,10 +75,10 @@ export function DialogMoveSession(props: DialogMoveSessionProps) {
|
|||
() => (props.initialRemoving ? undefined : props.projectID),
|
||||
async (projectID, info): Promise<ProjectDirectory[] | undefined> => {
|
||||
try {
|
||||
await sdk.client.v2.projectCopy.refresh(
|
||||
{ projectID, location: { directory: projectContext.instance.directory() || paths.cwd } },
|
||||
{ throwOnError: true },
|
||||
)
|
||||
await sdk.api.projectCopies.refresh({
|
||||
projectID,
|
||||
location: { directory: projectContext.instance.directory() || paths.cwd },
|
||||
})
|
||||
const directories = await sdk.client.project.directories({ projectID }, { throwOnError: true })
|
||||
setLoadError(undefined)
|
||||
return directories.data ?? []
|
||||
|
|
@ -221,18 +222,21 @@ export function DialogMoveSession(props: DialogMoveSessionProps) {
|
|||
setToDelete(undefined)
|
||||
setRemoving(selected.directory)
|
||||
setWorking(true)
|
||||
const result = await sdk.client.v2.projectCopy
|
||||
const error = await sdk.api.projectCopies
|
||||
.remove({
|
||||
projectID: props.projectID,
|
||||
location: { directory: projectContext.instance.directory() || paths.cwd },
|
||||
directory: selected.directory,
|
||||
force: false,
|
||||
})
|
||||
.catch((error) => ({ error }))
|
||||
if (result.error) {
|
||||
.then(
|
||||
() => undefined,
|
||||
(error) => error,
|
||||
)
|
||||
if (error) {
|
||||
setRemoving(undefined)
|
||||
setWorking(false)
|
||||
if ("data" in result.error && result.error.data.forceRequired) {
|
||||
if (isRecord(error) && isRecord(error.data) && error.data.forceRequired === true) {
|
||||
const status = await sdk.client.vcs.status({ directory: selected.directory }).catch(() => undefined)
|
||||
const choice = await DialogWorkspaceFileChanges.show(dialog, status?.data ?? [], {
|
||||
title: "Delete working copy?",
|
||||
|
|
@ -243,19 +247,22 @@ export function DialogMoveSession(props: DialogMoveSessionProps) {
|
|||
return
|
||||
}
|
||||
reopen(selected.directory)
|
||||
const forced = await sdk.client.v2.projectCopy
|
||||
const forcedError = await sdk.api.projectCopies
|
||||
.remove({
|
||||
projectID: props.projectID,
|
||||
location: { directory: projectContext.instance.directory() || paths.cwd },
|
||||
directory: selected.directory,
|
||||
force: true,
|
||||
})
|
||||
.catch((error) => ({ error }))
|
||||
if (forced.error) {
|
||||
.then(
|
||||
() => undefined,
|
||||
(error) => error,
|
||||
)
|
||||
if (forcedError) {
|
||||
toast.show({
|
||||
variant: "error",
|
||||
title: "Failed to delete project copy",
|
||||
message: errorMessage(forced.error),
|
||||
message: errorMessage(forcedError),
|
||||
})
|
||||
reopen()
|
||||
return
|
||||
|
|
@ -269,7 +276,7 @@ export function DialogMoveSession(props: DialogMoveSessionProps) {
|
|||
toast.show({
|
||||
variant: "error",
|
||||
title: "Failed to delete project copy",
|
||||
message: errorMessage(result.error),
|
||||
message: errorMessage(error),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,16 @@ export function DialogSessionList() {
|
|||
|
||||
const [searchResults] = createResource(search, async (query) => {
|
||||
if (!query) return
|
||||
const response = await sdk.client.v2.session.list(
|
||||
{ search: query, limit: 50, order: "desc" },
|
||||
{ throwOnError: true },
|
||||
)
|
||||
return { query, sessions: response.data.data }
|
||||
const location = data.location.default()
|
||||
const response = await sdk.api.sessions.list({
|
||||
search: query,
|
||||
limit: 50,
|
||||
order: "desc",
|
||||
directory: location.directory,
|
||||
workspace: location.workspaceID,
|
||||
})
|
||||
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- generated client output is readonly; session list UI reuses legacy mutable session types.
|
||||
return { query, sessions: structuredClone(response.data) as SessionV2Info[] }
|
||||
})
|
||||
|
||||
const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
|
||||
|
|
@ -59,7 +64,11 @@ export function DialogSessionList() {
|
|||
|
||||
const options = createMemo(() => {
|
||||
const today = new Date().toDateString()
|
||||
const sessionMap = new Map(sessions().filter((session) => !session.parentID).map((session) => [session.id, session]))
|
||||
const sessionMap = new Map(
|
||||
sessions()
|
||||
.filter((session) => !session.parentID)
|
||||
.map((session) => [session.id, session]),
|
||||
)
|
||||
const pinned = local.session.pinned().filter((sessionID) => sessionMap.has(sessionID))
|
||||
const pinnedSet = new Set(pinned)
|
||||
const slotByID = new Map(local.session.slots().map((sessionID, index) => [sessionID, index + 1]))
|
||||
|
|
|
|||
|
|
@ -17,11 +17,15 @@ export function DialogSessionRename(props: { sessionID: string; currentTitle?: s
|
|||
onConfirm={(value) => {
|
||||
const title = value.trim()
|
||||
if (!title) return
|
||||
void sdk.client.v2.session
|
||||
.rename({ sessionID: props.sessionID, title }, { throwOnError: true })
|
||||
void sdk.api.sessions
|
||||
.rename({ sessionID: props.sessionID, title })
|
||||
.then(() => dialog.clear())
|
||||
.catch((error) =>
|
||||
toast.show({ message: `Failed to rename session: ${errorMessage(error)}`, variant: "error", duration: 5000 }),
|
||||
toast.show({
|
||||
message: `Failed to rename session: ${errorMessage(error)}`,
|
||||
variant: "error",
|
||||
duration: 5000,
|
||||
}),
|
||||
)
|
||||
}}
|
||||
onCancel={() => dialog.clear()}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,15 @@ export function DialogTag(props: { onSelect?: (value: string) => void }) {
|
|||
const [files] = createResource(
|
||||
() => [store.filter],
|
||||
async () => {
|
||||
const result = await sdk.client.find.files({
|
||||
query: store.filter,
|
||||
workspace: project.workspace.current(),
|
||||
})
|
||||
if (result.error) return []
|
||||
const sliced = (result.data ?? []).slice(0, 5)
|
||||
return sliced
|
||||
const result = await sdk.api.files
|
||||
.find({
|
||||
query: store.filter,
|
||||
type: "file",
|
||||
limit: 5,
|
||||
location: { workspace: project.workspace.current() },
|
||||
})
|
||||
.catch(() => undefined)
|
||||
return result?.data.map((item) => item.path) ?? []
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -320,29 +320,26 @@ export function Autocomplete(props: {
|
|||
if (referenceMatch()) return []
|
||||
const { lineRange, baseQuery } = extractLineRange(input.query ?? "")
|
||||
|
||||
// Get files from SDK
|
||||
const result = await sdk.client.v2.fs.find({
|
||||
query: baseQuery,
|
||||
limit: "20",
|
||||
location: {
|
||||
directory: input.location?.directory,
|
||||
workspace: input.location?.workspaceID ?? project.workspace.current(),
|
||||
},
|
||||
})
|
||||
const result = await sdk.api.files
|
||||
.find({
|
||||
query: baseQuery,
|
||||
limit: 20,
|
||||
location: {
|
||||
directory: input.location?.directory,
|
||||
workspace: input.location?.workspaceID ?? project.workspace.current(),
|
||||
},
|
||||
})
|
||||
.catch(() => undefined)
|
||||
|
||||
const options: AutocompleteOption[] = []
|
||||
|
||||
// Add file options. Trust the order returned by fff (frecency, fuzzy
|
||||
// score, filename bonus, etc. are already factored in).
|
||||
if (!result.error && result.data) {
|
||||
if (result) {
|
||||
const width = props.anchor().width - 4
|
||||
options.push(
|
||||
...result.data.data.map((item): AutocompleteOption => {
|
||||
const { filename, part } = createFilePart(
|
||||
item,
|
||||
path.join(result.data.location.directory, item.path),
|
||||
lineRange,
|
||||
)
|
||||
...result.data.map((item): AutocompleteOption => {
|
||||
const { filename, part } = createFilePart(item, path.join(result.location.directory, item.path), lineRange)
|
||||
return {
|
||||
display: Locale.truncateMiddle(filename, width),
|
||||
value: filename,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import { usePromptStash } from "../../prompt/stash"
|
|||
import { DialogStash } from "../dialog-stash"
|
||||
import { type AutocompleteRef, Autocomplete } from "./autocomplete"
|
||||
import { useRenderer, useTerminalDimensions, type JSX } from "@opentui/solid"
|
||||
import type { AssistantMessage, FilePart, UserMessage } from "@opencode-ai/sdk/v2"
|
||||
import type { AssistantMessage, FilePart, SessionV2Info, UserMessage } from "@opencode-ai/sdk/v2"
|
||||
import { Locale } from "../../util/locale"
|
||||
import { errorMessage } from "../../util/error"
|
||||
import { createColors, createFrames } from "../../ui/spinner"
|
||||
|
|
@ -158,11 +158,12 @@ export function Prompt(props: PromptProps) {
|
|||
const dialog = useDialog()
|
||||
const toast = useToast()
|
||||
const status = createMemo(() => data.session.status(props.sessionID ?? ""))
|
||||
const activeSubagents = createMemo(() =>
|
||||
data.session
|
||||
.list()
|
||||
.filter((session) => session.parentID === props.sessionID && data.session.status(session.id) === "running")
|
||||
.length,
|
||||
const activeSubagents = createMemo(
|
||||
() =>
|
||||
data.session
|
||||
.list()
|
||||
.filter((session) => session.parentID === props.sessionID && data.session.status(session.id) === "running")
|
||||
.length,
|
||||
)
|
||||
const runningShells = createMemo(
|
||||
() => data.shell.list().filter((shell) => shell.metadata.sessionID === props.sessionID).length,
|
||||
|
|
@ -424,7 +425,7 @@ export function Prompt(props: PromptProps) {
|
|||
}, 5000)
|
||||
|
||||
if (store.interrupt >= 2) {
|
||||
void sdk.client.v2.session.interrupt({
|
||||
void sdk.api.sessions.interrupt({
|
||||
sessionID: props.sessionID,
|
||||
})
|
||||
setStore("interrupt", 0)
|
||||
|
|
@ -1009,18 +1010,23 @@ export function Prompt(props: PromptProps) {
|
|||
const directory = await move.getDirectory(store.prompt.input)
|
||||
if (move.pending() && !directory) return false
|
||||
finishMoveProgress = Boolean(move.progress())
|
||||
const location = data.location.default()
|
||||
|
||||
const res = await sdk.client.v2.session.create({
|
||||
location: directory ? { directory, workspaceID } : undefined,
|
||||
agent: agent.id,
|
||||
model: {
|
||||
providerID: selectedModel.providerID,
|
||||
id: selectedModel.modelID,
|
||||
variant,
|
||||
},
|
||||
})
|
||||
const created = await sdk.api.sessions
|
||||
.create({
|
||||
location: directory
|
||||
? { directory, workspaceID }
|
||||
: { directory: location.directory, workspaceID: workspaceID ?? location.workspaceID },
|
||||
agent: agent.id,
|
||||
model: {
|
||||
providerID: selectedModel.providerID,
|
||||
id: selectedModel.modelID,
|
||||
variant,
|
||||
},
|
||||
})
|
||||
.catch(() => undefined)
|
||||
|
||||
if (res.error) {
|
||||
if (!created) {
|
||||
if (finishMoveProgress) move.finishSubmit()
|
||||
toast.show({
|
||||
message: "Creating a session failed. Open console for more details.",
|
||||
|
|
@ -1030,8 +1036,9 @@ export function Prompt(props: PromptProps) {
|
|||
return true
|
||||
}
|
||||
|
||||
sessionID = res.data.data.id
|
||||
session = res.data.data
|
||||
sessionID = created.id
|
||||
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- generated client output is readonly; prompt state still uses legacy mutable session types.
|
||||
session = structuredClone(created) as SessionV2Info
|
||||
}
|
||||
|
||||
const inputText = expandTrackedPastedText(
|
||||
|
|
@ -1107,65 +1114,70 @@ export function Prompt(props: PromptProps) {
|
|||
session = data.session.get(sessionID)
|
||||
}
|
||||
if (session?.agent !== agent.id) {
|
||||
await sdk.client.v2.session.switchAgent({ sessionID, agent: agent.id }, { throwOnError: true })
|
||||
await sdk.api.sessions.switchAgent({ sessionID, agent: agent.id })
|
||||
}
|
||||
if (
|
||||
session?.model?.providerID !== selectedModel.providerID ||
|
||||
session.model.id !== selectedModel.modelID ||
|
||||
session.model.variant !== variant
|
||||
) {
|
||||
await sdk.client.v2.session.switchModel(
|
||||
{
|
||||
sessionID,
|
||||
model: { providerID: selectedModel.providerID, id: selectedModel.modelID, variant },
|
||||
},
|
||||
{ throwOnError: true },
|
||||
)
|
||||
await sdk.api.sessions.switchModel({
|
||||
sessionID,
|
||||
model: { providerID: selectedModel.providerID, id: selectedModel.modelID, variant },
|
||||
})
|
||||
}
|
||||
if (session?.revert) {
|
||||
const revertResult = await sdk.client.v2.session.revert.commit({ sessionID })
|
||||
if (revertResult.error) {
|
||||
toast.show({ title: "Failed to commit revert", message: errorMessage(revertResult.error), variant: "error" })
|
||||
const error = await sdk.api.sessions.commit({ sessionID }).then(
|
||||
() => undefined,
|
||||
(error) => error,
|
||||
)
|
||||
if (error) {
|
||||
toast.show({ title: "Failed to commit revert", message: errorMessage(error), variant: "error" })
|
||||
return false
|
||||
}
|
||||
}
|
||||
const result = await sdk.client.v2.session.prompt({
|
||||
sessionID,
|
||||
prompt: {
|
||||
text: [...editorParts.map((part) => part.text), inputText].filter(Boolean).join("\n\n"),
|
||||
files: nonTextParts.flatMap((part) =>
|
||||
part.type === "file"
|
||||
? [
|
||||
{
|
||||
uri: part.url,
|
||||
name: part.filename,
|
||||
source: part.source
|
||||
? {
|
||||
start: part.source.text.start,
|
||||
end: part.source.text.end,
|
||||
text: part.source.text.value,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
),
|
||||
agents: nonTextParts.flatMap((part) =>
|
||||
part.type === "agent"
|
||||
? [
|
||||
{
|
||||
name: part.name,
|
||||
source: part.source
|
||||
? { start: part.source.start, end: part.source.end, text: part.source.value }
|
||||
: undefined,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
),
|
||||
},
|
||||
})
|
||||
if (result.error) {
|
||||
toast.show({ title: "Failed to send prompt", message: errorMessage(result.error), variant: "error" })
|
||||
const error = await sdk.api.sessions
|
||||
.prompt({
|
||||
sessionID,
|
||||
prompt: {
|
||||
text: [...editorParts.map((part) => part.text), inputText].filter(Boolean).join("\n\n"),
|
||||
files: nonTextParts.flatMap((part) =>
|
||||
part.type === "file"
|
||||
? [
|
||||
{
|
||||
uri: part.url,
|
||||
name: part.filename,
|
||||
source: part.source
|
||||
? {
|
||||
start: part.source.text.start,
|
||||
end: part.source.text.end,
|
||||
text: part.source.text.value,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
),
|
||||
agents: nonTextParts.flatMap((part) =>
|
||||
part.type === "agent"
|
||||
? [
|
||||
{
|
||||
name: part.name,
|
||||
source: part.source
|
||||
? { start: part.source.start, end: part.source.end, text: part.source.value }
|
||||
: undefined,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
),
|
||||
},
|
||||
})
|
||||
.then(
|
||||
() => undefined,
|
||||
(error) => error,
|
||||
)
|
||||
if (error) {
|
||||
toast.show({ title: "Failed to send prompt", message: errorMessage(error), variant: "error" })
|
||||
return false
|
||||
}
|
||||
if (editorParts.length > 0) editor.markSelectionSent()
|
||||
|
|
|
|||
|
|
@ -37,17 +37,14 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
{ projectID, context },
|
||||
{ throwOnError: true },
|
||||
)
|
||||
const result = await sdk.client.v2.projectCopy.create(
|
||||
{
|
||||
projectID,
|
||||
location: { directory: project.instance.directory() || paths.cwd },
|
||||
strategy: "git_worktree",
|
||||
directory: path.join(paths.worktree, projectID.slice(0, 6)),
|
||||
name: generated.data.name,
|
||||
},
|
||||
{ throwOnError: true },
|
||||
)
|
||||
const directory = result.data?.directory
|
||||
const result = await sdk.api.projectCopies.create({
|
||||
projectID,
|
||||
location: { directory: project.instance.directory() || paths.cwd },
|
||||
strategy: "git_worktree",
|
||||
directory: path.join(paths.worktree, projectID.slice(0, 6)),
|
||||
name: generated.data.name,
|
||||
})
|
||||
const directory = result.directory
|
||||
if (!directory) throw new Error("No project copy directory returned")
|
||||
|
||||
// Call a location-based route to make sure it's bootstrapped
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue