@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
package/INSTALL.txt
CHANGED
|
@@ -4,7 +4,7 @@ Goal
|
|
|
4
4
|
Install @thelioo/opencode-balancer as an opencode npm plugin without removing or rewriting the user's existing opencode configuration.
|
|
5
5
|
|
|
6
6
|
What this plugin does
|
|
7
|
-
opencode-balancer lets opencode users save multiple authenticated accounts per provider, switch active accounts
|
|
7
|
+
opencode-balancer lets opencode users save multiple authenticated accounts per provider, switch active accounts from the Balancer TUI sidebar/dashboard, and automatically retry with another saved account when the active account is rate-limited.
|
|
8
8
|
|
|
9
9
|
Important rules
|
|
10
10
|
- Do not run npm install for this plugin. opencode installs npm plugins automatically with Bun at startup.
|
|
@@ -41,18 +41,27 @@ Expected config shape
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
After restart
|
|
44
|
-
|
|
45
|
-
/connect
|
|
46
|
-
/balancer alias work
|
|
44
|
+
1. Tell the user to run opencode's native connect flow:
|
|
45
|
+
/connect <provider>
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
2. Tell the user to use the Balancer TUI modal/sidebar to save the detected pending connection with an alias.
|
|
48
|
+
|
|
49
|
+
3. If the Balancer dashboard is not visible, tell the user to open it from the opencode command palette.
|
|
50
|
+
|
|
51
|
+
4. Remind the user to restart opencode after any later config changes.
|
|
52
|
+
|
|
53
|
+
Security note
|
|
54
|
+
The plugin stores saved provider credentials locally in balancer.sqlite under the opencode config directory, usually ~/.config/opencode/balancer.sqlite or OPENCODE_CONFIG_DIR/balancer.sqlite. Keep this file private and never commit, paste, or share it.
|
|
55
|
+
|
|
56
|
+
Fallback commands
|
|
57
|
+
/balancer commands are compatibility and troubleshooting fallbacks. Alias creation is handled by the TUI pending-connection flow.
|
|
49
58
|
/balancer help
|
|
50
59
|
/balancer list
|
|
51
60
|
/balancer status
|
|
52
61
|
/balancer use <provider> <alias>
|
|
53
|
-
/balancer
|
|
62
|
+
/balancer active <provider>
|
|
54
63
|
|
|
55
64
|
Verification
|
|
56
65
|
- Confirm the opencode config contains @thelioo/opencode-balancer@latest in the plugin array.
|
|
57
66
|
- Confirm no existing plugin entries were removed.
|
|
58
|
-
- Remind the user that opencode must be restarted before
|
|
67
|
+
- Remind the user that opencode must be restarted before the plugin UI and fallback commands become available.
|
package/README.md
CHANGED
|
@@ -9,13 +9,13 @@ _Use multiple accounts per opencode provider and switch automatically when one h
|
|
|
9
9
|
[](https://opencode.ai/docs/plugins)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
|
|
12
|
-
[Features](#features) | [Installation](#installation) | [Usage](#usage) | [Commands](#commands) | [Troubleshooting](#troubleshooting)
|
|
12
|
+
[Features](#features) | [Installation](#installation) | [Usage](#usage) | [Fallback Commands](#fallback-commands) | [Troubleshooting](#troubleshooting)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
`opencode-balancer` is an [opencode](https://opencode.ai/) plugin that lets you save multiple authenticated accounts for the same provider, give each one a friendly alias, and keep working when the active account becomes rate-limited.
|
|
17
17
|
|
|
18
|
-
It works with opencode's existing auth flow: connect a provider, save the detected credentials
|
|
18
|
+
It works with opencode's existing auth flow: connect a provider, save the detected credentials from the Balancer TUI, then let the plugin inject the active account credentials into future model requests.
|
|
19
19
|
|
|
20
20
|
> [!NOTE]
|
|
21
21
|
> This plugin manages credentials already configured through opencode. It does not create accounts, bypass provider limits, or modify provider-side quotas.
|
|
@@ -24,10 +24,10 @@ It works with opencode's existing auth flow: connect a provider, save the detect
|
|
|
24
24
|
|
|
25
25
|
- **Multiple accounts per provider**: Save aliases like `work`, `personal`, or `backup` for the same provider.
|
|
26
26
|
- **Automatic failover**: Switches to another saved account on retryable rate-limit/server responses.
|
|
27
|
-
- **
|
|
28
|
-
- **OAuth-friendly setup**:
|
|
27
|
+
- **TUI-first account management**: Save aliases, switch accounts, and inspect pending connections from the Balancer sidebar/dashboard.
|
|
28
|
+
- **OAuth-friendly setup**: Uses opencode's native `/connect` flow, then prompts you to save the detected credentials in the TUI.
|
|
29
29
|
- **Agent tool support**: Exposes a `balancer_command` tool so opencode agents can manage accounts when asked.
|
|
30
|
-
- **Local credential store**: Saves account
|
|
30
|
+
- **Local credential store**: Saves account credentials and status data under your opencode config directory.
|
|
31
31
|
|
|
32
32
|
## Installation
|
|
33
33
|
|
|
@@ -62,60 +62,39 @@ Then restart opencode.
|
|
|
62
62
|
|
|
63
63
|
### Save Your First Account
|
|
64
64
|
|
|
65
|
-
Connect a provider
|
|
65
|
+
Connect a provider with opencode's native flow:
|
|
66
66
|
|
|
67
67
|
```text
|
|
68
68
|
/connect anthropic
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
After the connection is detected, save it with an alias
|
|
72
|
-
|
|
73
|
-
```text
|
|
74
|
-
/balancer alias work
|
|
75
|
-
```
|
|
71
|
+
After the connection is detected, use the Balancer TUI modal or sidebar to save it with an alias such as `work`.
|
|
76
72
|
|
|
77
73
|
### Add Another Account
|
|
78
74
|
|
|
79
|
-
Connect the same provider with a different account, then save
|
|
75
|
+
Connect the same provider with a different account, then save the new pending connection from the Balancer TUI:
|
|
80
76
|
|
|
81
77
|
```text
|
|
82
78
|
/connect anthropic
|
|
83
|
-
/balancer alias personal
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Switch Accounts Manually
|
|
87
|
-
|
|
88
|
-
```text
|
|
89
|
-
/balancer use anthropic work
|
|
90
79
|
```
|
|
91
80
|
|
|
92
|
-
###
|
|
81
|
+
### Manage Accounts
|
|
93
82
|
|
|
94
|
-
|
|
95
|
-
/balancer list
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Example output:
|
|
99
|
-
|
|
100
|
-
```text
|
|
101
|
-
anthropic:
|
|
102
|
-
* work (oauth, healthy)
|
|
103
|
-
personal (oauth, healthy)
|
|
104
|
-
```
|
|
83
|
+
Use the Balancer sidebar or dashboard to switch accounts, view usage, and review pending connections. If the dashboard is not visible, open it from opencode's command palette.
|
|
105
84
|
|
|
106
85
|
When the active account receives a retryable response such as `429`, `500`, `502`, `503`, `504`, or `529`, the plugin marks it as temporarily rate-limited and retries with another available account for the same provider.
|
|
107
86
|
|
|
108
|
-
## Commands
|
|
87
|
+
## Fallback Commands
|
|
88
|
+
|
|
89
|
+
`opencode-balancer` is TUI-first. `/balancer` commands are compatibility and troubleshooting fallbacks, not the primary account management workflow. Alias creation is intentionally handled by the TUI pending-connection flow.
|
|
109
90
|
|
|
110
91
|
| Command | Description |
|
|
111
92
|
| --- | --- |
|
|
112
|
-
| `/balancer help` | Show
|
|
113
|
-
| `/balancer
|
|
114
|
-
| `/balancer
|
|
115
|
-
| `/balancer use <provider> <
|
|
116
|
-
| `/balancer
|
|
117
|
-
| `/balancer status` | Show saved accounts, last capture, and storage path. |
|
|
118
|
-
| `/balancer remove <provider> <name>` | Remove a saved account. |
|
|
93
|
+
| `/balancer help` | Show fallback commands. |
|
|
94
|
+
| `/balancer list` | List saved accounts as `provider/alias`. |
|
|
95
|
+
| `/balancer status` | Show saved and pending counts. |
|
|
96
|
+
| `/balancer use <provider> <alias>` | Switch the active account for a provider. |
|
|
97
|
+
| `/balancer active <provider>` | Show the active account for a provider. |
|
|
119
98
|
|
|
120
99
|
Aliases are normalized to lowercase and may contain letters, numbers, dots, hyphens, and underscores.
|
|
121
100
|
|
|
@@ -123,15 +102,15 @@ Aliases are normalized to lowercase and may contain letters, numbers, dots, hyph
|
|
|
123
102
|
|
|
124
103
|
`opencode-balancer` hooks into opencode's plugin lifecycle and request flow:
|
|
125
104
|
|
|
126
|
-
1. It watches opencode auth changes and records
|
|
127
|
-
2.
|
|
105
|
+
1. It watches opencode auth changes and records detected provider credentials as pending connections.
|
|
106
|
+
2. The Balancer TUI saves pending connections under provider-specific aliases.
|
|
128
107
|
3. Before model requests, the plugin selects the active account for the request provider.
|
|
129
108
|
4. If the provider returns a retryable rate-limit or server response, the plugin marks the account as temporarily unavailable and retries with another saved account.
|
|
130
109
|
|
|
131
110
|
Saved account data is written to:
|
|
132
111
|
|
|
133
112
|
```text
|
|
134
|
-
~/.config/opencode/balancer
|
|
113
|
+
~/.config/opencode/balancer.sqlite
|
|
135
114
|
```
|
|
136
115
|
|
|
137
116
|
If `OPENCODE_CONFIG_DIR` is set, the plugin uses that directory instead.
|
|
@@ -161,9 +140,9 @@ To test a local checkout with opencode, point your config to the package directo
|
|
|
161
140
|
| --- | --- |
|
|
162
141
|
| Plugin does not load | Confirm `plugin` is singular, restart opencode, and check that the package name is `@thelioo/opencode-balancer@latest`. |
|
|
163
142
|
| `/balancer` is unavailable | Restart opencode after editing the config. |
|
|
164
|
-
| No connection detected | Run `/connect <provider>` first, then
|
|
165
|
-
| Account is not switching |
|
|
166
|
-
| Need
|
|
143
|
+
| No connection detected | Run `/connect <provider>` first, then save the pending connection from the Balancer TUI modal/sidebar. |
|
|
144
|
+
| Account is not switching | Open the Balancer sidebar/dashboard and confirm there is another saved account for the same provider. |
|
|
145
|
+
| Need command-line fallback | Run `/balancer help` for compatibility commands. |
|
|
167
146
|
|
|
168
147
|
## Resources
|
|
169
148
|
|
|
@@ -0,0 +1,14 @@
|
|
|
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;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { now } from "./time";
|
|
2
|
+
const authTypes = ["api", "oauth", "wellknown"];
|
|
3
|
+
export function normalizeAlias(alias) {
|
|
4
|
+
return alias
|
|
5
|
+
.trim()
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
8
|
+
.replace(/^-+|-+$/g, "");
|
|
9
|
+
}
|
|
10
|
+
function accountRowID(row) {
|
|
11
|
+
return `${row.provider_id}/${row.alias}`;
|
|
12
|
+
}
|
|
13
|
+
function isAuthType(value) {
|
|
14
|
+
return authTypes.includes(value);
|
|
15
|
+
}
|
|
16
|
+
function parseAccountAuth(row) {
|
|
17
|
+
const id = accountRowID(row);
|
|
18
|
+
let auth;
|
|
19
|
+
try {
|
|
20
|
+
auth = JSON.parse(row.auth_json);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
throw new Error(`Invalid account row for ${id}: auth_json is invalid`);
|
|
24
|
+
}
|
|
25
|
+
if (!auth || typeof auth !== "object" || !("type" in auth) || typeof auth.type !== "string") {
|
|
26
|
+
throw new Error(`Invalid account row for ${id}: auth_json type is invalid`);
|
|
27
|
+
}
|
|
28
|
+
if (!isAuthType(auth.type)) {
|
|
29
|
+
throw new Error(`Invalid account row for ${id}: auth_json type ${auth.type} is invalid`);
|
|
30
|
+
}
|
|
31
|
+
return auth;
|
|
32
|
+
}
|
|
33
|
+
function validateAccountAuthType(row) {
|
|
34
|
+
if (!isAuthType(row.auth_type)) {
|
|
35
|
+
throw new Error(`Invalid account row for ${accountRowID(row)}: auth_type ${row.auth_type} is invalid`);
|
|
36
|
+
}
|
|
37
|
+
return row.auth_type;
|
|
38
|
+
}
|
|
39
|
+
function validateDisabled(row) {
|
|
40
|
+
if (row.disabled !== 0 && row.disabled !== 1) {
|
|
41
|
+
throw new Error(`Invalid account row for ${accountRowID(row)}: disabled ${row.disabled} is invalid`);
|
|
42
|
+
}
|
|
43
|
+
return Boolean(row.disabled);
|
|
44
|
+
}
|
|
45
|
+
function accountFromRow(row) {
|
|
46
|
+
const auth = parseAccountAuth(row);
|
|
47
|
+
const authType = validateAccountAuthType(row);
|
|
48
|
+
if (auth.type !== authType) {
|
|
49
|
+
throw new Error(`Invalid account row for ${accountRowID(row)}: auth_type ${authType} does not match auth_json type ${auth.type}`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
providerID: row.provider_id,
|
|
53
|
+
alias: row.alias,
|
|
54
|
+
auth,
|
|
55
|
+
authType,
|
|
56
|
+
createdAt: row.created_at,
|
|
57
|
+
updatedAt: row.updated_at,
|
|
58
|
+
lastUsedAt: row.last_used_at ?? undefined,
|
|
59
|
+
rateLimitedUntil: row.rate_limited_until ?? undefined,
|
|
60
|
+
failures: row.failures,
|
|
61
|
+
disabled: validateDisabled(row),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function setActiveAlias(db, providerID, alias) {
|
|
65
|
+
db.query(`INSERT INTO provider_state (provider_id, active_alias, updated_at, metadata_json)
|
|
66
|
+
VALUES (?, ?, ?, '{}')
|
|
67
|
+
ON CONFLICT(provider_id) DO UPDATE SET
|
|
68
|
+
active_alias = excluded.active_alias,
|
|
69
|
+
updated_at = excluded.updated_at`).run(providerID, alias, now());
|
|
70
|
+
db.query(`INSERT INTO settings (key, value) VALUES ('selected_provider_id', ?)
|
|
71
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(providerID);
|
|
72
|
+
}
|
|
73
|
+
function parseMetadataJson(value) {
|
|
74
|
+
if (!value)
|
|
75
|
+
return {};
|
|
76
|
+
try {
|
|
77
|
+
const metadata = JSON.parse(value);
|
|
78
|
+
return metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : {};
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function saveAccount(db, providerID, aliasInput, auth) {
|
|
85
|
+
const alias = normalizeAlias(aliasInput);
|
|
86
|
+
if (!alias)
|
|
87
|
+
throw new Error("Invalid alias");
|
|
88
|
+
const save = db.transaction(() => {
|
|
89
|
+
const timestamp = now();
|
|
90
|
+
db.query(`INSERT INTO accounts (
|
|
91
|
+
provider_id,
|
|
92
|
+
alias,
|
|
93
|
+
auth_json,
|
|
94
|
+
auth_type,
|
|
95
|
+
created_at,
|
|
96
|
+
updated_at,
|
|
97
|
+
failures,
|
|
98
|
+
disabled
|
|
99
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
100
|
+
ON CONFLICT(provider_id, alias) DO UPDATE SET
|
|
101
|
+
auth_json = excluded.auth_json,
|
|
102
|
+
auth_type = excluded.auth_type,
|
|
103
|
+
updated_at = excluded.updated_at`).run(providerID, alias, JSON.stringify(auth), auth.type, timestamp, timestamp, 0, 0);
|
|
104
|
+
setActiveAlias(db, providerID, alias);
|
|
105
|
+
const account = getAccount(db, providerID, alias);
|
|
106
|
+
if (!account)
|
|
107
|
+
throw new Error(`Account not found: ${providerID}/${alias}`);
|
|
108
|
+
return account;
|
|
109
|
+
});
|
|
110
|
+
return save();
|
|
111
|
+
}
|
|
112
|
+
export function updateAccountAuth(db, providerID, aliasInput, auth) {
|
|
113
|
+
const alias = normalizeAlias(aliasInput);
|
|
114
|
+
if (!alias)
|
|
115
|
+
throw new Error("Invalid alias");
|
|
116
|
+
db.query(`UPDATE accounts
|
|
117
|
+
SET auth_json = ?,
|
|
118
|
+
auth_type = ?,
|
|
119
|
+
updated_at = ?
|
|
120
|
+
WHERE provider_id = ? AND alias = ?`).run(JSON.stringify(auth), auth.type, now(), providerID, alias);
|
|
121
|
+
return getAccount(db, providerID, alias);
|
|
122
|
+
}
|
|
123
|
+
export function renameAccount(db, providerID, fromAliasInput, toAliasInput) {
|
|
124
|
+
const fromAlias = normalizeAlias(fromAliasInput);
|
|
125
|
+
const toAlias = normalizeAlias(toAliasInput);
|
|
126
|
+
if (!fromAlias || !toAlias)
|
|
127
|
+
throw new Error("Invalid alias");
|
|
128
|
+
if (fromAlias === toAlias) {
|
|
129
|
+
const account = getAccount(db, providerID, fromAlias);
|
|
130
|
+
if (!account)
|
|
131
|
+
throw new Error(`Account not found: ${providerID}/${fromAlias}`);
|
|
132
|
+
return account;
|
|
133
|
+
}
|
|
134
|
+
const rename = db.transaction(() => {
|
|
135
|
+
const existing = getAccount(db, providerID, fromAlias);
|
|
136
|
+
if (!existing)
|
|
137
|
+
throw new Error(`Account not found: ${providerID}/${fromAlias}`);
|
|
138
|
+
if (getAccount(db, providerID, toAlias))
|
|
139
|
+
throw new Error(`Account already exists: ${providerID}/${toAlias}`);
|
|
140
|
+
db.query(`UPDATE accounts
|
|
141
|
+
SET alias = ?, updated_at = ?
|
|
142
|
+
WHERE provider_id = ? AND alias = ?`).run(toAlias, now(), providerID, fromAlias);
|
|
143
|
+
db.query(`UPDATE usage_snapshots
|
|
144
|
+
SET alias = ?
|
|
145
|
+
WHERE provider_id = ? AND alias = ?`).run(toAlias, providerID, fromAlias);
|
|
146
|
+
const active = db
|
|
147
|
+
.query("SELECT active_alias FROM provider_state WHERE provider_id = ?")
|
|
148
|
+
.get(providerID);
|
|
149
|
+
if (active?.active_alias === fromAlias)
|
|
150
|
+
setActiveAlias(db, providerID, toAlias);
|
|
151
|
+
const account = getAccount(db, providerID, toAlias);
|
|
152
|
+
if (!account)
|
|
153
|
+
throw new Error(`Account not found: ${providerID}/${toAlias}`);
|
|
154
|
+
return account;
|
|
155
|
+
});
|
|
156
|
+
return rename();
|
|
157
|
+
}
|
|
158
|
+
export function getAccount(db, providerID, alias) {
|
|
159
|
+
const normalizedAlias = normalizeAlias(alias);
|
|
160
|
+
const row = db
|
|
161
|
+
.query(`SELECT provider_id, alias, auth_json, auth_type, created_at, updated_at,
|
|
162
|
+
last_used_at, rate_limited_until, failures, disabled
|
|
163
|
+
FROM accounts
|
|
164
|
+
WHERE provider_id = ? AND alias = ?`)
|
|
165
|
+
.get(providerID, normalizedAlias);
|
|
166
|
+
return row ? accountFromRow(row) : undefined;
|
|
167
|
+
}
|
|
168
|
+
export function listAccounts(db, providerID) {
|
|
169
|
+
const sql = `SELECT provider_id, alias, auth_json, auth_type, created_at, updated_at,
|
|
170
|
+
last_used_at, rate_limited_until, failures, disabled
|
|
171
|
+
FROM accounts`;
|
|
172
|
+
const rows = providerID
|
|
173
|
+
? db
|
|
174
|
+
.query(`${sql} WHERE provider_id = ? ORDER BY alias`)
|
|
175
|
+
.all(providerID)
|
|
176
|
+
: db.query(`${sql} ORDER BY provider_id, alias`).all();
|
|
177
|
+
return rows.map(accountFromRow);
|
|
178
|
+
}
|
|
179
|
+
export function setActiveAccount(db, providerID, alias) {
|
|
180
|
+
const normalizedAlias = normalizeAlias(alias);
|
|
181
|
+
const account = getAccount(db, providerID, normalizedAlias);
|
|
182
|
+
if (!account)
|
|
183
|
+
throw new Error(`Account not found: ${providerID}/${normalizedAlias}`);
|
|
184
|
+
setActiveAlias(db, providerID, normalizedAlias);
|
|
185
|
+
return account;
|
|
186
|
+
}
|
|
187
|
+
export function removeAccount(db, providerID, alias) {
|
|
188
|
+
const normalizedAlias = normalizeAlias(alias);
|
|
189
|
+
const remove = db.transaction(() => {
|
|
190
|
+
const existing = getAccount(db, providerID, normalizedAlias);
|
|
191
|
+
if (!existing)
|
|
192
|
+
return false;
|
|
193
|
+
db.query("DELETE FROM accounts WHERE provider_id = ? AND alias = ?").run(providerID, normalizedAlias);
|
|
194
|
+
db.query("DELETE FROM usage_snapshots WHERE provider_id = ? AND alias = ?").run(providerID, normalizedAlias);
|
|
195
|
+
const active = db
|
|
196
|
+
.query("SELECT active_alias FROM provider_state WHERE provider_id = ?")
|
|
197
|
+
.get(providerID);
|
|
198
|
+
if (active?.active_alias === normalizedAlias) {
|
|
199
|
+
const next = db
|
|
200
|
+
.query("SELECT alias FROM accounts WHERE provider_id = ? ORDER BY alias LIMIT 1")
|
|
201
|
+
.get(providerID);
|
|
202
|
+
db.query(`UPDATE provider_state
|
|
203
|
+
SET active_alias = ?, updated_at = ?
|
|
204
|
+
WHERE provider_id = ?`).run(next?.alias ?? null, now(), providerID);
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
});
|
|
208
|
+
return remove();
|
|
209
|
+
}
|
|
210
|
+
export function getActiveAccount(db, providerID) {
|
|
211
|
+
const row = db
|
|
212
|
+
.query("SELECT active_alias FROM provider_state WHERE provider_id = ?")
|
|
213
|
+
.get(providerID);
|
|
214
|
+
if (!row?.active_alias)
|
|
215
|
+
return undefined;
|
|
216
|
+
return getAccount(db, providerID, row.active_alias);
|
|
217
|
+
}
|
|
218
|
+
export function getSelectedAccount(db) {
|
|
219
|
+
const selectedProvider = db
|
|
220
|
+
.query("SELECT value FROM settings WHERE key = 'selected_provider_id'")
|
|
221
|
+
.get();
|
|
222
|
+
if (selectedProvider?.value) {
|
|
223
|
+
const active = getActiveAccount(db, selectedProvider.value);
|
|
224
|
+
if (active)
|
|
225
|
+
return active;
|
|
226
|
+
}
|
|
227
|
+
const row = db
|
|
228
|
+
.query(`SELECT provider_id, active_alias
|
|
229
|
+
FROM provider_state
|
|
230
|
+
WHERE active_alias IS NOT NULL
|
|
231
|
+
ORDER BY updated_at DESC, provider_id
|
|
232
|
+
LIMIT 1`)
|
|
233
|
+
.get();
|
|
234
|
+
if (!row?.active_alias)
|
|
235
|
+
return undefined;
|
|
236
|
+
return getAccount(db, row.provider_id, row.active_alias);
|
|
237
|
+
}
|
|
238
|
+
export function setSelectedModel(db, providerID, modelID) {
|
|
239
|
+
const row = db
|
|
240
|
+
.query("SELECT metadata_json FROM provider_state WHERE provider_id = ?")
|
|
241
|
+
.get(providerID);
|
|
242
|
+
const metadata = parseMetadataJson(row?.metadata_json);
|
|
243
|
+
metadata.selected_model_id = modelID;
|
|
244
|
+
db.query(`INSERT INTO provider_state (provider_id, active_alias, updated_at, metadata_json)
|
|
245
|
+
VALUES (?, NULL, ?, ?)
|
|
246
|
+
ON CONFLICT(provider_id) DO UPDATE SET
|
|
247
|
+
updated_at = excluded.updated_at,
|
|
248
|
+
metadata_json = excluded.metadata_json`).run(providerID, now(), JSON.stringify(metadata));
|
|
249
|
+
return { providerID, modelID };
|
|
250
|
+
}
|
|
251
|
+
export function getSelectedModel(db, providerID) {
|
|
252
|
+
const row = db
|
|
253
|
+
.query("SELECT metadata_json FROM provider_state WHERE provider_id = ?")
|
|
254
|
+
.get(providerID);
|
|
255
|
+
const modelID = parseMetadataJson(row?.metadata_json).selected_model_id;
|
|
256
|
+
if (typeof modelID !== "string" || modelID.length === 0)
|
|
257
|
+
return undefined;
|
|
258
|
+
return { providerID, modelID };
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/core/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { Database } from "bun:sqlite";
|
|
4
|
+
const databases = new Map();
|
|
5
|
+
function isMissingFileError(error) {
|
|
6
|
+
return error.code === "ENOENT";
|
|
7
|
+
}
|
|
8
|
+
function isClosedDatabaseError(error) {
|
|
9
|
+
return error instanceof Error && /closed/i.test(error.message);
|
|
10
|
+
}
|
|
11
|
+
function cachedDatabaseIsOpen(database) {
|
|
12
|
+
try {
|
|
13
|
+
database.exec("SELECT 1;");
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (isClosedDatabaseError(error))
|
|
18
|
+
return false;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function secureDatabaseFiles(path) {
|
|
23
|
+
for (const file of [path, `${path}-wal`, `${path}-shm`]) {
|
|
24
|
+
try {
|
|
25
|
+
chmodSync(file, 0o600);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (!isMissingFileError(error))
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function openBalancerDatabase(path) {
|
|
34
|
+
const existing = databases.get(path);
|
|
35
|
+
if (existing) {
|
|
36
|
+
if (cachedDatabaseIsOpen(existing))
|
|
37
|
+
return existing;
|
|
38
|
+
databases.delete(path);
|
|
39
|
+
}
|
|
40
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
41
|
+
const database = new Database(path);
|
|
42
|
+
try {
|
|
43
|
+
database.exec("PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON;");
|
|
44
|
+
secureDatabaseFiles(path);
|
|
45
|
+
databases.set(path, database);
|
|
46
|
+
return database;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
try {
|
|
50
|
+
database.close();
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function closeBalancerDatabase(path) {
|
|
57
|
+
const existing = databases.get(path);
|
|
58
|
+
if (!existing)
|
|
59
|
+
return;
|
|
60
|
+
databases.delete(path);
|
|
61
|
+
try {
|
|
62
|
+
existing.close();
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (!isClosedDatabaseError(error))
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;AAE9C,SAAS,kBAAkB,CAAC,KAAc;IACtC,OAAQ,KAA2B,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC1D,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAkB;IAC5C,IAAI,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,qBAAqB,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC/C,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC5C,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC;YACD,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;QAChD,CAAC;IACL,CAAC;AACL,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC7C,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,QAAQ,EAAE,CAAC;QACX,IAAI,oBAAoB,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;QACpD,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QACtE,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC1B,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC9B,OAAO,QAAQ,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,CAAC;YACD,QAAQ,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QACV,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAC9C,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC;QACD,QAAQ,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;IACnD,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { BalancerEvent, BalancerEventType } from "./types";
|
|
3
|
+
export declare function appendEvent(db: Database, input: {
|
|
4
|
+
type: BalancerEventType;
|
|
5
|
+
providerID?: string;
|
|
6
|
+
alias?: string;
|
|
7
|
+
message: string;
|
|
8
|
+
metadata?: Record<string, string>;
|
|
9
|
+
}): BalancerEvent;
|
|
10
|
+
export declare function listEvents(db: Database, limit?: number): {
|
|
11
|
+
id: string;
|
|
12
|
+
type: BalancerEventType;
|
|
13
|
+
providerID: string | undefined;
|
|
14
|
+
alias: string | undefined;
|
|
15
|
+
message: string;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
metadata: Record<string, string>;
|
|
18
|
+
}[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { now } from "./time";
|
|
2
|
+
export function appendEvent(db, input) {
|
|
3
|
+
const event = {
|
|
4
|
+
id: crypto.randomUUID(),
|
|
5
|
+
type: input.type,
|
|
6
|
+
providerID: input.providerID,
|
|
7
|
+
alias: input.alias,
|
|
8
|
+
message: input.message,
|
|
9
|
+
createdAt: now(),
|
|
10
|
+
metadata: input.metadata ?? {},
|
|
11
|
+
};
|
|
12
|
+
db.query(`INSERT INTO events (id, type, provider_id, alias, message, created_at, metadata_json)
|
|
13
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(event.id, event.type, event.providerID ?? null, event.alias ?? null, event.message, event.createdAt, JSON.stringify(event.metadata));
|
|
14
|
+
return event;
|
|
15
|
+
}
|
|
16
|
+
export function listEvents(db, limit = 50) {
|
|
17
|
+
return db
|
|
18
|
+
.query("SELECT * FROM events ORDER BY created_at DESC LIMIT ?")
|
|
19
|
+
.all(limit)
|
|
20
|
+
.map((row) => {
|
|
21
|
+
let metadata;
|
|
22
|
+
try {
|
|
23
|
+
metadata = JSON.parse(row.metadata_json);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw new Error(`Invalid event metadata JSON for event ${row.id}`, { cause: error });
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
id: row.id,
|
|
30
|
+
type: row.type,
|
|
31
|
+
providerID: row.provider_id ?? undefined,
|
|
32
|
+
alias: row.alias ?? undefined,
|
|
33
|
+
message: row.message,
|
|
34
|
+
createdAt: row.created_at,
|
|
35
|
+
metadata,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAa7B,MAAM,UAAU,WAAW,CACvB,EAAY,EACZ,KAMC;IAED,MAAM,KAAK,GAAkB;QACzB,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;QACvB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,GAAG,EAAE;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;KACjC,CAAC;IACF,EAAE,CAAC,KAAK,CACJ;sCAC8B,CACjC,CAAC,GAAG,CACD,KAAK,CAAC,EAAE,EACR,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,UAAU,IAAI,IAAI,EACxB,KAAK,CAAC,KAAK,IAAI,IAAI,EACnB,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,SAAS,EACf,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CACjC,CAAC;IACF,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAY,EAAE,KAAK,GAAG,EAAE;IAC/C,OAAO,EAAE;SACJ,KAAK,CAAqB,uDAAuD,CAAC;SAClF,GAAG,CAAC,KAAK,CAAC;SACV,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACT,IAAI,QAAgC,CAAC;QACrC,IAAI,CAAC;YACD,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAA2B,CAAC;QACvE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,yCAAyC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACzF,CAAC;QAED,OAAO;YACH,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,UAAU,EAAE,GAAG,CAAC,WAAW,IAAI,SAAS;YACxC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS;YAC7B,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,SAAS,EAAE,GAAG,CAAC,UAAU;YACzB,QAAQ;SACX,CAAC;IACN,CAAC,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { now } from "./time";
|
|
2
|
+
function key(providerID) {
|
|
3
|
+
return `native_auth_suppressed_until:${providerID}`;
|
|
4
|
+
}
|
|
5
|
+
export function suppressNativeAuthCapture(db, providerID, durationMs = 10_000) {
|
|
6
|
+
db.query(`INSERT INTO settings (key, value) VALUES (?, ?)
|
|
7
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(key(providerID), String(now() + durationMs));
|
|
8
|
+
}
|
|
9
|
+
export function isNativeAuthCaptureSuppressed(db, providerID) {
|
|
10
|
+
const settingKey = key(providerID);
|
|
11
|
+
const row = db.query("SELECT value FROM settings WHERE key = ?").get(settingKey);
|
|
12
|
+
const suppressedUntil = Number(row?.value ?? 0);
|
|
13
|
+
if (Number.isFinite(suppressedUntil) && suppressedUntil > now())
|
|
14
|
+
return true;
|
|
15
|
+
if (row)
|
|
16
|
+
db.query("DELETE FROM settings WHERE key = ?").run(settingKey);
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=native-auth-suppression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-auth-suppression.js","sourceRoot":"","sources":["../../src/core/native-auth-suppression.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAE7B,SAAS,GAAG,CAAC,UAAkB;IAC3B,OAAO,gCAAgC,UAAU,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,EAAY,EAAE,UAAkB,EAAE,UAAU,GAAG,MAAM;IAC3F,EAAE,CAAC,KAAK,CACJ;+DACuD,CAC1D,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,EAAY,EAAE,UAAkB;IAC1E,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAA8B,0CAA0C,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC9G,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC;IAChD,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,eAAe,GAAG,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7E,IAAI,GAAG;QAAE,EAAE,CAAC,KAAK,CAAoB,oCAAoC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3F,OAAO,KAAK,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
export declare function markNativeConnectInProgress(db: Database, durationMs?: number): void;
|
|
3
|
+
export declare function isNativeConnectInProgress(db: Database): boolean;
|
|
4
|
+
export declare function clearNativeConnectInProgress(db: Database): void;
|