feat(aircoding): AirCoding V2 baseline — deterministic multi-agent architecture
Forked from OpenCode v1.17.4 with multi-agent system: - 5 agents: aircoding, scheduler, worker, architect, reviewer - Deterministic DAG scheduling engine (coordinator_tick) - Tool whitelists as hard enforcement - AirCoding validation plugin - System prompt injection for routing - V1 requirements: C4 docs, ADR, AGENTS.md, debug-log.md - Design documents in docs/
This commit is contained in:
103
packages/tui/src/component/dialog-console-org.tsx
Normal file
103
packages/tui/src/component/dialog-console-org.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import { createResource, createMemo } from "solid-js"
|
||||
import { DialogSelect } from "../ui/dialog-select"
|
||||
import { useSDK } from "../context/sdk"
|
||||
import { useDialog } from "../ui/dialog"
|
||||
import { useToast } from "../ui/toast"
|
||||
import { useTheme } from "../context/theme"
|
||||
import type { ExperimentalConsoleListOrgsResponse } from "@opencode-ai/sdk/v2"
|
||||
|
||||
type OrgOption = ExperimentalConsoleListOrgsResponse["orgs"][number]
|
||||
|
||||
const accountHost = (url: string) => {
|
||||
try {
|
||||
return new URL(url).host
|
||||
} catch {
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
const accountLabel = (item: Pick<OrgOption, "accountEmail" | "accountUrl">) =>
|
||||
`${item.accountEmail} ${accountHost(item.accountUrl)}`
|
||||
|
||||
export function DialogConsoleOrg() {
|
||||
const sdk = useSDK()
|
||||
const dialog = useDialog()
|
||||
const toast = useToast()
|
||||
const { theme } = useTheme()
|
||||
|
||||
const [orgs] = createResource(async () => {
|
||||
const result = await sdk.client.experimental.console.listOrgs({}, { throwOnError: true })
|
||||
return result.data?.orgs ?? []
|
||||
})
|
||||
|
||||
const current = createMemo(() => orgs()?.find((item) => item.active))
|
||||
|
||||
const options = createMemo(() => {
|
||||
const listed = orgs()
|
||||
if (listed === undefined) {
|
||||
return [
|
||||
{
|
||||
title: "Loading orgs...",
|
||||
value: "loading",
|
||||
onSelect: () => {},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
if (listed.length === 0) {
|
||||
return [
|
||||
{
|
||||
title: "No orgs found",
|
||||
value: "empty",
|
||||
onSelect: () => {},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return listed
|
||||
.toSorted((a, b) => {
|
||||
const activeAccountA = a.active ? 0 : 1
|
||||
const activeAccountB = b.active ? 0 : 1
|
||||
if (activeAccountA !== activeAccountB) return activeAccountA - activeAccountB
|
||||
|
||||
const accountCompare = accountLabel(a).localeCompare(accountLabel(b))
|
||||
if (accountCompare !== 0) return accountCompare
|
||||
|
||||
return a.orgName.localeCompare(b.orgName)
|
||||
})
|
||||
.map((item) => ({
|
||||
title: item.orgName,
|
||||
value: item,
|
||||
category: accountLabel(item),
|
||||
categoryView: (
|
||||
<box flexDirection="row" gap={2}>
|
||||
<text fg={theme.accent}>{item.accountEmail}</text>
|
||||
<text fg={theme.textMuted}>{accountHost(item.accountUrl)}</text>
|
||||
</box>
|
||||
),
|
||||
onSelect: async () => {
|
||||
if (item.active) {
|
||||
dialog.clear()
|
||||
return
|
||||
}
|
||||
|
||||
await sdk.client.experimental.console.switchOrg(
|
||||
{
|
||||
accountID: item.accountID,
|
||||
orgID: item.orgID,
|
||||
},
|
||||
{ throwOnError: true },
|
||||
)
|
||||
|
||||
await sdk.client.instance.dispose()
|
||||
toast.show({
|
||||
message: `Switched to ${item.orgName}`,
|
||||
variant: "info",
|
||||
})
|
||||
dialog.clear()
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
return <DialogSelect<string | OrgOption> title="Switch org" options={options()} current={current()} />
|
||||
}
|
||||
Reference in New Issue
Block a user