@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
package/dist/tui/tui.tsx
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/** @jsxImportSource @opentui/solid */
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
TuiPlugin,
|
|
5
|
+
TuiPluginModule,
|
|
6
|
+
TuiRouteCurrent,
|
|
7
|
+
} from "@opencode-ai/plugin/tui";
|
|
4
8
|
import { createComponent } from "solid-js";
|
|
5
9
|
import { getSelectedAccount, getSelectedModel } from "../core/accounts";
|
|
6
10
|
import { getBalancingEnabled, setProviderModel } from "../core/priority";
|
|
@@ -15,160 +19,206 @@ import { createUsageAutoRefresh } from "./usage-auto-refresh";
|
|
|
15
19
|
|
|
16
20
|
type DashboardModule = typeof import("./components/dashboard");
|
|
17
21
|
type PriorityScreenModule = typeof import("./components/priority-screen");
|
|
18
|
-
type ProviderModelDialogModule =
|
|
22
|
+
type ProviderModelDialogModule =
|
|
23
|
+
typeof import("./components/provider-model-dialog");
|
|
19
24
|
type RenameDialogModule = typeof import("./components/rename-dialog");
|
|
20
25
|
type SidebarModule = typeof import("./components/sidebar");
|
|
21
26
|
type StatusIndicatorModule = typeof import("./components/status-indicator");
|
|
22
27
|
|
|
23
28
|
function inferProviderID(session: unknown) {
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
const providerID = (
|
|
30
|
+
session as { model?: { providerID?: unknown } } | undefined
|
|
31
|
+
)?.model?.providerID;
|
|
32
|
+
return typeof providerID === "string" && providerID.length > 0
|
|
33
|
+
? providerID
|
|
34
|
+
: undefined;
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
function copyRoute(route: TuiRouteCurrent): TuiRouteCurrent {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
return "params" in route && route.params
|
|
39
|
+
? { name: route.name, params: { ...route.params } }
|
|
40
|
+
: { name: route.name };
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
const tui: TuiPlugin = async (api) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
44
|
+
await import("@opentui/solid/runtime-plugin" + "-support");
|
|
45
|
+
|
|
46
|
+
const [
|
|
47
|
+
dashboardModule,
|
|
48
|
+
priorityScreenModule,
|
|
49
|
+
providerModelDialogModule,
|
|
50
|
+
renameDialogModule,
|
|
51
|
+
sidebarModule,
|
|
52
|
+
statusIndicatorModule,
|
|
53
|
+
] = await Promise.all([
|
|
54
|
+
import("./components/dashboard" + ".tsx") as Promise<DashboardModule>,
|
|
55
|
+
import(
|
|
56
|
+
"./components/priority-screen" + ".tsx"
|
|
57
|
+
) as Promise<PriorityScreenModule>,
|
|
58
|
+
import(
|
|
59
|
+
"./components/provider-model-dialog" + ".tsx"
|
|
60
|
+
) as Promise<ProviderModelDialogModule>,
|
|
61
|
+
import(
|
|
62
|
+
"./components/rename-dialog" + ".tsx"
|
|
63
|
+
) as Promise<RenameDialogModule>,
|
|
64
|
+
import("./components/sidebar" + ".tsx") as Promise<SidebarModule>,
|
|
65
|
+
import(
|
|
66
|
+
"./components/status-indicator" + ".tsx"
|
|
67
|
+
) as Promise<StatusIndicatorModule>,
|
|
68
|
+
]);
|
|
69
|
+
const state = createBalancerTuiState();
|
|
70
|
+
const usageAutoRefresh = createUsageAutoRefresh(api, state);
|
|
71
|
+
const balancerBarSync = createTuiBalancerBarSync(api, state);
|
|
72
|
+
const nativeModelApplier = createNativeModelApplier(api);
|
|
73
|
+
let dashboardReturnRoute: TuiRouteCurrent | undefined;
|
|
74
|
+
let nativeProviderID: string | undefined;
|
|
75
|
+
let sessionProviderID: string | undefined;
|
|
76
|
+
|
|
77
|
+
const applyNativeProviderModel = async (providerID: string) => {
|
|
78
|
+
const modelOptions = providerModelOptions(api.state.provider, providerID);
|
|
79
|
+
const selected = getSelectedModel(state.db, providerID);
|
|
80
|
+
const option =
|
|
81
|
+
modelOptions.find((item) => item.modelID === selected?.modelID) ??
|
|
82
|
+
modelOptions[0];
|
|
83
|
+
if (!option) return false;
|
|
84
|
+
return nativeModelApplier(
|
|
85
|
+
{ modelID: option.modelID, providerID: option.providerID },
|
|
86
|
+
option.title,
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const applyNativeProviderModelAndTrack = async (providerID: string) => {
|
|
91
|
+
const applied = await applyNativeProviderModel(providerID);
|
|
92
|
+
if (applied) nativeProviderID = providerID;
|
|
93
|
+
return applied;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const selectedAccountBarSync = createSelectedAccountBarSync({
|
|
97
|
+
applyProvider: applyNativeProviderModelAndTrack,
|
|
98
|
+
currentProvider: () => nativeProviderID ?? sessionProviderID,
|
|
99
|
+
dialogOpen: () => api.ui.dialog.open,
|
|
100
|
+
selectedProvider: () =>
|
|
101
|
+
getBalancingEnabled(state.db)
|
|
102
|
+
? undefined
|
|
103
|
+
: getSelectedAccount(state.db)?.providerID,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
api.lifecycle.onDispose(() => {
|
|
107
|
+
usageAutoRefresh.dispose();
|
|
108
|
+
state.dispose();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const openDashboard = () => {
|
|
112
|
+
if (api.route.current.name !== "balancer.dashboard")
|
|
113
|
+
dashboardReturnRoute = copyRoute(api.route.current);
|
|
114
|
+
api.route.navigate("balancer.dashboard");
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const openPriority = () => {
|
|
118
|
+
api.route.navigate("balancer.priority");
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const backFromDashboard = () => {
|
|
122
|
+
const route = dashboardReturnRoute;
|
|
123
|
+
dashboardReturnRoute = undefined;
|
|
124
|
+
if (route)
|
|
125
|
+
api.route.navigate(
|
|
126
|
+
route.name,
|
|
127
|
+
"params" in route ? route.params : undefined,
|
|
128
|
+
);
|
|
129
|
+
else api.route.navigate("home");
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const unregisterDashboard = api.route.register([
|
|
133
|
+
{
|
|
134
|
+
name: "balancer.dashboard",
|
|
135
|
+
render: () =>
|
|
136
|
+
createComponent(dashboardModule.Dashboard, {
|
|
137
|
+
api,
|
|
138
|
+
onBack: backFromDashboard,
|
|
139
|
+
openConnect: () => openNativeConnect({ ...api, db: state.db }),
|
|
140
|
+
openPriority,
|
|
141
|
+
removeAccount: (providerID, alias) =>
|
|
142
|
+
removeAccountFromTui(api, state, providerID, alias),
|
|
143
|
+
renameAccount: (providerID, alias) =>
|
|
144
|
+
renameDialogModule.openRenameDialog(api, state, providerID, alias),
|
|
145
|
+
state,
|
|
146
|
+
}),
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "balancer.priority",
|
|
150
|
+
render: () =>
|
|
151
|
+
createComponent(priorityScreenModule.PriorityScreen, {
|
|
152
|
+
api,
|
|
153
|
+
onBack: () => api.route.navigate("balancer.dashboard"),
|
|
154
|
+
openModelPicker: (providerID, onComplete) =>
|
|
155
|
+
providerModelDialogModule.openProviderModelDialog(
|
|
156
|
+
api,
|
|
157
|
+
state,
|
|
158
|
+
providerID,
|
|
159
|
+
{
|
|
160
|
+
applyNativeSelection: false,
|
|
161
|
+
onComplete,
|
|
162
|
+
onSelected: (model) =>
|
|
163
|
+
setProviderModel(state.db, model.providerID, model.modelID),
|
|
164
|
+
},
|
|
165
|
+
),
|
|
166
|
+
state,
|
|
167
|
+
}),
|
|
168
|
+
},
|
|
169
|
+
]);
|
|
170
|
+
api.lifecycle.onDispose(unregisterDashboard);
|
|
171
|
+
|
|
172
|
+
const unregisterKeymap = api.keymap.registerLayer({
|
|
173
|
+
bindings: [{ cmd: "balancer.dashboard.open", key: "ctrl+b" }],
|
|
174
|
+
commands: [
|
|
175
|
+
{
|
|
176
|
+
category: "Plugin",
|
|
177
|
+
name: "balancer.dashboard.open",
|
|
178
|
+
namespace: "palette",
|
|
179
|
+
run() {
|
|
180
|
+
openDashboard();
|
|
181
|
+
},
|
|
182
|
+
slashName: "balancer",
|
|
183
|
+
title: "Open Balancer Dashboard",
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
api.lifecycle.onDispose(unregisterKeymap);
|
|
188
|
+
|
|
189
|
+
api.slots.register({
|
|
190
|
+
slots: {
|
|
191
|
+
session_prompt_right(_ctx, value) {
|
|
192
|
+
sessionProviderID = inferProviderID(
|
|
193
|
+
api.state.session.get(value.session_id),
|
|
194
|
+
);
|
|
195
|
+
void usageAutoRefresh.refreshForPrompt();
|
|
196
|
+
void selectedAccountBarSync.maybeSync();
|
|
197
|
+
void balancerBarSync.maybeSync();
|
|
198
|
+
return createComponent(statusIndicatorModule.BalancerStatusIndicator, {
|
|
199
|
+
api,
|
|
200
|
+
providerID: () =>
|
|
201
|
+
inferProviderID(api.state.session.get(value.session_id)),
|
|
202
|
+
state,
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
sidebar_content(_ctx, value) {
|
|
206
|
+
return createComponent(sidebarModule.BalancerSidebar, {
|
|
207
|
+
activateAccount: (providerID, alias) => {
|
|
208
|
+
return activateAccount(api, state, providerID, alias, {
|
|
209
|
+
applyNativeProviderModel: applyNativeProviderModelAndTrack,
|
|
210
|
+
sessionProviderID:
|
|
211
|
+
nativeProviderID ??
|
|
212
|
+
inferProviderID(api.state.session.get(value.session_id)),
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
api,
|
|
216
|
+
openDashboard,
|
|
217
|
+
state,
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
});
|
|
172
222
|
};
|
|
173
223
|
|
|
174
224
|
export default { id: "opencode-balancer", tui } satisfies TuiPluginModule;
|
|
@@ -1,64 +1,71 @@
|
|
|
1
1
|
import { listAccounts } from "../core/accounts";
|
|
2
|
-
import { refreshAccountUsage } from "../core/usage";
|
|
2
|
+
import type { refreshAccountUsage } from "../core/usage";
|
|
3
3
|
import { refreshUsageForAccount } from "./actions";
|
|
4
4
|
import type { BalancerTuiState } from "./state";
|
|
5
5
|
|
|
6
6
|
type ToastApi = Parameters<typeof refreshUsageForAccount>[0];
|
|
7
7
|
|
|
8
8
|
type UsageAutoRefreshOptions = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
intervalMs?: number;
|
|
10
|
+
promptDebounceMs?: number;
|
|
11
|
+
refreshUsage?: typeof refreshAccountUsage;
|
|
12
|
+
now?: () => number;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export function createUsageAutoRefresh(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
api: ToastApi,
|
|
17
|
+
state: BalancerTuiState,
|
|
18
|
+
options: UsageAutoRefreshOptions = {},
|
|
19
19
|
) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
const intervalMs = options.intervalMs ?? 60_000;
|
|
21
|
+
const promptDebounceMs = options.promptDebounceMs ?? 30_000;
|
|
22
|
+
const now = options.now ?? Date.now;
|
|
23
|
+
const inFlight = new Set<string>();
|
|
24
|
+
let lastPromptRefreshAt = 0;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const refreshOne = async (providerID: string, alias: string) => {
|
|
27
|
+
const key = `${providerID}/${alias}`;
|
|
28
|
+
if (inFlight.has(key)) return;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
inFlight.add(key);
|
|
31
|
+
try {
|
|
32
|
+
await refreshUsageForAccount(api, state, providerID, alias, {
|
|
33
|
+
refreshUsage: options.refreshUsage,
|
|
34
|
+
silent: true,
|
|
35
|
+
});
|
|
36
|
+
} finally {
|
|
37
|
+
inFlight.delete(key);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
const refreshNow = async () => {
|
|
42
|
+
const accounts = listAccounts(state.db).filter(
|
|
43
|
+
(account) => !account.disabled,
|
|
44
|
+
);
|
|
45
|
+
await Promise.all(
|
|
46
|
+
accounts.map((account) => refreshOne(account.providerID, account.alias)),
|
|
47
|
+
);
|
|
48
|
+
};
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
const refreshForPrompt = async () => {
|
|
51
|
+
const current = now();
|
|
52
|
+
if (current - lastPromptRefreshAt < promptDebounceMs) return;
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
lastPromptRefreshAt = current;
|
|
55
|
+
await refreshNow();
|
|
56
|
+
};
|
|
53
57
|
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
const timer =
|
|
59
|
+
intervalMs > 0
|
|
60
|
+
? setInterval(() => void refreshNow(), intervalMs)
|
|
61
|
+
: undefined;
|
|
62
|
+
void refreshNow();
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
return {
|
|
65
|
+
dispose() {
|
|
66
|
+
if (timer) clearInterval(timer);
|
|
67
|
+
},
|
|
68
|
+
refreshForPrompt,
|
|
69
|
+
refreshNow,
|
|
70
|
+
};
|
|
64
71
|
}
|
package/dist/tui/usage-format.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export function formatUsageBar(percent: number | undefined, width = 8) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
if (percent === undefined) return `${"─".repeat(width)} --`;
|
|
3
|
+
const value = Math.max(0, Math.min(100, Math.round(percent)));
|
|
4
|
+
const filled = Math.max(0, Math.min(width, Math.ceil((value / 100) * width)));
|
|
5
|
+
return `${"█".repeat(filled)}${"░".repeat(width - filled)} ${value}%`;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function truncateMiddle(value: string, maxLength: number) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
if (value.length <= maxLength) return value;
|
|
10
|
+
if (maxLength <= 1) return "…";
|
|
11
|
+
const left = Math.ceil((maxLength - 1) / 2);
|
|
12
|
+
const right = Math.floor((maxLength - 1) / 2);
|
|
13
|
+
return `${value.slice(0, left)}…${value.slice(value.length - right)}`;
|
|
14
14
|
}
|
package/package.json
CHANGED
|
@@ -1,54 +1,63 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
2
|
+
"bugs": {
|
|
3
|
+
"url": "https://github.com/thelioo/opencode-balancer/issues"
|
|
4
|
+
},
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@opencode-ai/plugin": "^1.14.51",
|
|
7
|
+
"@opentui/solid": "^0.2.16",
|
|
8
|
+
"solid-js": "^1.9.12"
|
|
9
|
+
},
|
|
10
|
+
"description": "Account balancer plugin for opencode.",
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@biomejs/biome": "^2.4.16",
|
|
13
|
+
"@changesets/cli": "^2.31.0",
|
|
14
|
+
"@types/bun": "^1.3.14",
|
|
15
|
+
"@typescript/native-preview": "7.0.0-dev.20260607.1",
|
|
16
|
+
"concurrently": "^10.0.3",
|
|
17
|
+
"husky": "^9.1.7"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./tui": {
|
|
24
|
+
"default": "./dist/tui/tui.tsx"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"INSTALL.txt",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"homepage": "https://github.com/thelioo/opencode-balancer#readme",
|
|
34
|
+
"keywords": [
|
|
35
|
+
"opencode",
|
|
36
|
+
"opencode-plugin",
|
|
37
|
+
"balancer",
|
|
38
|
+
"accounts"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"main": "./dist/index.js",
|
|
42
|
+
"name": "@thelioo/opencode-balancer",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/thelioo/opencode-balancer.git"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "bun scripts/build.ts",
|
|
49
|
+
"changeset": "changeset",
|
|
50
|
+
"checktypes": "tsgo --noEmit",
|
|
51
|
+
"format": "biome format --write",
|
|
52
|
+
"lint": "biome check .",
|
|
53
|
+
"lint:fix": "biome check . --fix",
|
|
54
|
+
"lint:unsafe": "biome check . --fix --unsafe",
|
|
55
|
+
"prepare": "husky",
|
|
56
|
+
"prepublishOnly": "bun run build",
|
|
57
|
+
"release": "changeset publish",
|
|
58
|
+
"test": "bun test",
|
|
59
|
+
"version": "changeset version"
|
|
60
|
+
},
|
|
61
|
+
"type": "module",
|
|
62
|
+
"version": "0.2.1"
|
|
54
63
|
}
|
package/dist/core/accounts.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { Database } from "bun:sqlite";
|
|
2
|
-
import type { Account, AuthInfo, SelectedModel } from "./types";
|
|
3
|
-
export declare function normalizeAlias(alias: string): string;
|
|
4
|
-
export declare function saveAccount(db: Database, providerID: string, aliasInput: string, auth: AuthInfo): Account;
|
|
5
|
-
export declare function updateAccountAuth(db: Database, providerID: string, aliasInput: string, auth: AuthInfo): Account | undefined;
|
|
6
|
-
export declare function renameAccount(db: Database, providerID: string, fromAliasInput: string, toAliasInput: string): Account;
|
|
7
|
-
export declare function getAccount(db: Database, providerID: string, alias: string): Account | undefined;
|
|
8
|
-
export declare function listAccounts(db: Database, providerID?: string): Account[];
|
|
9
|
-
export declare function setActiveAccount(db: Database, providerID: string, alias: string): Account;
|
|
10
|
-
export declare function removeAccount(db: Database, providerID: string, alias: string): boolean;
|
|
11
|
-
export declare function getActiveAccount(db: Database, providerID: string): Account | undefined;
|
|
12
|
-
export declare function getSelectedAccount(db: Database): Account | undefined;
|
|
13
|
-
export declare function setSelectedModel(db: Database, providerID: string, modelID: string): SelectedModel;
|
|
14
|
-
export declare function getSelectedModel(db: Database, providerID: string): SelectedModel | undefined;
|