@thelioo/opencode-balancer 0.1.3 → 0.1.5
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 +4 -36
- package/dist/index.js +3 -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,16 @@
|
|
|
1
|
+
export type LayoutMode = "compact" | "full";
|
|
2
|
+
|
|
3
|
+
export function dashboardLayoutMode(size: { width?: number; height?: number }): LayoutMode {
|
|
4
|
+
const width = size.width ?? 999;
|
|
5
|
+
const height = size.height ?? 999;
|
|
6
|
+
return width < 100 || height < 26 ? "compact" : "full";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function visibleRecentEventLimit(mode: LayoutMode): number {
|
|
10
|
+
return mode === "compact" ? 5 : 10;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function dashboardContentHeight(size: { height?: number }): number {
|
|
14
|
+
const height = size.height ?? 28;
|
|
15
|
+
return Math.max(6, height - 8);
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function selectedRowColors(theme) {
|
|
2
|
+
const bg = theme.backgroundElement;
|
|
3
|
+
const textContrast = contrast(theme.text, bg);
|
|
4
|
+
const backgroundContrast = theme.background === undefined ? undefined : contrast(theme.background, bg);
|
|
5
|
+
const fg = backgroundContrast !== undefined && backgroundContrast > textContrast ? theme.background : theme.text;
|
|
6
|
+
return { fg, bg };
|
|
7
|
+
}
|
|
8
|
+
function contrast(a, b) {
|
|
9
|
+
const ca = channels(a);
|
|
10
|
+
const cb = channels(b);
|
|
11
|
+
if (!ca || !cb)
|
|
12
|
+
return 0;
|
|
13
|
+
const la = luminance(ca);
|
|
14
|
+
const lb = luminance(cb);
|
|
15
|
+
const lighter = Math.max(la, lb);
|
|
16
|
+
const darker = Math.min(la, lb);
|
|
17
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
18
|
+
}
|
|
19
|
+
function channels(value) {
|
|
20
|
+
if (!value || typeof value !== "object")
|
|
21
|
+
return undefined;
|
|
22
|
+
const color = value;
|
|
23
|
+
const r = numberChannel(color.r ?? color.red);
|
|
24
|
+
const g = numberChannel(color.g ?? color.green);
|
|
25
|
+
const b = numberChannel(color.b ?? color.blue);
|
|
26
|
+
return r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };
|
|
27
|
+
}
|
|
28
|
+
function numberChannel(value) {
|
|
29
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(255, value)) : undefined;
|
|
30
|
+
}
|
|
31
|
+
function luminance(color) {
|
|
32
|
+
const [r, g, b] = [color.r, color.g, color.b].map((channel) => {
|
|
33
|
+
const value = channel / 255;
|
|
34
|
+
return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
35
|
+
});
|
|
36
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=selection-colors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-colors.js","sourceRoot":"","sources":["../../src/tui/selection-colors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function selectedRowColors<TColor>(theme: {
|
|
2
|
+
text: TColor;
|
|
3
|
+
textMuted?: TColor;
|
|
4
|
+
backgroundElement: TColor;
|
|
5
|
+
background?: TColor;
|
|
6
|
+
accent?: TColor;
|
|
7
|
+
}) {
|
|
8
|
+
const bg = theme.backgroundElement;
|
|
9
|
+
const textContrast = contrast(theme.text, bg);
|
|
10
|
+
const backgroundContrast = theme.background === undefined ? undefined : contrast(theme.background, bg);
|
|
11
|
+
const fg = backgroundContrast !== undefined && backgroundContrast > textContrast ? theme.background : theme.text;
|
|
12
|
+
return { fg, bg };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function contrast<TColor>(a: TColor, b: TColor) {
|
|
16
|
+
const ca = channels(a);
|
|
17
|
+
const cb = channels(b);
|
|
18
|
+
if (!ca || !cb) return 0;
|
|
19
|
+
const la = luminance(ca);
|
|
20
|
+
const lb = luminance(cb);
|
|
21
|
+
const lighter = Math.max(la, lb);
|
|
22
|
+
const darker = Math.min(la, lb);
|
|
23
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function channels(value: unknown) {
|
|
27
|
+
if (!value || typeof value !== "object") return undefined;
|
|
28
|
+
const color = value as Record<string, unknown>;
|
|
29
|
+
const r = numberChannel(color.r ?? color.red);
|
|
30
|
+
const g = numberChannel(color.g ?? color.green);
|
|
31
|
+
const b = numberChannel(color.b ?? color.blue);
|
|
32
|
+
return r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function numberChannel(value: unknown) {
|
|
36
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(255, value)) : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function luminance(color: { r: number; g: number; b: number }) {
|
|
40
|
+
const [r, g, b] = [color.r, color.g, color.b].map((channel) => {
|
|
41
|
+
const value = channel / 255;
|
|
42
|
+
return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
43
|
+
});
|
|
44
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
45
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { Account, BalancerEvent, PendingConnection } from "../core/types";
|
|
3
|
+
export type BalancerTuiState = {
|
|
4
|
+
db: Database;
|
|
5
|
+
version: () => number;
|
|
6
|
+
refresh: () => void;
|
|
7
|
+
accounts: () => Account[];
|
|
8
|
+
pending: () => PendingConnection[];
|
|
9
|
+
events: () => BalancerEvent[];
|
|
10
|
+
removeAccountView: (providerID: string, alias: string) => void;
|
|
11
|
+
removePendingView: (pendingID: string) => void;
|
|
12
|
+
dispose: () => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function createBalancerTuiState(): BalancerTuiState;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
2
|
+
import { listAccounts } from "../core/accounts";
|
|
3
|
+
import { closeBalancerDatabase, openBalancerDatabase } from "../core/database";
|
|
4
|
+
import { listEvents } from "../core/events";
|
|
5
|
+
import { listPendingConnections } from "../core/pending";
|
|
6
|
+
import { storePath } from "../core/path";
|
|
7
|
+
import { migrate } from "../core/schema";
|
|
8
|
+
export function createBalancerTuiState() {
|
|
9
|
+
const dbPath = storePath();
|
|
10
|
+
const db = openBalancerDatabase(dbPath);
|
|
11
|
+
migrate(db);
|
|
12
|
+
const [version, setVersion] = createSignal(0);
|
|
13
|
+
const [accounts, setAccounts] = createSignal([]);
|
|
14
|
+
const [pending, setPending] = createSignal([]);
|
|
15
|
+
const [events, setEvents] = createSignal([]);
|
|
16
|
+
const refresh = () => {
|
|
17
|
+
setAccounts(listAccounts(db));
|
|
18
|
+
setPending(listPendingConnections(db));
|
|
19
|
+
setEvents(listEvents(db, 10));
|
|
20
|
+
setVersion((current) => current + 1);
|
|
21
|
+
};
|
|
22
|
+
const removeAccountView = (providerID, alias) => {
|
|
23
|
+
setAccounts((current) => current.filter((account) => account.providerID !== providerID || account.alias !== alias));
|
|
24
|
+
setVersion((current) => current + 1);
|
|
25
|
+
};
|
|
26
|
+
const removePendingView = (pendingID) => {
|
|
27
|
+
setPending((current) => current.filter((pending) => pending.id !== pendingID));
|
|
28
|
+
setVersion((current) => current + 1);
|
|
29
|
+
};
|
|
30
|
+
let interval;
|
|
31
|
+
let disposed = false;
|
|
32
|
+
const dispose = () => {
|
|
33
|
+
if (disposed)
|
|
34
|
+
return;
|
|
35
|
+
disposed = true;
|
|
36
|
+
if (interval)
|
|
37
|
+
clearInterval(interval);
|
|
38
|
+
interval = undefined;
|
|
39
|
+
closeBalancerDatabase(dbPath);
|
|
40
|
+
};
|
|
41
|
+
refresh();
|
|
42
|
+
interval = setInterval(refresh, 1000);
|
|
43
|
+
onCleanup(dispose);
|
|
44
|
+
return { db, version, refresh, accounts, pending, events, removeAccountView, removePendingView, dispose };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/tui/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
3
|
+
import { listAccounts } from "../core/accounts";
|
|
4
|
+
import { closeBalancerDatabase, openBalancerDatabase } from "../core/database";
|
|
5
|
+
import { listEvents } from "../core/events";
|
|
6
|
+
import { listPendingConnections } from "../core/pending";
|
|
7
|
+
import { storePath } from "../core/path";
|
|
8
|
+
import { migrate } from "../core/schema";
|
|
9
|
+
import type { Account, BalancerEvent, PendingConnection } from "../core/types";
|
|
10
|
+
|
|
11
|
+
export type BalancerTuiState = {
|
|
12
|
+
db: Database;
|
|
13
|
+
version: () => number;
|
|
14
|
+
refresh: () => void;
|
|
15
|
+
accounts: () => Account[];
|
|
16
|
+
pending: () => PendingConnection[];
|
|
17
|
+
events: () => BalancerEvent[];
|
|
18
|
+
removeAccountView: (providerID: string, alias: string) => void;
|
|
19
|
+
removePendingView: (pendingID: string) => void;
|
|
20
|
+
dispose: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function createBalancerTuiState(): BalancerTuiState {
|
|
24
|
+
const dbPath = storePath();
|
|
25
|
+
const db = openBalancerDatabase(dbPath);
|
|
26
|
+
migrate(db);
|
|
27
|
+
|
|
28
|
+
const [version, setVersion] = createSignal(0);
|
|
29
|
+
const [accounts, setAccounts] = createSignal<Account[]>([]);
|
|
30
|
+
const [pending, setPending] = createSignal<PendingConnection[]>([]);
|
|
31
|
+
const [events, setEvents] = createSignal<BalancerEvent[]>([]);
|
|
32
|
+
|
|
33
|
+
const refresh = () => {
|
|
34
|
+
setAccounts(listAccounts(db));
|
|
35
|
+
setPending(listPendingConnections(db));
|
|
36
|
+
setEvents(listEvents(db, 10));
|
|
37
|
+
setVersion((current) => current + 1);
|
|
38
|
+
};
|
|
39
|
+
const removeAccountView = (providerID: string, alias: string) => {
|
|
40
|
+
setAccounts((current) => current.filter((account) => account.providerID !== providerID || account.alias !== alias));
|
|
41
|
+
setVersion((current) => current + 1);
|
|
42
|
+
};
|
|
43
|
+
const removePendingView = (pendingID: string) => {
|
|
44
|
+
setPending((current) => current.filter((pending) => pending.id !== pendingID));
|
|
45
|
+
setVersion((current) => current + 1);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let interval: ReturnType<typeof setInterval> | undefined;
|
|
49
|
+
let disposed = false;
|
|
50
|
+
|
|
51
|
+
const dispose = () => {
|
|
52
|
+
if (disposed) return;
|
|
53
|
+
disposed = true;
|
|
54
|
+
if (interval) clearInterval(interval);
|
|
55
|
+
interval = undefined;
|
|
56
|
+
closeBalancerDatabase(dbPath);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
refresh();
|
|
60
|
+
|
|
61
|
+
interval = setInterval(refresh, 1000);
|
|
62
|
+
onCleanup(dispose);
|
|
63
|
+
|
|
64
|
+
return { db, version, refresh, accounts, pending, events, removeAccountView, removePendingView, dispose };
|
|
65
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type StatusAccount = {
|
|
2
|
+
providerID: string;
|
|
3
|
+
alias: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function formatBalancerStatus(input: {
|
|
6
|
+
selected?: StatusAccount;
|
|
7
|
+
sessionActive?: StatusAccount;
|
|
8
|
+
sessionProviderID?: string;
|
|
9
|
+
balancing?: {
|
|
10
|
+
providerID: string;
|
|
11
|
+
alias?: string;
|
|
12
|
+
modelID: string;
|
|
13
|
+
};
|
|
14
|
+
usage?: string;
|
|
15
|
+
}): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function formatBalancerStatus(input) {
|
|
2
|
+
const withUsage = (value) => (input.usage ? `${value} · ${input.usage}` : value);
|
|
3
|
+
if (input.balancing) {
|
|
4
|
+
const account = input.balancing.alias
|
|
5
|
+
? `${input.balancing.providerID}/${input.balancing.alias}`
|
|
6
|
+
: input.balancing.providerID;
|
|
7
|
+
return withUsage(account);
|
|
8
|
+
}
|
|
9
|
+
if (!input.selected) {
|
|
10
|
+
if (input.sessionProviderID)
|
|
11
|
+
return withUsage(`${input.sessionProviderID}/${input.sessionActive?.alias ?? "none"}`);
|
|
12
|
+
return withUsage("balancer");
|
|
13
|
+
}
|
|
14
|
+
const selected = `${input.selected.providerID}/${input.selected.alias}`;
|
|
15
|
+
return withUsage(selected);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=status-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-format.js","sourceRoot":"","sources":["../../src/tui/status-format.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,oBAAoB,CAAC,KAMpC;IACG,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAEzF,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK;YACjC,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE;YAC1D,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC;QACjC,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClB,IAAI,KAAK,CAAC,iBAAiB;YAAE,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,iBAAiB,IAAI,KAAK,CAAC,aAAa,EAAE,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC;QACpH,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxE,OAAO,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type StatusAccount = {
|
|
2
|
+
providerID: string;
|
|
3
|
+
alias: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function formatBalancerStatus(input: {
|
|
7
|
+
selected?: StatusAccount;
|
|
8
|
+
sessionActive?: StatusAccount;
|
|
9
|
+
sessionProviderID?: string;
|
|
10
|
+
balancing?: { providerID: string; alias?: string; modelID: string };
|
|
11
|
+
usage?: string;
|
|
12
|
+
}) {
|
|
13
|
+
const withUsage = (value: string) => (input.usage ? `${value} · ${input.usage}` : value);
|
|
14
|
+
|
|
15
|
+
if (input.balancing) {
|
|
16
|
+
const account = input.balancing.alias
|
|
17
|
+
? `${input.balancing.providerID}/${input.balancing.alias}`
|
|
18
|
+
: input.balancing.providerID;
|
|
19
|
+
return withUsage(account);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!input.selected) {
|
|
23
|
+
if (input.sessionProviderID) return withUsage(`${input.sessionProviderID}/${input.sessionActive?.alias ?? "none"}`);
|
|
24
|
+
return withUsage("balancer");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const selected = `${input.selected.providerID}/${input.selected.alias}`;
|
|
28
|
+
return withUsage(selected);
|
|
29
|
+
}
|
package/dist/tui/tui.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import { createComponent } from "solid-js";
|
|
3
|
+
import { setProviderModel } from "../core/priority";
|
|
4
|
+
import { activateAccount, removeAccountFromTui } from "./actions";
|
|
5
|
+
import { createTuiBalancerBarSync } from "./balancer-bar-sync";
|
|
6
|
+
import { openNativeConnect } from "./connect";
|
|
7
|
+
import { createBalancerTuiState } from "./state";
|
|
8
|
+
import { createUsageAutoRefresh } from "./usage-auto-refresh";
|
|
9
|
+
function inferProviderID(session) {
|
|
10
|
+
const providerID = session?.model?.providerID;
|
|
11
|
+
return typeof providerID === "string" && providerID.length > 0 ? providerID : undefined;
|
|
12
|
+
}
|
|
13
|
+
function copyRoute(route) {
|
|
14
|
+
return "params" in route && route.params
|
|
15
|
+
? { name: route.name, params: { ...route.params } }
|
|
16
|
+
: { name: route.name };
|
|
17
|
+
}
|
|
18
|
+
const tui = async (api) => {
|
|
19
|
+
await import("@opentui/solid/runtime-plugin" + "-support");
|
|
20
|
+
const [dashboardModule, priorityScreenModule, providerModelDialogModule, renameDialogModule, sidebarModule, statusIndicatorModule] = await Promise.all([
|
|
21
|
+
import("./components/dashboard" + ".tsx"),
|
|
22
|
+
import("./components/priority-screen" + ".tsx"),
|
|
23
|
+
import("./components/provider-model-dialog" + ".tsx"),
|
|
24
|
+
import("./components/rename-dialog" + ".tsx"),
|
|
25
|
+
import("./components/sidebar" + ".tsx"),
|
|
26
|
+
import("./components/status-indicator" + ".tsx"),
|
|
27
|
+
]);
|
|
28
|
+
const state = createBalancerTuiState();
|
|
29
|
+
const usageAutoRefresh = createUsageAutoRefresh(api, state);
|
|
30
|
+
const balancerBarSync = createTuiBalancerBarSync(api, state);
|
|
31
|
+
let dashboardReturnRoute;
|
|
32
|
+
api.lifecycle.onDispose(() => {
|
|
33
|
+
usageAutoRefresh.dispose();
|
|
34
|
+
state.dispose();
|
|
35
|
+
});
|
|
36
|
+
const openDashboard = () => {
|
|
37
|
+
if (api.route.current.name !== "balancer.dashboard")
|
|
38
|
+
dashboardReturnRoute = copyRoute(api.route.current);
|
|
39
|
+
api.route.navigate("balancer.dashboard");
|
|
40
|
+
};
|
|
41
|
+
const openPriority = () => {
|
|
42
|
+
api.route.navigate("balancer.priority");
|
|
43
|
+
};
|
|
44
|
+
const backFromDashboard = () => {
|
|
45
|
+
const route = dashboardReturnRoute;
|
|
46
|
+
dashboardReturnRoute = undefined;
|
|
47
|
+
if (route)
|
|
48
|
+
api.route.navigate(route.name, "params" in route ? route.params : undefined);
|
|
49
|
+
else
|
|
50
|
+
api.route.navigate("home");
|
|
51
|
+
};
|
|
52
|
+
const unregisterDashboard = api.route.register([
|
|
53
|
+
{
|
|
54
|
+
name: "balancer.dashboard",
|
|
55
|
+
render: () => createComponent(dashboardModule.Dashboard, {
|
|
56
|
+
api,
|
|
57
|
+
state,
|
|
58
|
+
onBack: backFromDashboard,
|
|
59
|
+
openPriority,
|
|
60
|
+
openConnect: () => openNativeConnect({ ...api, db: state.db }),
|
|
61
|
+
renameAccount: (providerID, alias) => renameDialogModule.openRenameDialog(api, state, providerID, alias),
|
|
62
|
+
removeAccount: (providerID, alias) => removeAccountFromTui(api, state, providerID, alias),
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "balancer.priority",
|
|
67
|
+
render: () => createComponent(priorityScreenModule.PriorityScreen, {
|
|
68
|
+
api,
|
|
69
|
+
state,
|
|
70
|
+
onBack: () => api.route.navigate("balancer.dashboard"),
|
|
71
|
+
openModelPicker: (providerID) => providerModelDialogModule.openProviderModelDialog(api, state, providerID, {
|
|
72
|
+
onSelected: (model) => setProviderModel(state.db, model.providerID, model.modelID),
|
|
73
|
+
}),
|
|
74
|
+
}),
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
api.lifecycle.onDispose(unregisterDashboard);
|
|
78
|
+
const unregisterKeymap = api.keymap.registerLayer({
|
|
79
|
+
commands: [
|
|
80
|
+
{
|
|
81
|
+
name: "balancer.dashboard.open",
|
|
82
|
+
title: "Open Balancer Dashboard",
|
|
83
|
+
category: "Plugin",
|
|
84
|
+
namespace: "palette",
|
|
85
|
+
slashName: "balancer",
|
|
86
|
+
run() {
|
|
87
|
+
openDashboard();
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
bindings: [{ key: "ctrl+b", cmd: "balancer.dashboard.open" }],
|
|
92
|
+
});
|
|
93
|
+
api.lifecycle.onDispose(unregisterKeymap);
|
|
94
|
+
api.slots.register({
|
|
95
|
+
slots: {
|
|
96
|
+
session_prompt_right(_ctx, value) {
|
|
97
|
+
void usageAutoRefresh.refreshForPrompt();
|
|
98
|
+
void balancerBarSync.maybeSync();
|
|
99
|
+
return createComponent(statusIndicatorModule.BalancerStatusIndicator, {
|
|
100
|
+
api,
|
|
101
|
+
state,
|
|
102
|
+
providerID: () => inferProviderID(api.state.session.get(value.session_id)),
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
sidebar_content(_ctx, value) {
|
|
106
|
+
return createComponent(sidebarModule.BalancerSidebar, {
|
|
107
|
+
api,
|
|
108
|
+
state,
|
|
109
|
+
openDashboard,
|
|
110
|
+
activateAccount: (providerID, alias) => activateAccount(api, state, providerID, alias, {
|
|
111
|
+
sessionProviderID: inferProviderID(api.state.session.get(value.session_id)),
|
|
112
|
+
openProviderModelPicker: (targetProviderID) => providerModelDialogModule.openProviderModelDialog(api, state, targetProviderID),
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
export default { id: "opencode-balancer", tui };
|
|
120
|
+
//# sourceMappingURL=tui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../../src/tui/tui.tsx"],"names":[],"mappings":"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"}
|
package/dist/tui/tui.tsx
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
import type { TuiPlugin, TuiPluginModule, TuiRouteCurrent } from "@opencode-ai/plugin/tui";
|
|
4
|
+
import { createComponent } from "solid-js";
|
|
5
|
+
import { setProviderModel } from "../core/priority";
|
|
6
|
+
import { activateAccount, removeAccountFromTui } from "./actions";
|
|
7
|
+
import { createTuiBalancerBarSync } from "./balancer-bar-sync";
|
|
8
|
+
import { openNativeConnect } from "./connect";
|
|
9
|
+
import { createBalancerTuiState } from "./state";
|
|
10
|
+
import { createUsageAutoRefresh } from "./usage-auto-refresh";
|
|
11
|
+
|
|
12
|
+
type DashboardModule = typeof import("./components/dashboard");
|
|
13
|
+
type PriorityScreenModule = typeof import("./components/priority-screen");
|
|
14
|
+
type ProviderModelDialogModule = typeof import("./components/provider-model-dialog");
|
|
15
|
+
type RenameDialogModule = typeof import("./components/rename-dialog");
|
|
16
|
+
type SidebarModule = typeof import("./components/sidebar");
|
|
17
|
+
type StatusIndicatorModule = typeof import("./components/status-indicator");
|
|
18
|
+
|
|
19
|
+
function inferProviderID(session: unknown) {
|
|
20
|
+
const providerID = (session as { model?: { providerID?: unknown } } | undefined)?.model?.providerID;
|
|
21
|
+
return typeof providerID === "string" && providerID.length > 0 ? providerID : undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function copyRoute(route: TuiRouteCurrent): TuiRouteCurrent {
|
|
25
|
+
return "params" in route && route.params
|
|
26
|
+
? { name: route.name, params: { ...route.params } }
|
|
27
|
+
: { name: route.name };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const tui: TuiPlugin = async (api) => {
|
|
31
|
+
await import("@opentui/solid/runtime-plugin" + "-support");
|
|
32
|
+
|
|
33
|
+
const [dashboardModule, priorityScreenModule, providerModelDialogModule, renameDialogModule, sidebarModule, statusIndicatorModule] = await Promise.all([
|
|
34
|
+
import("./components/dashboard" + ".tsx") as Promise<DashboardModule>,
|
|
35
|
+
import("./components/priority-screen" + ".tsx") as Promise<PriorityScreenModule>,
|
|
36
|
+
import("./components/provider-model-dialog" + ".tsx") as Promise<ProviderModelDialogModule>,
|
|
37
|
+
import("./components/rename-dialog" + ".tsx") as Promise<RenameDialogModule>,
|
|
38
|
+
import("./components/sidebar" + ".tsx") as Promise<SidebarModule>,
|
|
39
|
+
import("./components/status-indicator" + ".tsx") as Promise<StatusIndicatorModule>,
|
|
40
|
+
]);
|
|
41
|
+
const state = createBalancerTuiState();
|
|
42
|
+
const usageAutoRefresh = createUsageAutoRefresh(api, state);
|
|
43
|
+
const balancerBarSync = createTuiBalancerBarSync(api, state);
|
|
44
|
+
let dashboardReturnRoute: TuiRouteCurrent | undefined;
|
|
45
|
+
|
|
46
|
+
api.lifecycle.onDispose(() => {
|
|
47
|
+
usageAutoRefresh.dispose();
|
|
48
|
+
state.dispose();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const openDashboard = () => {
|
|
52
|
+
if (api.route.current.name !== "balancer.dashboard") dashboardReturnRoute = copyRoute(api.route.current);
|
|
53
|
+
api.route.navigate("balancer.dashboard");
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const openPriority = () => {
|
|
57
|
+
api.route.navigate("balancer.priority");
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const backFromDashboard = () => {
|
|
61
|
+
const route = dashboardReturnRoute;
|
|
62
|
+
dashboardReturnRoute = undefined;
|
|
63
|
+
if (route) api.route.navigate(route.name, "params" in route ? route.params : undefined);
|
|
64
|
+
else api.route.navigate("home");
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const unregisterDashboard = api.route.register([
|
|
68
|
+
{
|
|
69
|
+
name: "balancer.dashboard",
|
|
70
|
+
render: () =>
|
|
71
|
+
createComponent(dashboardModule.Dashboard, {
|
|
72
|
+
api,
|
|
73
|
+
state,
|
|
74
|
+
onBack: backFromDashboard,
|
|
75
|
+
openPriority,
|
|
76
|
+
openConnect: () => openNativeConnect({ ...api, db: state.db }),
|
|
77
|
+
renameAccount: (providerID, alias) => renameDialogModule.openRenameDialog(api, state, providerID, alias),
|
|
78
|
+
removeAccount: (providerID, alias) => removeAccountFromTui(api, state, providerID, alias),
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "balancer.priority",
|
|
83
|
+
render: () =>
|
|
84
|
+
createComponent(priorityScreenModule.PriorityScreen, {
|
|
85
|
+
api,
|
|
86
|
+
state,
|
|
87
|
+
onBack: () => api.route.navigate("balancer.dashboard"),
|
|
88
|
+
openModelPicker: (providerID) =>
|
|
89
|
+
providerModelDialogModule.openProviderModelDialog(api, state, providerID, {
|
|
90
|
+
onSelected: (model) => setProviderModel(state.db, model.providerID, model.modelID),
|
|
91
|
+
}),
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
api.lifecycle.onDispose(unregisterDashboard);
|
|
96
|
+
|
|
97
|
+
const unregisterKeymap = api.keymap.registerLayer({
|
|
98
|
+
commands: [
|
|
99
|
+
{
|
|
100
|
+
name: "balancer.dashboard.open",
|
|
101
|
+
title: "Open Balancer Dashboard",
|
|
102
|
+
category: "Plugin",
|
|
103
|
+
namespace: "palette",
|
|
104
|
+
slashName: "balancer",
|
|
105
|
+
run() {
|
|
106
|
+
openDashboard();
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
bindings: [{ key: "ctrl+b", cmd: "balancer.dashboard.open" }],
|
|
111
|
+
});
|
|
112
|
+
api.lifecycle.onDispose(unregisterKeymap);
|
|
113
|
+
|
|
114
|
+
api.slots.register({
|
|
115
|
+
slots: {
|
|
116
|
+
session_prompt_right(_ctx, value) {
|
|
117
|
+
void usageAutoRefresh.refreshForPrompt();
|
|
118
|
+
void balancerBarSync.maybeSync();
|
|
119
|
+
return createComponent(statusIndicatorModule.BalancerStatusIndicator, {
|
|
120
|
+
api,
|
|
121
|
+
state,
|
|
122
|
+
providerID: () => inferProviderID(api.state.session.get(value.session_id)),
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
sidebar_content(_ctx, value) {
|
|
126
|
+
return createComponent(sidebarModule.BalancerSidebar, {
|
|
127
|
+
api,
|
|
128
|
+
state,
|
|
129
|
+
openDashboard,
|
|
130
|
+
activateAccount: (providerID, alias) =>
|
|
131
|
+
activateAccount(api, state, providerID, alias, {
|
|
132
|
+
sessionProviderID: inferProviderID(api.state.session.get(value.session_id)),
|
|
133
|
+
openProviderModelPicker: (targetProviderID) =>
|
|
134
|
+
providerModelDialogModule.openProviderModelDialog(api, state, targetProviderID),
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export default { id: "opencode-balancer", tui } satisfies TuiPluginModule;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { refreshAccountUsage } from "../core/usage";
|
|
2
|
+
import { refreshUsageForAccount } from "./actions";
|
|
3
|
+
import type { BalancerTuiState } from "./state";
|
|
4
|
+
type ToastApi = Parameters<typeof refreshUsageForAccount>[0];
|
|
5
|
+
type UsageAutoRefreshOptions = {
|
|
6
|
+
intervalMs?: number;
|
|
7
|
+
promptDebounceMs?: number;
|
|
8
|
+
refreshUsage?: typeof refreshAccountUsage;
|
|
9
|
+
now?: () => number;
|
|
10
|
+
};
|
|
11
|
+
export declare function createUsageAutoRefresh(api: ToastApi, state: BalancerTuiState, options?: UsageAutoRefreshOptions): {
|
|
12
|
+
refreshNow: () => Promise<void>;
|
|
13
|
+
refreshForPrompt: () => Promise<void>;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
};
|
|
16
|
+
export {};
|