172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
import type {
|
|
AgentListOutput,
|
|
ModelDefaultOutput,
|
|
ModelListOutput,
|
|
PermissionV2Request,
|
|
ProviderListOutput,
|
|
} from "@opencode-ai/client/promise"
|
|
import type { Agent, PermissionRequest, Project, Provider, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
|
|
import type { Project as CurrentProject } from "@opencode-ai/client/promise"
|
|
import { NormalizedProviderListResponse } from "@opencode-ai/session-ui/context"
|
|
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 function normalizeAgentList(input: AgentListOutput["data"] | Agent[]): Agent[] {
|
|
if (input.every((agent) => !("request" in agent))) return input as Agent[]
|
|
return (input as AgentListOutput["data"]).map((agent) => ({
|
|
name: agent.id,
|
|
description: agent.description,
|
|
mode: agent.mode,
|
|
hidden: agent.hidden,
|
|
temperature:
|
|
typeof agent.request.settings.temperature === "number" ? agent.request.settings.temperature : undefined,
|
|
topP: typeof agent.request.settings.topP === "number" ? agent.request.settings.topP : undefined,
|
|
color: agent.color,
|
|
permission: agent.permissions.map((rule) => ({
|
|
permission: rule.action,
|
|
pattern: rule.resource,
|
|
action: rule.effect,
|
|
})),
|
|
model: agent.model && { providerID: agent.model.providerID, modelID: agent.model.id },
|
|
variant: agent.model?.variant,
|
|
prompt: agent.system,
|
|
options: agent.request.settings,
|
|
steps: agent.steps,
|
|
}))
|
|
}
|
|
|
|
export function normalizePermissionRequest(input: PermissionV2Request | PermissionRequest): PermissionRequest {
|
|
if ("permission" in input) return input
|
|
return {
|
|
id: input.id,
|
|
sessionID: input.sessionID,
|
|
permission: input.action,
|
|
patterns: input.resources,
|
|
always: input.save ?? [],
|
|
metadata: input.metadata ?? {},
|
|
tool:
|
|
input.source?.type === "tool" ? { messageID: input.source.messageID, callID: input.source.callID } : undefined,
|
|
}
|
|
}
|
|
|
|
export function normalizeProviderList(
|
|
providers: ProviderListOutput["data"] | ProviderListResponse,
|
|
models?: ModelListOutput["data"],
|
|
defaultModel?: ModelDefaultOutput["data"],
|
|
): NormalizedProviderListResponse {
|
|
if (!Array.isArray(providers)) {
|
|
return {
|
|
...providers,
|
|
all: new Map(
|
|
providers.all.map((provider) => [
|
|
provider.id,
|
|
{
|
|
...provider,
|
|
models: Object.fromEntries(
|
|
Object.entries(provider.models).filter(([, model]) => model.status !== "deprecated"),
|
|
),
|
|
},
|
|
]),
|
|
),
|
|
}
|
|
}
|
|
const all = new Map<string, Provider>()
|
|
|
|
for (const provider of providers) {
|
|
all.set(provider.id, {
|
|
id: provider.id,
|
|
name: provider.name,
|
|
source: "custom",
|
|
env: [],
|
|
options: provider.settings ?? {},
|
|
models: {},
|
|
})
|
|
}
|
|
|
|
for (const model of models ?? []) {
|
|
const provider = all.get(model.providerID)
|
|
if (!provider || model.status === "deprecated") continue
|
|
const cost = model.cost.find((item) => item.tier === undefined) ?? model.cost[0]
|
|
provider.models[model.id] = {
|
|
id: model.id,
|
|
providerID: model.providerID,
|
|
api: {
|
|
id: model.modelID,
|
|
url: "",
|
|
npm: model.package ?? provider.id,
|
|
},
|
|
name: model.name,
|
|
family: model.family,
|
|
capabilities: {
|
|
temperature: false,
|
|
reasoning: false,
|
|
attachment: model.capabilities.input.some((item) => item !== "text"),
|
|
toolcall: model.capabilities.tools,
|
|
input: {
|
|
text: model.capabilities.input.includes("text"),
|
|
audio: model.capabilities.input.includes("audio"),
|
|
image: model.capabilities.input.includes("image"),
|
|
video: model.capabilities.input.includes("video"),
|
|
pdf: model.capabilities.input.includes("pdf"),
|
|
},
|
|
output: {
|
|
text: model.capabilities.output.includes("text"),
|
|
audio: model.capabilities.output.includes("audio"),
|
|
image: model.capabilities.output.includes("image"),
|
|
video: model.capabilities.output.includes("video"),
|
|
pdf: model.capabilities.output.includes("pdf"),
|
|
},
|
|
interleaved: false,
|
|
},
|
|
cost: {
|
|
input: cost?.input ?? 0,
|
|
output: cost?.output ?? 0,
|
|
cache: {
|
|
read: cost?.cache.read ?? 0,
|
|
write: cost?.cache.write ?? 0,
|
|
},
|
|
},
|
|
limit: model.limit,
|
|
status: model.status,
|
|
options: model.settings ?? {},
|
|
headers: model.headers ?? {},
|
|
release_date: new Date(model.time.released).toISOString().slice(0, 10),
|
|
variants: Object.fromEntries(model.variants.map((variant) => [variant.id, variant.settings ?? {}])),
|
|
}
|
|
}
|
|
|
|
return {
|
|
all,
|
|
connected: providers.map((provider) => provider.id),
|
|
defaultModel: defaultModel ? { providerID: defaultModel.providerID, modelID: defaultModel.id } : null,
|
|
default: Object.fromEntries(
|
|
providers.flatMap((provider) => {
|
|
const model =
|
|
defaultModel?.providerID === provider.id
|
|
? defaultModel
|
|
: models?.find((item) => item.providerID === provider.id && item.status !== "deprecated")
|
|
return model ? [[provider.id, model.id]] : []
|
|
}),
|
|
),
|
|
}
|
|
}
|
|
|
|
export function sanitizeProject(project: Project) {
|
|
if (!project.icon?.url && !project.icon?.override) return project
|
|
return {
|
|
...project,
|
|
icon: {
|
|
...project.icon,
|
|
url: undefined,
|
|
override: undefined,
|
|
},
|
|
}
|
|
}
|
|
|
|
export function normalizeProjectInfo(project: Project | CurrentProject): Project {
|
|
return {
|
|
...project,
|
|
vcs: project.vcs === "git" ? "git" : undefined,
|
|
}
|
|
}
|