@thelioo/opencode-balancer 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.txt +16 -7
- package/README.md +24 -45
- package/dist/core/accounts.d.ts +14 -0
- package/dist/core/accounts.js +260 -0
- package/dist/core/accounts.js.map +1 -0
- package/dist/core/database.d.ts +4 -0
- package/dist/core/database.js +69 -0
- package/dist/core/database.js.map +1 -0
- package/dist/core/events.d.ts +18 -0
- package/dist/core/events.js +39 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/native-auth-suppression.d.ts +3 -0
- package/dist/core/native-auth-suppression.js +19 -0
- package/dist/core/native-auth-suppression.js.map +1 -0
- package/dist/core/native-connect.d.ts +4 -0
- package/dist/core/native-connect.js +19 -0
- package/dist/core/native-connect.js.map +1 -0
- package/dist/core/path.d.ts +4 -0
- package/dist/core/path.js +26 -0
- package/dist/core/path.js.map +1 -0
- package/dist/core/pending.d.ts +9 -0
- package/dist/core/pending.js +237 -0
- package/dist/core/pending.js.map +1 -0
- package/dist/core/priority.d.ts +20 -0
- package/dist/core/priority.js +120 -0
- package/dist/core/priority.js.map +1 -0
- package/dist/core/schema.d.ts +2 -0
- package/dist/core/schema.js +265 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/time.d.ts +1 -0
- package/dist/core/time.js +4 -0
- package/dist/core/time.js.map +1 -0
- package/dist/core/types.d.ts +59 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/usage/index.d.ts +4 -0
- package/dist/core/usage/index.js +16 -0
- package/dist/core/usage/index.js.map +1 -0
- package/dist/core/usage/providers/copilot.d.ts +2 -0
- package/dist/core/usage/providers/copilot.js +169 -0
- package/dist/core/usage/providers/copilot.js.map +1 -0
- package/dist/core/usage/providers/openai.d.ts +2 -0
- package/dist/core/usage/providers/openai.js +133 -0
- package/dist/core/usage/providers/openai.js.map +1 -0
- package/dist/core/usage/redact.d.ts +3 -0
- package/dist/core/usage/redact.js +67 -0
- package/dist/core/usage/redact.js.map +1 -0
- package/dist/core/usage/store.d.ts +4 -0
- package/dist/core/usage/store.js +31 -0
- package/dist/core/usage/store.js.map +1 -0
- package/dist/core/usage/types.d.ts +21 -0
- package/dist/core/usage/types.js +2 -0
- package/dist/core/usage/types.js.map +1 -0
- package/dist/index.d.ts +1 -37
- package/dist/index.js +1 -60
- package/dist/index.js.map +1 -1
- package/dist/server/auth-watcher.d.ts +31 -0
- package/dist/server/auth-watcher.js +227 -0
- package/dist/server/auth-watcher.js.map +1 -0
- package/dist/server/commands.d.ts +2 -0
- package/dist/server/commands.js +46 -0
- package/dist/server/commands.js.map +1 -0
- package/dist/server/fetch-patch.d.ts +3 -0
- package/dist/server/fetch-patch.js +118 -0
- package/dist/server/fetch-patch.js.map +1 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.js +94 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/native.d.ts +6 -0
- package/dist/server/native.js +35 -0
- package/dist/server/native.js.map +1 -0
- package/dist/server/request-balancer.d.ts +16 -0
- package/dist/server/request-balancer.js +43 -0
- package/dist/server/request-balancer.js.map +1 -0
- package/dist/tui/actions.d.ts +41 -0
- package/dist/tui/actions.js +88 -0
- package/dist/tui/actions.js.map +1 -0
- package/dist/tui/actions.ts +140 -0
- package/dist/tui/balancer-bar-sync.d.ts +19 -0
- package/dist/tui/balancer-bar-sync.js +45 -0
- package/dist/tui/balancer-bar-sync.js.map +1 -0
- package/dist/tui/balancer-bar-sync.ts +56 -0
- package/dist/tui/components/alias-dialog.d.ts +4 -0
- package/dist/tui/components/alias-dialog.js +57 -0
- package/dist/tui/components/alias-dialog.js.map +1 -0
- package/dist/tui/components/alias-dialog.tsx +72 -0
- package/dist/tui/components/dashboard.d.ts +12 -0
- package/dist/tui/components/dashboard.js +201 -0
- package/dist/tui/components/dashboard.js.map +1 -0
- package/dist/tui/components/dashboard.tsx +393 -0
- package/dist/tui/components/priority-screen.d.ts +9 -0
- package/dist/tui/components/priority-screen.js +120 -0
- package/dist/tui/components/priority-screen.js.map +1 -0
- package/dist/tui/components/priority-screen.tsx +302 -0
- package/dist/tui/components/provider-model-dialog.d.ts +13 -0
- package/dist/tui/components/provider-model-dialog.js +45 -0
- package/dist/tui/components/provider-model-dialog.js.map +1 -0
- package/dist/tui/components/provider-model-dialog.tsx +71 -0
- package/dist/tui/components/rename-dialog.d.ts +4 -0
- package/dist/tui/components/rename-dialog.js +24 -0
- package/dist/tui/components/rename-dialog.js.map +1 -0
- package/dist/tui/components/rename-dialog.tsx +40 -0
- package/dist/tui/components/sidebar.d.ts +10 -0
- package/dist/tui/components/sidebar.js +27 -0
- package/dist/tui/components/sidebar.js.map +1 -0
- package/dist/tui/components/sidebar.tsx +97 -0
- package/dist/tui/components/status-indicator.d.ts +9 -0
- package/dist/tui/components/status-indicator.js +59 -0
- package/dist/tui/components/status-indicator.js.map +1 -0
- package/dist/tui/components/status-indicator.tsx +78 -0
- package/dist/tui/components/usage-bar.d.ts +8 -0
- package/dist/tui/components/usage-bar.js +7 -0
- package/dist/tui/components/usage-bar.js.map +1 -0
- package/dist/tui/components/usage-bar.tsx +13 -0
- package/dist/tui/components/usage-display.d.ts +10 -0
- package/dist/tui/components/usage-display.js +32 -0
- package/dist/tui/components/usage-display.js.map +1 -0
- package/dist/tui/components/usage-display.tsx +29 -0
- package/dist/tui/connect.d.ts +30 -0
- package/dist/tui/connect.js +73 -0
- package/dist/tui/connect.js.map +1 -0
- package/dist/tui/connect.ts +100 -0
- package/dist/tui/dashboard-keys.d.ts +45 -0
- package/dist/tui/dashboard-keys.js +44 -0
- package/dist/tui/dashboard-keys.js.map +1 -0
- package/dist/tui/dashboard-keys.ts +60 -0
- package/dist/tui/native-model-apply.d.ts +21 -0
- package/dist/tui/native-model-apply.js +53 -0
- package/dist/tui/native-model-apply.js.map +1 -0
- package/dist/tui/native-model-apply.ts +73 -0
- package/dist/tui/priority-keys.d.ts +40 -0
- package/dist/tui/priority-keys.js +38 -0
- package/dist/tui/priority-keys.js.map +1 -0
- package/dist/tui/priority-keys.ts +59 -0
- package/dist/tui/provider-models.d.ts +19 -0
- package/dist/tui/provider-models.js +17 -0
- package/dist/tui/provider-models.js.map +1 -0
- package/dist/tui/provider-models.ts +36 -0
- package/dist/tui/responsive.d.ts +9 -0
- package/dist/tui/responsive.js +13 -0
- package/dist/tui/responsive.js.map +1 -0
- package/dist/tui/responsive.ts +16 -0
- package/dist/tui/selection-colors.d.ts +10 -0
- package/dist/tui/selection-colors.js +38 -0
- package/dist/tui/selection-colors.js.map +1 -0
- package/dist/tui/selection-colors.ts +45 -0
- package/dist/tui/state.d.ts +14 -0
- package/dist/tui/state.js +46 -0
- package/dist/tui/state.js.map +1 -0
- package/dist/tui/state.ts +65 -0
- package/dist/tui/status-format.d.ts +15 -0
- package/dist/tui/status-format.js +17 -0
- package/dist/tui/status-format.js.map +1 -0
- package/dist/tui/status-format.ts +29 -0
- package/dist/tui/tui.d.ts +7 -0
- package/dist/tui/tui.js +120 -0
- package/dist/tui/tui.js.map +1 -0
- package/dist/tui/tui.tsx +142 -0
- package/dist/tui/usage-auto-refresh.d.ts +16 -0
- package/dist/tui/usage-auto-refresh.js +46 -0
- package/dist/tui/usage-auto-refresh.js.map +1 -0
- package/dist/tui/usage-auto-refresh.ts +64 -0
- package/dist/tui/usage-format.d.ts +2 -0
- package/dist/tui/usage-format.js +17 -0
- package/dist/tui/usage-format.js.map +1 -0
- package/dist/tui/usage-format.ts +14 -0
- package/package.json +10 -3
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
4
|
+
import { normalizeAlias } from "../../core/accounts";
|
|
5
|
+
import { renameAccountFromTui } from "../actions";
|
|
6
|
+
import type { BalancerTuiState } from "../state";
|
|
7
|
+
|
|
8
|
+
function errorMessage(error: unknown) {
|
|
9
|
+
return error instanceof Error && error.message ? error.message : "Failed to rename account";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function openRenameDialog(api: TuiPluginApi, state: BalancerTuiState, providerID: string, alias: string) {
|
|
13
|
+
api.ui.dialog.setSize("medium");
|
|
14
|
+
api.ui.dialog.replace(() => (
|
|
15
|
+
<api.ui.DialogPrompt
|
|
16
|
+
title="Rename account"
|
|
17
|
+
placeholder="account alias"
|
|
18
|
+
description={() => (
|
|
19
|
+
<text fg={api.theme.current.textMuted} wrapMode="none">
|
|
20
|
+
Choose a new alias for {providerID}/{alias}.
|
|
21
|
+
</text>
|
|
22
|
+
)}
|
|
23
|
+
onCancel={() => api.ui.dialog.clear()}
|
|
24
|
+
onConfirm={(value) => {
|
|
25
|
+
const nextAlias = normalizeAlias(value);
|
|
26
|
+
if (!nextAlias) {
|
|
27
|
+
api.ui.toast({ variant: "error", message: "Alias is required" });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
renameAccountFromTui(api, state, providerID, alias, nextAlias);
|
|
33
|
+
api.ui.dialog.clear();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
api.ui.toast({ variant: "error", message: errorMessage(error) });
|
|
36
|
+
}
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
));
|
|
40
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
3
|
+
import type { BalancerTuiState } from "../state";
|
|
4
|
+
export type BalancerSidebarProps = {
|
|
5
|
+
api: TuiPluginApi;
|
|
6
|
+
state: BalancerTuiState;
|
|
7
|
+
openDashboard: () => void;
|
|
8
|
+
activateAccount: (providerID: string, alias: string) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function BalancerSidebar(props: BalancerSidebarProps): any;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/solid/jsx-runtime";
|
|
2
|
+
import { createSignal, For, onCleanup, Show } from "solid-js";
|
|
3
|
+
import { listAccounts } from "../../core/accounts";
|
|
4
|
+
import { getBalancingEnabled } from "../../core/priority";
|
|
5
|
+
import { getUsageSnapshot } from "../../core/usage/store";
|
|
6
|
+
import { UsageSnapshotBar } from "./usage-display";
|
|
7
|
+
export function BalancerSidebar(props) {
|
|
8
|
+
const theme = () => props.api.theme.current;
|
|
9
|
+
const [accounts, setAccounts] = createSignal(listAccounts(props.state.db));
|
|
10
|
+
const [usage, setUsage] = createSignal({});
|
|
11
|
+
const [balancingEnabled, setBalancingEnabled] = createSignal(getBalancingEnabled(props.state.db));
|
|
12
|
+
const refreshSidebar = () => {
|
|
13
|
+
const nextAccounts = listAccounts(props.state.db);
|
|
14
|
+
setAccounts(nextAccounts);
|
|
15
|
+
setBalancingEnabled(getBalancingEnabled(props.state.db));
|
|
16
|
+
setUsage(Object.fromEntries(nextAccounts.map((account) => [
|
|
17
|
+
`${account.providerID}/${account.alias}`,
|
|
18
|
+
getUsageSnapshot(props.state.db, account.providerID, account.alias),
|
|
19
|
+
])));
|
|
20
|
+
};
|
|
21
|
+
refreshSidebar();
|
|
22
|
+
const timer = setInterval(refreshSidebar, 500);
|
|
23
|
+
onCleanup(() => clearInterval(timer));
|
|
24
|
+
const Button = (buttonProps) => (_jsx("box", { onMouseUp: buttonProps.onClick, paddingLeft: 0, paddingRight: 0, children: _jsxs("text", { fg: theme().accent, wrapMode: "none", truncate: true, children: ["[ ", buttonProps.label, " ]"] }) }));
|
|
25
|
+
return (_jsxs("box", { flexDirection: "column", gap: 1, children: [_jsxs("box", { flexDirection: "column", gap: 0, children: [_jsx("text", { fg: theme().text, wrapMode: "none", children: "Balancer" }), _jsx("text", { fg: balancingEnabled() ? theme().success : theme().textMuted, wrapMode: "none", children: balancingEnabled() ? "ON" : "OFF" }), _jsx(Button, { label: "dashboard", onClick: () => props.openDashboard() }), _jsx("text", { fg: theme().textMuted, wrapMode: "none", children: "ctrl+b" })] }), _jsxs("box", { flexDirection: "column", gap: 0, children: [_jsx("text", { fg: theme().textMuted, wrapMode: "none", children: "Accounts" }), _jsx(Show, { when: accounts().length > 0, fallback: _jsx("text", { fg: theme().textMuted, wrapMode: "none", children: "none" }), children: _jsx(For, { each: accounts(), children: (account) => (_jsxs("box", { flexDirection: "column", gap: 0, onMouseUp: () => props.activateAccount(account.providerID, account.alias), children: [_jsxs("text", { fg: theme().text, wrapMode: "none", truncate: true, children: [_jsx("span", { style: { fg: theme().primary }, children: account.providerID }), "/", _jsx("span", { style: { fg: account.disabled ? theme().textMuted : theme().text }, children: account.alias }), " ", _jsxs("span", { style: { fg: theme().textMuted }, children: ["(", account.authType, ")"] })] }), _jsx(UsageSnapshotBar, { theme: theme(), snapshot: usage()[`${account.providerID}/${account.alias}`] })] })) }) })] })] }));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=sidebar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidebar.js","sourceRoot":"","sources":["../../../src/tui/components/sidebar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
4
|
+
import { createSignal, For, onCleanup, Show } from "solid-js";
|
|
5
|
+
import { listAccounts } from "../../core/accounts";
|
|
6
|
+
import { getBalancingEnabled } from "../../core/priority";
|
|
7
|
+
import { getUsageSnapshot } from "../../core/usage/store";
|
|
8
|
+
import type { Account } from "../../core/types";
|
|
9
|
+
import type { ProviderUsageSnapshot } from "../../core/usage/types";
|
|
10
|
+
import type { BalancerTuiState } from "../state";
|
|
11
|
+
import { UsageSnapshotBar } from "./usage-display";
|
|
12
|
+
|
|
13
|
+
export type BalancerSidebarProps = {
|
|
14
|
+
api: TuiPluginApi;
|
|
15
|
+
state: BalancerTuiState;
|
|
16
|
+
openDashboard: () => void;
|
|
17
|
+
activateAccount: (providerID: string, alias: string) => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function BalancerSidebar(props: BalancerSidebarProps) {
|
|
21
|
+
const theme = () => props.api.theme.current;
|
|
22
|
+
const [accounts, setAccounts] = createSignal<Account[]>(listAccounts(props.state.db));
|
|
23
|
+
const [usage, setUsage] = createSignal<Record<string, ProviderUsageSnapshot | undefined>>({});
|
|
24
|
+
const [balancingEnabled, setBalancingEnabled] = createSignal(getBalancingEnabled(props.state.db));
|
|
25
|
+
const refreshSidebar = () => {
|
|
26
|
+
const nextAccounts = listAccounts(props.state.db);
|
|
27
|
+
setAccounts(nextAccounts);
|
|
28
|
+
setBalancingEnabled(getBalancingEnabled(props.state.db));
|
|
29
|
+
setUsage(
|
|
30
|
+
Object.fromEntries(
|
|
31
|
+
nextAccounts.map((account) => [
|
|
32
|
+
`${account.providerID}/${account.alias}`,
|
|
33
|
+
getUsageSnapshot(props.state.db, account.providerID, account.alias),
|
|
34
|
+
]),
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
refreshSidebar();
|
|
39
|
+
const timer = setInterval(refreshSidebar, 500);
|
|
40
|
+
onCleanup(() => clearInterval(timer));
|
|
41
|
+
const Button = (buttonProps: { label: string; onClick: () => void }) => (
|
|
42
|
+
<box onMouseUp={buttonProps.onClick} paddingLeft={0} paddingRight={0}>
|
|
43
|
+
<text fg={theme().accent} wrapMode="none" truncate>
|
|
44
|
+
[ {buttonProps.label} ]
|
|
45
|
+
</text>
|
|
46
|
+
</box>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<box flexDirection="column" gap={1}>
|
|
51
|
+
<box flexDirection="column" gap={0}>
|
|
52
|
+
<text fg={theme().text} wrapMode="none">
|
|
53
|
+
Balancer
|
|
54
|
+
</text>
|
|
55
|
+
<text fg={balancingEnabled() ? theme().success : theme().textMuted} wrapMode="none">
|
|
56
|
+
{balancingEnabled() ? "ON" : "OFF"}
|
|
57
|
+
</text>
|
|
58
|
+
<Button label="dashboard" onClick={() => props.openDashboard()} />
|
|
59
|
+
<text fg={theme().textMuted} wrapMode="none">
|
|
60
|
+
ctrl+b
|
|
61
|
+
</text>
|
|
62
|
+
</box>
|
|
63
|
+
|
|
64
|
+
<box flexDirection="column" gap={0}>
|
|
65
|
+
<text fg={theme().textMuted} wrapMode="none">
|
|
66
|
+
Accounts
|
|
67
|
+
</text>
|
|
68
|
+
<Show
|
|
69
|
+
when={accounts().length > 0}
|
|
70
|
+
fallback={
|
|
71
|
+
<text fg={theme().textMuted} wrapMode="none">
|
|
72
|
+
none
|
|
73
|
+
</text>
|
|
74
|
+
}
|
|
75
|
+
>
|
|
76
|
+
<For each={accounts()}>
|
|
77
|
+
{(account) => (
|
|
78
|
+
<box flexDirection="column" gap={0} onMouseUp={() => props.activateAccount(account.providerID, account.alias)}>
|
|
79
|
+
<text fg={theme().text} wrapMode="none" truncate>
|
|
80
|
+
<span style={{ fg: theme().primary }}>{account.providerID}</span>/
|
|
81
|
+
<span style={{ fg: account.disabled ? theme().textMuted : theme().text }}>
|
|
82
|
+
{account.alias}
|
|
83
|
+
</span>{" "}
|
|
84
|
+
<span style={{ fg: theme().textMuted }}>({account.authType})</span>
|
|
85
|
+
</text>
|
|
86
|
+
<UsageSnapshotBar
|
|
87
|
+
theme={theme()}
|
|
88
|
+
snapshot={usage()[`${account.providerID}/${account.alias}`]}
|
|
89
|
+
/>
|
|
90
|
+
</box>
|
|
91
|
+
)}
|
|
92
|
+
</For>
|
|
93
|
+
</Show>
|
|
94
|
+
</box>
|
|
95
|
+
</box>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
3
|
+
import type { BalancerTuiState } from "../state";
|
|
4
|
+
export type BalancerStatusIndicatorProps = {
|
|
5
|
+
api: TuiPluginApi;
|
|
6
|
+
state: BalancerTuiState;
|
|
7
|
+
providerID?: string | (() => string | undefined);
|
|
8
|
+
};
|
|
9
|
+
export declare function BalancerStatusIndicator(props: BalancerStatusIndicatorProps): any;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
|
|
2
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
3
|
+
import { getActiveAccount, getSelectedAccount } from "../../core/accounts";
|
|
4
|
+
import { getBalancingEnabled, resolveActiveSelection } from "../../core/priority";
|
|
5
|
+
import { getUsageSnapshot } from "../../core/usage/store";
|
|
6
|
+
import { formatBalancerStatus } from "../status-format";
|
|
7
|
+
import { formatUsageBar } from "../usage-format";
|
|
8
|
+
import { snapshotPercent } from "./usage-display";
|
|
9
|
+
export function BalancerStatusIndicator(props) {
|
|
10
|
+
const [selected, setSelected] = createSignal();
|
|
11
|
+
const [sessionActive, setSessionActive] = createSignal();
|
|
12
|
+
const [balancing, setBalancing] = createSignal();
|
|
13
|
+
const [usage, setUsage] = createSignal();
|
|
14
|
+
const providerID = () => (typeof props.providerID === "function" ? props.providerID() : props.providerID);
|
|
15
|
+
const refresh = () => {
|
|
16
|
+
const currentProviderID = providerID();
|
|
17
|
+
const nextSelected = getSelectedAccount(props.state.db);
|
|
18
|
+
const nextSessionActive = currentProviderID ? getActiveAccount(props.state.db, currentProviderID) : undefined;
|
|
19
|
+
const nextBalancing = getBalancingEnabled(props.state.db)
|
|
20
|
+
? resolveActiveSelection(props.state.db, undefined, currentProviderID)
|
|
21
|
+
: undefined;
|
|
22
|
+
const usageAccount = nextBalancing?.account ?? nextSelected ?? nextSessionActive;
|
|
23
|
+
setSelected(nextSelected);
|
|
24
|
+
setSessionActive(nextSessionActive);
|
|
25
|
+
setBalancing(nextBalancing);
|
|
26
|
+
setUsage(usageAccount
|
|
27
|
+
? formatUsageBar(snapshotPercent(getUsageSnapshot(props.state.db, usageAccount.providerID, usageAccount.alias)), 4)
|
|
28
|
+
: undefined);
|
|
29
|
+
};
|
|
30
|
+
refresh();
|
|
31
|
+
const timer = setInterval(refresh, 500);
|
|
32
|
+
onCleanup(() => clearInterval(timer));
|
|
33
|
+
const status = () => {
|
|
34
|
+
return formatBalancerStatus({
|
|
35
|
+
selected: selected(),
|
|
36
|
+
sessionActive: sessionActive(),
|
|
37
|
+
sessionProviderID: providerID(),
|
|
38
|
+
balancing: balancing()
|
|
39
|
+
? {
|
|
40
|
+
providerID: balancing().providerID,
|
|
41
|
+
alias: balancing().account.alias,
|
|
42
|
+
modelID: balancing().modelID,
|
|
43
|
+
}
|
|
44
|
+
: undefined,
|
|
45
|
+
usage: usage(),
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const color = () => {
|
|
49
|
+
if (balancing())
|
|
50
|
+
return props.api.theme.current.success;
|
|
51
|
+
if (providerID())
|
|
52
|
+
return sessionActive() ? props.api.theme.current.success : props.api.theme.current.textMuted;
|
|
53
|
+
if (!selected())
|
|
54
|
+
return props.api.theme.current.textMuted;
|
|
55
|
+
return props.api.theme.current.success;
|
|
56
|
+
};
|
|
57
|
+
return (_jsx("text", { fg: color(), wrapMode: "none", truncate: true, children: status() }));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=status-indicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-indicator.js","sourceRoot":"","sources":["../../../src/tui/components/status-indicator.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
4
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
5
|
+
import { getActiveAccount, getSelectedAccount } from "../../core/accounts";
|
|
6
|
+
import { getBalancingEnabled, resolveActiveSelection, type ActiveSelection } from "../../core/priority";
|
|
7
|
+
import type { Account } from "../../core/types";
|
|
8
|
+
import { getUsageSnapshot } from "../../core/usage/store";
|
|
9
|
+
import { formatBalancerStatus } from "../status-format";
|
|
10
|
+
import type { BalancerTuiState } from "../state";
|
|
11
|
+
import { formatUsageBar } from "../usage-format";
|
|
12
|
+
import { snapshotPercent } from "./usage-display";
|
|
13
|
+
|
|
14
|
+
export type BalancerStatusIndicatorProps = {
|
|
15
|
+
api: TuiPluginApi;
|
|
16
|
+
state: BalancerTuiState;
|
|
17
|
+
providerID?: string | (() => string | undefined);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function BalancerStatusIndicator(props: BalancerStatusIndicatorProps) {
|
|
21
|
+
const [selected, setSelected] = createSignal<Account | undefined>();
|
|
22
|
+
const [sessionActive, setSessionActive] = createSignal<Account | undefined>();
|
|
23
|
+
const [balancing, setBalancing] = createSignal<ActiveSelection | undefined>();
|
|
24
|
+
const [usage, setUsage] = createSignal<string | undefined>();
|
|
25
|
+
const providerID = () => (typeof props.providerID === "function" ? props.providerID() : props.providerID);
|
|
26
|
+
const refresh = () => {
|
|
27
|
+
const currentProviderID = providerID();
|
|
28
|
+
const nextSelected = getSelectedAccount(props.state.db);
|
|
29
|
+
const nextSessionActive = currentProviderID ? getActiveAccount(props.state.db, currentProviderID) : undefined;
|
|
30
|
+
const nextBalancing = getBalancingEnabled(props.state.db)
|
|
31
|
+
? resolveActiveSelection(props.state.db, undefined, currentProviderID)
|
|
32
|
+
: undefined;
|
|
33
|
+
const usageAccount = nextBalancing?.account ?? nextSelected ?? nextSessionActive;
|
|
34
|
+
setSelected(nextSelected);
|
|
35
|
+
setSessionActive(nextSessionActive);
|
|
36
|
+
setBalancing(nextBalancing);
|
|
37
|
+
setUsage(
|
|
38
|
+
usageAccount
|
|
39
|
+
? formatUsageBar(
|
|
40
|
+
snapshotPercent(getUsageSnapshot(props.state.db, usageAccount.providerID, usageAccount.alias)),
|
|
41
|
+
4,
|
|
42
|
+
)
|
|
43
|
+
: undefined,
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
refresh();
|
|
47
|
+
const timer = setInterval(refresh, 500);
|
|
48
|
+
onCleanup(() => clearInterval(timer));
|
|
49
|
+
|
|
50
|
+
const status = () => {
|
|
51
|
+
return formatBalancerStatus({
|
|
52
|
+
selected: selected(),
|
|
53
|
+
sessionActive: sessionActive(),
|
|
54
|
+
sessionProviderID: providerID(),
|
|
55
|
+
balancing: balancing()
|
|
56
|
+
? {
|
|
57
|
+
providerID: balancing()!.providerID,
|
|
58
|
+
alias: balancing()!.account.alias,
|
|
59
|
+
modelID: balancing()!.modelID,
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
62
|
+
usage: usage(),
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const color = () => {
|
|
67
|
+
if (balancing()) return props.api.theme.current.success;
|
|
68
|
+
if (providerID()) return sessionActive() ? props.api.theme.current.success : props.api.theme.current.textMuted;
|
|
69
|
+
if (!selected()) return props.api.theme.current.textMuted;
|
|
70
|
+
return props.api.theme.current.success;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<text fg={color()} wrapMode="none" truncate>
|
|
75
|
+
{status()}
|
|
76
|
+
</text>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/solid/jsx-runtime";
|
|
2
|
+
import { formatUsageBar, truncateMiddle } from "../usage-format";
|
|
3
|
+
export function UsageBar(props) {
|
|
4
|
+
const color = () => (props.muted || props.percent === undefined ? props.theme.textMuted : props.theme.primary);
|
|
5
|
+
return (_jsxs("text", { fg: color(), wrapMode: "none", overflow: "hidden", truncate: true, children: [formatUsageBar(props.percent, 8), " ", _jsx("span", { style: { fg: props.theme.textMuted }, children: truncateMiddle(props.label, 34) })] }));
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=usage-bar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-bar.js","sourceRoot":"","sources":["../../../src/tui/components/usage-bar.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjE,MAAM,UAAU,QAAQ,CAAC,KAAmF;IACxG,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/G,OAAO,CACH,gBAAM,EAAE,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAC,MAAM,EAAC,QAAQ,EAAC,QAAQ,EAAC,QAAQ,mBACxD,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,OAAE,eAAM,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,YAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,GAAQ,IACpH,CACV,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui";
|
|
4
|
+
import { formatUsageBar, truncateMiddle } from "../usage-format";
|
|
5
|
+
|
|
6
|
+
export function UsageBar(props: { theme: TuiThemeCurrent; percent?: number; label: string; muted?: boolean }) {
|
|
7
|
+
const color = () => (props.muted || props.percent === undefined ? props.theme.textMuted : props.theme.primary);
|
|
8
|
+
return (
|
|
9
|
+
<text fg={color()} wrapMode="none" overflow="hidden" truncate>
|
|
10
|
+
{formatUsageBar(props.percent, 8)} <span style={{ fg: props.theme.textMuted }}>{truncateMiddle(props.label, 34)}</span>
|
|
11
|
+
</text>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui";
|
|
3
|
+
import type { ProviderUsageSnapshot } from "../../core/usage/types";
|
|
4
|
+
export declare function snapshotPercent(snapshot: ProviderUsageSnapshot | undefined): number | undefined;
|
|
5
|
+
export declare function snapshotLabel(snapshot: ProviderUsageSnapshot | undefined): string;
|
|
6
|
+
export declare function UsageSnapshotBar(props: {
|
|
7
|
+
theme: TuiThemeCurrent;
|
|
8
|
+
snapshot?: ProviderUsageSnapshot;
|
|
9
|
+
muted?: boolean;
|
|
10
|
+
}): any;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
|
|
2
|
+
import { UsageBar } from "./usage-bar";
|
|
3
|
+
export function snapshotPercent(snapshot) {
|
|
4
|
+
if (!snapshot || snapshot.confidence === "unavailable")
|
|
5
|
+
return undefined;
|
|
6
|
+
if (snapshot.usedPercent !== undefined)
|
|
7
|
+
return snapshot.usedPercent;
|
|
8
|
+
if (snapshot.usedTokens !== undefined && snapshot.remainingTokens !== undefined) {
|
|
9
|
+
const total = snapshot.usedTokens + snapshot.remainingTokens;
|
|
10
|
+
if (total > 0)
|
|
11
|
+
return (snapshot.usedTokens / total) * 100;
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
export function snapshotLabel(snapshot) {
|
|
16
|
+
if (!snapshot)
|
|
17
|
+
return "usage unavailable";
|
|
18
|
+
if (snapshot.confidence === "unavailable")
|
|
19
|
+
return snapshot.message;
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (snapshot.usedTokens !== undefined)
|
|
22
|
+
parts.push(`used ${snapshot.usedTokens}`);
|
|
23
|
+
if (snapshot.remainingTokens !== undefined)
|
|
24
|
+
parts.push(`remaining ${snapshot.remainingTokens}`);
|
|
25
|
+
if (snapshot.planName)
|
|
26
|
+
parts.push(snapshot.planName);
|
|
27
|
+
return parts.length > 0 ? parts.join(" · ") : snapshot.message;
|
|
28
|
+
}
|
|
29
|
+
export function UsageSnapshotBar(props) {
|
|
30
|
+
return _jsx(UsageBar, { theme: props.theme, percent: snapshotPercent(props.snapshot), label: snapshotLabel(props.snapshot), muted: props.muted });
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=usage-display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-display.js","sourceRoot":"","sources":["../../../src/tui/components/usage-display.tsx"],"names":[],"mappings":";AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,UAAU,eAAe,CAAC,QAA2C;IACvE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,UAAU,KAAK,aAAa;QAAE,OAAO,SAAS,CAAC;IACzE,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC,WAAW,CAAC;IACpE,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC,eAAe,CAAC;QAC7D,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO,CAAC,QAAQ,CAAC,UAAU,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;IAC9D,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAA2C;IACrE,IAAI,CAAC,QAAQ;QAAE,OAAO,mBAAmB,CAAC;IAC1C,IAAI,QAAQ,CAAC,UAAU,KAAK,aAAa;QAAE,OAAO,QAAQ,CAAC,OAAO,CAAC;IACnE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACjF,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC;IAChG,IAAI,QAAQ,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoF;IACjH,OAAO,KAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,GAAI,CAAC;AAChJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui";
|
|
4
|
+
import type { ProviderUsageSnapshot } from "../../core/usage/types";
|
|
5
|
+
import { UsageBar } from "./usage-bar";
|
|
6
|
+
|
|
7
|
+
export function snapshotPercent(snapshot: ProviderUsageSnapshot | undefined) {
|
|
8
|
+
if (!snapshot || snapshot.confidence === "unavailable") return undefined;
|
|
9
|
+
if (snapshot.usedPercent !== undefined) return snapshot.usedPercent;
|
|
10
|
+
if (snapshot.usedTokens !== undefined && snapshot.remainingTokens !== undefined) {
|
|
11
|
+
const total = snapshot.usedTokens + snapshot.remainingTokens;
|
|
12
|
+
if (total > 0) return (snapshot.usedTokens / total) * 100;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function snapshotLabel(snapshot: ProviderUsageSnapshot | undefined) {
|
|
18
|
+
if (!snapshot) return "usage unavailable";
|
|
19
|
+
if (snapshot.confidence === "unavailable") return snapshot.message;
|
|
20
|
+
const parts: string[] = [];
|
|
21
|
+
if (snapshot.usedTokens !== undefined) parts.push(`used ${snapshot.usedTokens}`);
|
|
22
|
+
if (snapshot.remainingTokens !== undefined) parts.push(`remaining ${snapshot.remainingTokens}`);
|
|
23
|
+
if (snapshot.planName) parts.push(snapshot.planName);
|
|
24
|
+
return parts.length > 0 ? parts.join(" · ") : snapshot.message;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function UsageSnapshotBar(props: { theme: TuiThemeCurrent; snapshot?: ProviderUsageSnapshot; muted?: boolean }) {
|
|
28
|
+
return <UsageBar theme={props.theme} percent={snapshotPercent(props.snapshot)} label={snapshotLabel(props.snapshot)} muted={props.muted} />;
|
|
29
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { AuthInfo } from "../core/types";
|
|
3
|
+
type NativeAuthReadResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
auth: Record<string, AuthInfo>;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
};
|
|
9
|
+
type ConnectApi = {
|
|
10
|
+
db?: Database;
|
|
11
|
+
readAuth?: () => NativeAuthReadResult;
|
|
12
|
+
generateAlias?: () => string;
|
|
13
|
+
wait?: (ms: number) => Promise<void>;
|
|
14
|
+
maxWaitMs?: number;
|
|
15
|
+
pollIntervalMs?: number;
|
|
16
|
+
keymap?: {
|
|
17
|
+
dispatchCommand?: (command: string) => unknown;
|
|
18
|
+
};
|
|
19
|
+
ui?: {
|
|
20
|
+
dialog?: {
|
|
21
|
+
open?: boolean;
|
|
22
|
+
};
|
|
23
|
+
toast?: (input: {
|
|
24
|
+
variant: "success" | "error";
|
|
25
|
+
message: string;
|
|
26
|
+
}) => unknown;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export declare function openNativeConnect(api: ConnectApi): Promise<void>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { saveAccount } from "../core/accounts";
|
|
2
|
+
import { clearNativeConnectInProgress, markNativeConnectInProgress } from "../core/native-connect";
|
|
3
|
+
import { readNativeAuth } from "../server/auth-watcher";
|
|
4
|
+
const aliasAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
5
|
+
function generatedAlias() {
|
|
6
|
+
let alias = "";
|
|
7
|
+
for (let index = 0; index < 5; index++)
|
|
8
|
+
alias += aliasAlphabet[Math.floor(Math.random() * aliasAlphabet.length)];
|
|
9
|
+
return alias;
|
|
10
|
+
}
|
|
11
|
+
function authKey(auth) {
|
|
12
|
+
return JSON.stringify(auth ?? null);
|
|
13
|
+
}
|
|
14
|
+
function changedProvider(before, after) {
|
|
15
|
+
return Object.entries(after).find(([providerID, auth]) => authKey(before[providerID]) !== authKey(auth));
|
|
16
|
+
}
|
|
17
|
+
function uniqueAlias(db, providerID, generate) {
|
|
18
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
19
|
+
const alias = generate();
|
|
20
|
+
const existing = db
|
|
21
|
+
.query("SELECT alias FROM accounts WHERE provider_id = ? AND alias = ?")
|
|
22
|
+
.get(providerID, alias);
|
|
23
|
+
if (!existing)
|
|
24
|
+
return alias;
|
|
25
|
+
}
|
|
26
|
+
throw new Error("Could not generate a unique alias");
|
|
27
|
+
}
|
|
28
|
+
async function waitForChangedProvider(readAuth, before, options) {
|
|
29
|
+
const started = Date.now();
|
|
30
|
+
while (true) {
|
|
31
|
+
const after = readAuth();
|
|
32
|
+
if (before.ok && after.ok) {
|
|
33
|
+
const changed = changedProvider(before.auth, after.auth);
|
|
34
|
+
if (changed)
|
|
35
|
+
return changed;
|
|
36
|
+
}
|
|
37
|
+
if (Date.now() - started >= options.maxWaitMs)
|
|
38
|
+
return;
|
|
39
|
+
await options.wait(options.pollIntervalMs);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function openNativeConnect(api) {
|
|
43
|
+
if (api.keymap?.dispatchCommand) {
|
|
44
|
+
const readAuth = api.readAuth ?? readNativeAuth;
|
|
45
|
+
const before = readAuth();
|
|
46
|
+
if (api.db)
|
|
47
|
+
markNativeConnectInProgress(api.db);
|
|
48
|
+
await api.keymap.dispatchCommand("provider.connect");
|
|
49
|
+
if (api.db) {
|
|
50
|
+
try {
|
|
51
|
+
const changed = await waitForChangedProvider(readAuth, before, {
|
|
52
|
+
wait: api.wait ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
53
|
+
maxWaitMs: api.maxWaitMs ?? (api.ui?.dialog ? 10 * 60 * 1000 : 0),
|
|
54
|
+
pollIntervalMs: api.pollIntervalMs ?? 500,
|
|
55
|
+
});
|
|
56
|
+
if (changed) {
|
|
57
|
+
const [providerID, auth] = changed;
|
|
58
|
+
const account = saveAccount(api.db, providerID, uniqueAlias(api.db, providerID, api.generateAlias ?? generatedAlias), auth);
|
|
59
|
+
api.ui?.toast?.({ variant: "success", message: `Saved ${account.providerID}/${account.alias}.` });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
clearNativeConnectInProgress(api.db);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
api.ui?.toast?.({
|
|
69
|
+
variant: "error",
|
|
70
|
+
message: "Native provider connect is unavailable in this opencode build.",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/tui/connect.ts"],"names":[],"mappings":"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"}
|