@thelioo/opencode-balancer 0.1.8 → 0.2.1
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 +53 -25
- package/README.md +95 -51
- package/dist/core/accounts.ts +404 -0
- package/dist/core/database.ts +67 -0
- package/dist/core/events.ts +75 -0
- package/dist/core/native-auth-suppression.ts +36 -0
- package/dist/core/native-connect.ts +31 -0
- package/dist/core/path.ts +34 -0
- package/dist/core/pending.ts +351 -0
- package/dist/core/priority.ts +193 -0
- package/dist/core/schema.ts +439 -0
- package/dist/core/time.ts +3 -0
- package/dist/core/types.ts +72 -0
- package/dist/core/usage/index.ts +23 -0
- package/dist/core/usage/providers/copilot.ts +243 -0
- package/dist/core/usage/providers/openai.ts +179 -0
- package/dist/core/usage/redact.ts +80 -0
- package/dist/core/usage/store.ts +66 -0
- package/dist/core/usage/types.ts +24 -0
- package/dist/index.js +173 -4
- package/dist/index.js.map +1 -1
- package/dist/server/auth-watcher.ts +318 -0
- package/dist/server/commands.ts +58 -0
- package/dist/server/fetch-patch.ts +162 -0
- package/dist/server/index.ts +134 -0
- package/dist/server/native.ts +49 -0
- package/dist/server/request-balancer.ts +67 -0
- package/dist/tui/actions.ts +176 -112
- package/dist/tui/balancer-bar-sync.ts +55 -45
- package/dist/tui/components/alias-dialog.tsx +71 -56
- package/dist/tui/components/dashboard.tsx +530 -358
- package/dist/tui/components/priority-screen.tsx +389 -267
- package/dist/tui/components/provider-model-dialog.tsx +71 -64
- package/dist/tui/components/rename-dialog.tsx +35 -28
- package/dist/tui/components/sidebar.tsx +103 -79
- package/dist/tui/components/status-indicator.tsx +78 -59
- package/dist/tui/components/usage-bar.tsx +18 -7
- package/dist/tui/components/usage-display.tsx +32 -16
- package/dist/tui/connect.ts +104 -73
- package/dist/tui/dashboard-keys.ts +53 -41
- package/dist/tui/native-model-apply.ts +45 -36
- package/dist/tui/priority-keys.ts +44 -36
- package/dist/tui/provider-models.ts +32 -25
- package/dist/tui/responsive.ts +10 -7
- package/dist/tui/selected-account-bar-sync.ts +23 -23
- package/dist/tui/selection-colors.ts +38 -30
- package/dist/tui/state.ts +61 -44
- package/dist/tui/status-format.ts +24 -20
- package/dist/tui/tui.js +165 -153
- package/dist/tui/tui.js.map +1 -1
- package/dist/tui/tui.tsx +194 -144
- package/dist/tui/usage-auto-refresh.ts +52 -45
- package/dist/tui/usage-format.ts +9 -9
- package/package.json +61 -52
- package/dist/core/accounts.d.ts +0 -14
- package/dist/core/accounts.js +0 -260
- package/dist/core/accounts.js.map +0 -1
- package/dist/core/database.d.ts +0 -4
- package/dist/core/database.js +0 -69
- package/dist/core/database.js.map +0 -1
- package/dist/core/events.d.ts +0 -18
- package/dist/core/events.js +0 -39
- package/dist/core/events.js.map +0 -1
- package/dist/core/native-auth-suppression.d.ts +0 -3
- package/dist/core/native-auth-suppression.js +0 -19
- package/dist/core/native-auth-suppression.js.map +0 -1
- package/dist/core/native-connect.d.ts +0 -4
- package/dist/core/native-connect.js +0 -19
- package/dist/core/native-connect.js.map +0 -1
- package/dist/core/path.d.ts +0 -4
- package/dist/core/path.js +0 -26
- package/dist/core/path.js.map +0 -1
- package/dist/core/pending.d.ts +0 -9
- package/dist/core/pending.js +0 -237
- package/dist/core/pending.js.map +0 -1
- package/dist/core/priority.d.ts +0 -20
- package/dist/core/priority.js +0 -120
- package/dist/core/priority.js.map +0 -1
- package/dist/core/schema.d.ts +0 -2
- package/dist/core/schema.js +0 -265
- package/dist/core/schema.js.map +0 -1
- package/dist/core/time.d.ts +0 -1
- package/dist/core/time.js +0 -4
- package/dist/core/time.js.map +0 -1
- package/dist/core/types.d.ts +0 -59
- package/dist/core/types.js +0 -2
- package/dist/core/types.js.map +0 -1
- package/dist/core/usage/index.d.ts +0 -4
- package/dist/core/usage/index.js +0 -16
- package/dist/core/usage/index.js.map +0 -1
- package/dist/core/usage/providers/copilot.d.ts +0 -2
- package/dist/core/usage/providers/copilot.js +0 -169
- package/dist/core/usage/providers/copilot.js.map +0 -1
- package/dist/core/usage/providers/openai.d.ts +0 -2
- package/dist/core/usage/providers/openai.js +0 -133
- package/dist/core/usage/providers/openai.js.map +0 -1
- package/dist/core/usage/redact.d.ts +0 -3
- package/dist/core/usage/redact.js +0 -67
- package/dist/core/usage/redact.js.map +0 -1
- package/dist/core/usage/store.d.ts +0 -4
- package/dist/core/usage/store.js +0 -31
- package/dist/core/usage/store.js.map +0 -1
- package/dist/core/usage/types.d.ts +0 -21
- package/dist/core/usage/types.js +0 -2
- package/dist/core/usage/types.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/server/auth-watcher.d.ts +0 -32
- package/dist/server/auth-watcher.js +0 -227
- package/dist/server/auth-watcher.js.map +0 -1
- package/dist/server/commands.d.ts +0 -2
- package/dist/server/commands.js +0 -46
- package/dist/server/commands.js.map +0 -1
- package/dist/server/fetch-patch.d.ts +0 -3
- package/dist/server/fetch-patch.js +0 -118
- package/dist/server/fetch-patch.js.map +0 -1
- package/dist/server/index.d.ts +0 -8
- package/dist/server/index.js +0 -94
- package/dist/server/index.js.map +0 -1
- package/dist/server/native.d.ts +0 -6
- package/dist/server/native.js +0 -35
- package/dist/server/native.js.map +0 -1
- package/dist/server/request-balancer.d.ts +0 -16
- package/dist/server/request-balancer.js +0 -43
- package/dist/server/request-balancer.js.map +0 -1
- package/dist/tui/actions.d.ts +0 -41
- package/dist/tui/actions.js +0 -92
- package/dist/tui/actions.js.map +0 -1
- package/dist/tui/balancer-bar-sync.d.ts +0 -19
- package/dist/tui/balancer-bar-sync.js +0 -45
- package/dist/tui/balancer-bar-sync.js.map +0 -1
- package/dist/tui/components/alias-dialog.d.ts +0 -4
- package/dist/tui/components/dashboard.d.ts +0 -12
- package/dist/tui/components/priority-screen.d.ts +0 -9
- package/dist/tui/components/provider-model-dialog.d.ts +0 -14
- package/dist/tui/components/rename-dialog.d.ts +0 -4
- package/dist/tui/components/sidebar.d.ts +0 -10
- package/dist/tui/components/status-indicator.d.ts +0 -9
- package/dist/tui/components/usage-bar.d.ts +0 -8
- package/dist/tui/components/usage-display.d.ts +0 -10
- package/dist/tui/connect.d.ts +0 -30
- package/dist/tui/connect.js +0 -75
- package/dist/tui/connect.js.map +0 -1
- package/dist/tui/dashboard-keys.d.ts +0 -45
- package/dist/tui/dashboard-keys.js +0 -44
- package/dist/tui/dashboard-keys.js.map +0 -1
- package/dist/tui/native-model-apply.d.ts +0 -21
- package/dist/tui/native-model-apply.js +0 -53
- package/dist/tui/native-model-apply.js.map +0 -1
- package/dist/tui/priority-keys.d.ts +0 -40
- package/dist/tui/priority-keys.js +0 -38
- package/dist/tui/priority-keys.js.map +0 -1
- package/dist/tui/provider-models.d.ts +0 -19
- package/dist/tui/provider-models.js +0 -17
- package/dist/tui/provider-models.js.map +0 -1
- package/dist/tui/responsive.d.ts +0 -9
- package/dist/tui/responsive.js +0 -13
- package/dist/tui/responsive.js.map +0 -1
- package/dist/tui/selected-account-bar-sync.d.ts +0 -10
- package/dist/tui/selected-account-bar-sync.js +0 -26
- package/dist/tui/selected-account-bar-sync.js.map +0 -1
- package/dist/tui/selection-colors.d.ts +0 -10
- package/dist/tui/selection-colors.js +0 -38
- package/dist/tui/selection-colors.js.map +0 -1
- package/dist/tui/state.d.ts +0 -14
- package/dist/tui/state.js +0 -46
- package/dist/tui/state.js.map +0 -1
- package/dist/tui/status-format.d.ts +0 -15
- package/dist/tui/status-format.js +0 -17
- package/dist/tui/status-format.js.map +0 -1
- package/dist/tui/tui.d.ts +0 -7
- package/dist/tui/usage-auto-refresh.d.ts +0 -16
- package/dist/tui/usage-auto-refresh.js +0 -46
- package/dist/tui/usage-auto-refresh.js.map +0 -1
- package/dist/tui/usage-format.d.ts +0 -2
- package/dist/tui/usage-format.js +0 -17
- package/dist/tui/usage-format.js.map +0 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { suppressNativeAuthCapture as suppressDbNativeAuthCapture } from "../core/native-auth-suppression";
|
|
3
|
+
import { isNativeConnectInProgress } from "../core/native-connect";
|
|
4
|
+
import { now } from "../core/time";
|
|
5
|
+
import type { AuthInfo } from "../core/types";
|
|
6
|
+
|
|
7
|
+
const suppressAuthCaptureUntil = new Map<string, number>();
|
|
8
|
+
|
|
9
|
+
export function suppressNativeAuthCapture(
|
|
10
|
+
providerID: string,
|
|
11
|
+
durationMs = 2_000,
|
|
12
|
+
) {
|
|
13
|
+
suppressAuthCaptureUntil.set(providerID, now() + durationMs);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isNativeAuthCaptureSuppressed(providerID: string) {
|
|
17
|
+
const suppressedUntil = suppressAuthCaptureUntil.get(providerID) ?? 0;
|
|
18
|
+
if (suppressedUntil <= now()) {
|
|
19
|
+
suppressAuthCaptureUntil.delete(providerID);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function setNativeAuth(
|
|
26
|
+
client: any,
|
|
27
|
+
providerID: string,
|
|
28
|
+
auth: AuthInfo,
|
|
29
|
+
db?: Database,
|
|
30
|
+
) {
|
|
31
|
+
if (db && isNativeConnectInProgress(db)) return;
|
|
32
|
+
suppressNativeAuthCapture(providerID);
|
|
33
|
+
if (db) suppressDbNativeAuthCapture(db, providerID);
|
|
34
|
+
try {
|
|
35
|
+
await client.auth.set({ body: auth, path: { id: providerID } });
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function showToast(
|
|
40
|
+
client: any,
|
|
41
|
+
message: string,
|
|
42
|
+
variant: "info" | "success" | "warning" | "error" = "info",
|
|
43
|
+
) {
|
|
44
|
+
try {
|
|
45
|
+
await client.tui.showToast({
|
|
46
|
+
body: { duration: 15_000, message, variant },
|
|
47
|
+
});
|
|
48
|
+
} catch {}
|
|
49
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { getAccount, listAccounts, normalizeAlias } from "../core/accounts";
|
|
3
|
+
import { now } from "../core/time";
|
|
4
|
+
import type { Account } from "../core/types";
|
|
5
|
+
|
|
6
|
+
export const INTERNAL_REQUEST_HEADER = "x-opencode-balancer-request";
|
|
7
|
+
export const BALANCER_METADATA_KEY = "opencodeBalancerCommand";
|
|
8
|
+
|
|
9
|
+
export const RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504, 529]);
|
|
10
|
+
|
|
11
|
+
type PendingRequest = {
|
|
12
|
+
providerID: string;
|
|
13
|
+
account?: Account;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const pendingRequests = new Map<string, PendingRequest>();
|
|
17
|
+
|
|
18
|
+
export function setPendingRequest(requestID: string, request: PendingRequest) {
|
|
19
|
+
pendingRequests.set(requestID, request);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function takePendingRequest(requestID: string) {
|
|
23
|
+
const request = pendingRequests.get(requestID);
|
|
24
|
+
pendingRequests.delete(requestID);
|
|
25
|
+
return request;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function __testGetPendingRequest(requestID: string) {
|
|
29
|
+
return pendingRequests.get(requestID);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function __testClearPendingRequests() {
|
|
33
|
+
pendingRequests.clear();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function markRateLimited(
|
|
37
|
+
db: Database,
|
|
38
|
+
providerID: string,
|
|
39
|
+
alias: string,
|
|
40
|
+
retryAfterMs = 60_000,
|
|
41
|
+
) {
|
|
42
|
+
const account = getAccount(db, providerID, alias);
|
|
43
|
+
if (!account) return;
|
|
44
|
+
|
|
45
|
+
const timestamp = now();
|
|
46
|
+
db.query<unknown, [number, number, string, string]>(
|
|
47
|
+
`UPDATE accounts
|
|
48
|
+
SET failures = failures + 1,
|
|
49
|
+
rate_limited_until = ?,
|
|
50
|
+
updated_at = ?
|
|
51
|
+
WHERE provider_id = ? AND alias = ?`,
|
|
52
|
+
).run(timestamp + retryAfterMs, timestamp, providerID, account.alias);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function chooseFailoverAccount(
|
|
56
|
+
db: Database,
|
|
57
|
+
providerID: string,
|
|
58
|
+
currentAlias: string,
|
|
59
|
+
) {
|
|
60
|
+
const timestamp = now();
|
|
61
|
+
const normalizedCurrentAlias = normalizeAlias(currentAlias);
|
|
62
|
+
return listAccounts(db, providerID).find((account) => {
|
|
63
|
+
if (account.alias === normalizedCurrentAlias) return false;
|
|
64
|
+
if (account.disabled) return false;
|
|
65
|
+
return !account.rateLimitedUntil || account.rateLimitedUntil <= timestamp;
|
|
66
|
+
});
|
|
67
|
+
}
|
package/dist/tui/actions.ts
CHANGED
|
@@ -1,144 +1,208 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getAccount,
|
|
3
|
+
getActiveAccount,
|
|
4
|
+
getSelectedAccount,
|
|
5
|
+
removeAccount,
|
|
6
|
+
renameAccount,
|
|
7
|
+
setActiveAccount,
|
|
8
|
+
} from "../core/accounts";
|
|
2
9
|
import { appendEvent } from "../core/events";
|
|
3
|
-
import {
|
|
10
|
+
import { suppressNativeAuthCapture } from "../core/native-auth-suppression";
|
|
11
|
+
import {
|
|
12
|
+
completePendingConnection,
|
|
13
|
+
removePendingConnection,
|
|
14
|
+
} from "../core/pending";
|
|
4
15
|
import { refreshAccountUsage } from "../core/usage";
|
|
5
16
|
import { saveUsageSnapshot } from "../core/usage/store";
|
|
6
|
-
import { suppressNativeAuthCapture } from "../core/native-auth-suppression";
|
|
7
17
|
import type { BalancerTuiState } from "./state";
|
|
8
18
|
|
|
9
19
|
type AuthSetApi = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
client: {
|
|
21
|
+
auth: {
|
|
22
|
+
set: (input: any) => unknown;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
keymap?: {
|
|
26
|
+
dispatchCommand?: (command: string) => unknown;
|
|
27
|
+
};
|
|
28
|
+
ui?: {
|
|
29
|
+
toast: (input: {
|
|
30
|
+
variant: "success" | "warning" | "error" | "info";
|
|
31
|
+
message: string;
|
|
32
|
+
}) => unknown;
|
|
33
|
+
};
|
|
21
34
|
};
|
|
22
35
|
|
|
23
36
|
type ActivateAccountOptions = {
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
sessionProviderID?: string;
|
|
38
|
+
applyNativeProviderModel?: (providerID: string) => Promise<boolean>;
|
|
26
39
|
};
|
|
27
40
|
|
|
28
41
|
type ToastApi = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
ui: {
|
|
43
|
+
toast: (input: {
|
|
44
|
+
variant: "success" | "warning" | "error" | "info";
|
|
45
|
+
message: string;
|
|
46
|
+
}) => unknown;
|
|
47
|
+
};
|
|
32
48
|
};
|
|
33
49
|
|
|
34
50
|
type RefreshUsageOptions = {
|
|
35
|
-
|
|
36
|
-
|
|
51
|
+
refreshUsage?: typeof refreshAccountUsage;
|
|
52
|
+
silent?: boolean;
|
|
37
53
|
};
|
|
38
54
|
|
|
39
|
-
export function savePendingAlias(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
export function savePendingAlias(
|
|
56
|
+
state: BalancerTuiState,
|
|
57
|
+
pendingID: string,
|
|
58
|
+
alias: string,
|
|
59
|
+
) {
|
|
60
|
+
const account = completePendingConnection(state.db, pendingID, alias);
|
|
61
|
+
state.refresh();
|
|
62
|
+
return account;
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
export async function activateAccount(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
api: AuthSetApi,
|
|
67
|
+
state: BalancerTuiState,
|
|
68
|
+
providerID: string,
|
|
69
|
+
alias: string,
|
|
70
|
+
options: ActivateAccountOptions = {},
|
|
51
71
|
) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
const previousProviderID = getSelectedAccount(state.db)?.providerID;
|
|
73
|
+
const account =
|
|
74
|
+
setActiveAccount(state.db, providerID, alias) ??
|
|
75
|
+
getActiveAccount(state.db, providerID);
|
|
76
|
+
|
|
77
|
+
if (account) {
|
|
78
|
+
try {
|
|
79
|
+
suppressNativeAuthCapture(state.db, providerID);
|
|
80
|
+
await api.client.auth.set({
|
|
81
|
+
body: account.auth,
|
|
82
|
+
path: { id: providerID },
|
|
83
|
+
});
|
|
84
|
+
} catch {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
state.refresh();
|
|
88
|
+
const providerChanged =
|
|
89
|
+
(options.sessionProviderID ?? previousProviderID) !== providerID;
|
|
90
|
+
if (providerChanged) {
|
|
91
|
+
await options.applyNativeProviderModel?.(providerID);
|
|
92
|
+
}
|
|
93
|
+
api.ui?.toast({
|
|
94
|
+
message: `Activated ${providerID}/${alias}.`,
|
|
95
|
+
variant: "success",
|
|
96
|
+
});
|
|
68
97
|
}
|
|
69
98
|
|
|
70
99
|
export async function refreshUsageForAccount(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
100
|
+
api: ToastApi,
|
|
101
|
+
state: BalancerTuiState,
|
|
102
|
+
providerID: string,
|
|
103
|
+
alias: string,
|
|
104
|
+
options: RefreshUsageOptions = {},
|
|
76
105
|
) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
const account =
|
|
107
|
+
getAccount(state.db, providerID, alias) ??
|
|
108
|
+
state.accounts().find((candidate) => {
|
|
109
|
+
return candidate.providerID === providerID && candidate.alias === alias;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!account) {
|
|
113
|
+
api.ui.toast({
|
|
114
|
+
message: `Account not found: ${providerID}/${alias}`,
|
|
115
|
+
variant: "error",
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const snapshot = await (options.refreshUsage ?? refreshAccountUsage)(
|
|
122
|
+
account,
|
|
123
|
+
);
|
|
124
|
+
saveUsageSnapshot(state.db, snapshot);
|
|
125
|
+
appendEvent(state.db, {
|
|
126
|
+
alias,
|
|
127
|
+
message: snapshot.message,
|
|
128
|
+
providerID,
|
|
129
|
+
type:
|
|
130
|
+
snapshot.confidence === "unavailable"
|
|
131
|
+
? "usage_unavailable"
|
|
132
|
+
: "usage_refreshed",
|
|
133
|
+
});
|
|
134
|
+
state.refresh();
|
|
135
|
+
if (!options.silent) {
|
|
136
|
+
api.ui.toast({
|
|
137
|
+
message: snapshot.message,
|
|
138
|
+
variant: snapshot.confidence === "unavailable" ? "warning" : "success",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
const message =
|
|
143
|
+
error instanceof Error ? error.message : "Usage refresh failed.";
|
|
144
|
+
api.ui.toast({ message, variant: "error" });
|
|
145
|
+
}
|
|
106
146
|
}
|
|
107
147
|
|
|
108
|
-
export function removeAccountFromTui(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
148
|
+
export function removeAccountFromTui(
|
|
149
|
+
api: ToastApi,
|
|
150
|
+
state: BalancerTuiState,
|
|
151
|
+
providerID: string,
|
|
152
|
+
alias: string,
|
|
153
|
+
) {
|
|
154
|
+
const removed = removeAccount(state.db, providerID, alias);
|
|
155
|
+
if (!removed) {
|
|
156
|
+
api.ui.toast({
|
|
157
|
+
message: `Account not found: ${providerID}/${alias}`,
|
|
158
|
+
variant: "error",
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const message = `Removed account ${providerID}/${alias}.`;
|
|
164
|
+
appendEvent(state.db, {
|
|
165
|
+
alias,
|
|
166
|
+
message,
|
|
167
|
+
providerID,
|
|
168
|
+
type: "account_removed",
|
|
169
|
+
});
|
|
170
|
+
state.refresh();
|
|
171
|
+
state.removeAccountView(providerID, alias);
|
|
172
|
+
api.ui.toast({ message, variant: "success" });
|
|
125
173
|
}
|
|
126
174
|
|
|
127
|
-
export function renameAccountFromTui(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
175
|
+
export function renameAccountFromTui(
|
|
176
|
+
api: ToastApi,
|
|
177
|
+
state: BalancerTuiState,
|
|
178
|
+
providerID: string,
|
|
179
|
+
alias: string,
|
|
180
|
+
nextAlias: string,
|
|
181
|
+
) {
|
|
182
|
+
const account = renameAccount(state.db, providerID, alias, nextAlias);
|
|
183
|
+
state.refresh();
|
|
184
|
+
api.ui.toast({
|
|
185
|
+
message: `Renamed ${providerID}/${alias} to ${account.alias}.`,
|
|
186
|
+
variant: "success",
|
|
187
|
+
});
|
|
188
|
+
return account;
|
|
132
189
|
}
|
|
133
190
|
|
|
134
|
-
export function removePendingFromTui(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
191
|
+
export function removePendingFromTui(
|
|
192
|
+
api: ToastApi,
|
|
193
|
+
state: BalancerTuiState,
|
|
194
|
+
pendingID: string,
|
|
195
|
+
) {
|
|
196
|
+
const removed = removePendingConnection(state.db, pendingID);
|
|
197
|
+
if (!removed) {
|
|
198
|
+
api.ui.toast({
|
|
199
|
+
message: "Pending connection not found.",
|
|
200
|
+
variant: "error",
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
state.refresh();
|
|
206
|
+
state.removePendingView(pendingID);
|
|
207
|
+
api.ui.toast({ message: "Removed pending connection.", variant: "success" });
|
|
144
208
|
}
|
|
@@ -1,56 +1,66 @@
|
|
|
1
1
|
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
2
2
|
import { getBalancingEnabled, resolveActiveSelection } from "../core/priority";
|
|
3
|
+
import {
|
|
4
|
+
createNativeModelApplier,
|
|
5
|
+
type NativeModelApplier,
|
|
6
|
+
} from "./native-model-apply";
|
|
3
7
|
import type { BalancerTuiState } from "./state";
|
|
4
|
-
import { createNativeModelApplier, type NativeModelApplier } from "./native-model-apply";
|
|
5
8
|
|
|
6
9
|
export type BalancerBarSyncDeps = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
balancingEnabled: () => boolean;
|
|
11
|
+
dialogOpen: () => boolean;
|
|
12
|
+
activeSelection: () => { providerID: string; modelID: string } | undefined;
|
|
13
|
+
modelTitle: (providerID: string, modelID: string) => string | undefined;
|
|
14
|
+
apply: NativeModelApplier;
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
export function createBalancerBarSync(deps: BalancerBarSyncDeps) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
18
|
+
let lastApplied: string | undefined;
|
|
19
|
+
let applying = false;
|
|
20
|
+
|
|
21
|
+
const maybeSync = async () => {
|
|
22
|
+
if (!deps.balancingEnabled()) {
|
|
23
|
+
lastApplied = undefined;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (applying || deps.dialogOpen()) return false;
|
|
27
|
+
|
|
28
|
+
const selection = deps.activeSelection();
|
|
29
|
+
if (!selection) return false;
|
|
30
|
+
|
|
31
|
+
const key = `${selection.providerID}/${selection.modelID}`;
|
|
32
|
+
if (key === lastApplied) return false;
|
|
33
|
+
|
|
34
|
+
const title =
|
|
35
|
+
deps.modelTitle(selection.providerID, selection.modelID) ??
|
|
36
|
+
selection.modelID;
|
|
37
|
+
applying = true;
|
|
38
|
+
try {
|
|
39
|
+
const applied = await deps.apply(selection, title);
|
|
40
|
+
if (applied) lastApplied = key;
|
|
41
|
+
return applied;
|
|
42
|
+
} finally {
|
|
43
|
+
applying = false;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return { maybeSync };
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
export function createTuiBalancerBarSync(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
export function createTuiBalancerBarSync(
|
|
51
|
+
api: TuiPluginApi,
|
|
52
|
+
state: BalancerTuiState,
|
|
53
|
+
) {
|
|
54
|
+
return createBalancerBarSync({
|
|
55
|
+
activeSelection: () => resolveActiveSelection(state.db),
|
|
56
|
+
apply: createNativeModelApplier(api),
|
|
57
|
+
balancingEnabled: () => getBalancingEnabled(state.db),
|
|
58
|
+
dialogOpen: () => api.ui.dialog.open,
|
|
59
|
+
modelTitle: (providerID, modelID) => {
|
|
60
|
+
const provider = api.state.provider.find(
|
|
61
|
+
(item) => item.id === providerID,
|
|
62
|
+
);
|
|
63
|
+
return provider?.models?.[modelID]?.name;
|
|
64
|
+
},
|
|
65
|
+
});
|
|
56
66
|
}
|