@thelioo/opencode-balancer 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-patch.js","sourceRoot":"","sources":["../../src/balancer/fetch-patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { Account } from "./types";
2
+ export declare function applyAuthToHeaders(headers: Headers, account: Account): void;
3
+ export declare function cloneRequestInput(input: RequestInfo | URL, init: RequestInit | undefined, headers: Headers): [RequestInfo | URL, RequestInit | undefined];
4
+ export declare function parseUrl(input: RequestInfo | URL): URL | undefined;
5
+ export declare function headersFrom(input: RequestInfo | URL, init?: RequestInit): Headers;
6
+ export declare function parseJsonBody(init?: RequestInit): any;
7
+ export declare function isInternalUrl(url: URL | undefined): boolean;
8
+ export declare function parseAuthSetProvider(url: URL | undefined): string | undefined;
9
+ export declare function parseProviderAuthorize(url: URL | undefined): string | undefined;
10
+ export declare function parseProviderCallback(url: URL | undefined): string | undefined;
11
+ export declare function parseSessionCommand(url: URL | undefined): string | undefined;
12
+ export declare function fakeCommandResponse(sessionID: string, text: string): {
13
+ info: {
14
+ id: string;
15
+ sessionID: string;
16
+ role: string;
17
+ time: {
18
+ created: number;
19
+ };
20
+ agent: string;
21
+ model: {
22
+ providerID: string;
23
+ modelID: string;
24
+ };
25
+ };
26
+ parts: {
27
+ id: string;
28
+ sessionID: string;
29
+ messageID: string;
30
+ type: string;
31
+ text: string;
32
+ synthetic: boolean;
33
+ ignored: boolean;
34
+ metadata: {
35
+ opencodeBalancerCommand: boolean;
36
+ };
37
+ }[];
38
+ };
39
+ export declare function maybeInjectAliasPrompt(response: Response): Promise<Response>;
40
+ export declare function retryAfterMs(response: Response): number;
@@ -0,0 +1,199 @@
1
+ import { BALANCER_ALIAS_INPUT, BALANCER_METADATA_KEY, now, } from "./state";
2
+ function getHeaderEntries(headers) {
3
+ return Array.from(headers.entries()).map(([key, value]) => [key.toLowerCase(), value]);
4
+ }
5
+ export function applyAuthToHeaders(headers, account) {
6
+ const auth = account.auth;
7
+ const entries = getHeaderEntries(headers);
8
+ if (auth.type === "oauth") {
9
+ if (auth.access)
10
+ headers.set("authorization", `Bearer ${auth.access}`);
11
+ return;
12
+ }
13
+ if (auth.type === "wellknown") {
14
+ if (headers.has("authorization"))
15
+ headers.set("authorization", `Bearer ${auth.token}`);
16
+ else
17
+ headers.set(auth.key, auth.token);
18
+ return;
19
+ }
20
+ const authHeaderNames = [
21
+ "authorization",
22
+ "x-api-key",
23
+ "api-key",
24
+ "x-goog-api-key",
25
+ "x-stainless-api-key",
26
+ "anthropic-api-key",
27
+ "cohere-api-key",
28
+ ];
29
+ let changed = false;
30
+ for (const name of authHeaderNames) {
31
+ const current = entries.find(([key]) => key === name)?.[1];
32
+ if (!current)
33
+ continue;
34
+ if (name === "authorization") {
35
+ headers.set(name, current.toLowerCase().startsWith("bearer ")
36
+ ? `Bearer ${auth.key}`
37
+ : auth.key);
38
+ }
39
+ else {
40
+ headers.set(name, auth.key);
41
+ }
42
+ changed = true;
43
+ }
44
+ if (!changed)
45
+ headers.set("authorization", `Bearer ${auth.key}`);
46
+ }
47
+ export function cloneRequestInput(input, init, headers) {
48
+ if (typeof Request !== "undefined" && input instanceof Request) {
49
+ return [new Request(input, { ...init, headers }), undefined];
50
+ }
51
+ return [input, { ...init, headers }];
52
+ }
53
+ export function parseUrl(input) {
54
+ try {
55
+ if (typeof input === "string")
56
+ return new URL(input, "http://localhost");
57
+ if (input instanceof URL)
58
+ return input;
59
+ if (typeof Request !== "undefined" && input instanceof Request)
60
+ return new URL(input.url);
61
+ }
62
+ catch { }
63
+ return undefined;
64
+ }
65
+ export function headersFrom(input, init) {
66
+ if (init?.headers)
67
+ return new Headers(init.headers);
68
+ if (typeof Request !== "undefined" && input instanceof Request)
69
+ return new Headers(input.headers);
70
+ return new Headers();
71
+ }
72
+ function requestBodyText(init) {
73
+ const body = init?.body;
74
+ if (typeof body === "string")
75
+ return body;
76
+ if (body instanceof URLSearchParams)
77
+ return body.toString();
78
+ return undefined;
79
+ }
80
+ export function parseJsonBody(init) {
81
+ const text = requestBodyText(init);
82
+ if (!text)
83
+ return undefined;
84
+ try {
85
+ return JSON.parse(text);
86
+ }
87
+ catch {
88
+ return undefined;
89
+ }
90
+ }
91
+ export function isInternalUrl(url) {
92
+ if (!url)
93
+ return false;
94
+ return (url.hostname === "localhost" ||
95
+ url.hostname === "127.0.0.1" ||
96
+ url.hostname === "0.0.0.0");
97
+ }
98
+ export function parseAuthSetProvider(url) {
99
+ if (!url || !isInternalUrl(url))
100
+ return undefined;
101
+ const match = url.pathname.match(/^\/auth\/([^/]+)$/);
102
+ return match ? decodeURIComponent(match[1]) : undefined;
103
+ }
104
+ export function parseProviderAuthorize(url) {
105
+ if (!url || !isInternalUrl(url))
106
+ return undefined;
107
+ const match = url.pathname.match(/^\/provider\/([^/]+)\/oauth\/authorize$/);
108
+ return match ? decodeURIComponent(match[1]) : undefined;
109
+ }
110
+ export function parseProviderCallback(url) {
111
+ if (!url || !isInternalUrl(url))
112
+ return undefined;
113
+ const match = url.pathname.match(/^\/provider\/([^/]+)\/oauth\/callback$/);
114
+ return match ? decodeURIComponent(match[1]) : undefined;
115
+ }
116
+ export function parseSessionCommand(url) {
117
+ if (!url || !isInternalUrl(url))
118
+ return undefined;
119
+ const match = url.pathname.match(/^\/session\/([^/]+)\/command$/);
120
+ return match ? decodeURIComponent(match[1]) : undefined;
121
+ }
122
+ export function fakeCommandResponse(sessionID, text) {
123
+ const created = now();
124
+ const suffix = `${created.toString(36)}${Math.random().toString(36).slice(2, 8)}`;
125
+ const messageID = `msg_${suffix}`;
126
+ return {
127
+ info: {
128
+ id: messageID,
129
+ sessionID,
130
+ role: "user",
131
+ time: { created },
132
+ agent: "build",
133
+ model: { providerID: "opencode", modelID: "balancer-command" },
134
+ },
135
+ parts: [
136
+ {
137
+ id: `prt_${suffix}`,
138
+ sessionID,
139
+ messageID,
140
+ type: "text",
141
+ text,
142
+ synthetic: true,
143
+ ignored: true,
144
+ metadata: { [BALANCER_METADATA_KEY]: true },
145
+ },
146
+ ],
147
+ };
148
+ }
149
+ export async function maybeInjectAliasPrompt(response) {
150
+ const contentType = response.headers.get("content-type") ?? "";
151
+ if (!response.ok || !contentType.includes("application/json"))
152
+ return response;
153
+ try {
154
+ const data = (await response.clone().json());
155
+ for (const methods of Object.values(data)) {
156
+ if (!Array.isArray(methods))
157
+ continue;
158
+ for (const method of methods) {
159
+ const prompts = Array.isArray(method.prompts)
160
+ ? method.prompts
161
+ : [];
162
+ if (prompts.some((prompt) => prompt?.key === BALANCER_ALIAS_INPUT))
163
+ continue;
164
+ method.prompts = [
165
+ ...prompts,
166
+ {
167
+ type: "text",
168
+ key: BALANCER_ALIAS_INPUT,
169
+ message: "Alias to save in the balancer (optional)",
170
+ placeholder: "work, personal, backup",
171
+ },
172
+ ];
173
+ }
174
+ }
175
+ const headers = new Headers(response.headers);
176
+ headers.set("content-type", "application/json");
177
+ return new Response(JSON.stringify(data), {
178
+ status: response.status,
179
+ statusText: response.statusText,
180
+ headers,
181
+ });
182
+ }
183
+ catch {
184
+ return response;
185
+ }
186
+ }
187
+ export function retryAfterMs(response) {
188
+ const retryAfter = response.headers.get("retry-after");
189
+ if (!retryAfter)
190
+ return 60_000;
191
+ const seconds = Number(retryAfter);
192
+ if (Number.isFinite(seconds))
193
+ return Math.max(1_000, seconds * 1000);
194
+ const date = Date.parse(retryAfter);
195
+ if (Number.isFinite(date))
196
+ return Math.max(1_000, date - now());
197
+ return 60_000;
198
+ }
199
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/balancer/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AuthInfo } from "./types";
2
+ export declare function setNativeAuth(client: any, providerID: string, auth: AuthInfo): Promise<void>;
3
+ export declare function showToast(client: any, message: string, variant?: "info" | "success" | "warning" | "error"): Promise<void>;
@@ -0,0 +1,16 @@
1
+ import { now, state } from "./state";
2
+ export async function setNativeAuth(client, providerID, auth) {
3
+ state.suppressAuthCaptureUntil.set(providerID, now() + 2_000);
4
+ await client.auth
5
+ .set({
6
+ path: { id: providerID },
7
+ body: auth,
8
+ })
9
+ .catch(() => undefined);
10
+ }
11
+ export async function showToast(client, message, variant = "info") {
12
+ await client.tui
13
+ .showToast({ body: { message, variant, duration: 15_000 } })
14
+ .catch(() => undefined);
15
+ }
16
+ //# sourceMappingURL=native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.js","sourceRoot":"","sources":["../../src/balancer/native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGrC,MAAM,CAAC,KAAK,UAAU,aAAa,CAC/B,MAAW,EACX,UAAkB,EAClB,IAAc;IAEd,KAAK,CAAC,wBAAwB,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IAC9D,MAAM,MAAM,CAAC,IAAI;SACZ,GAAG,CAAC;QACD,IAAI,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE;QACxB,IAAI,EAAE,IAAI;KACb,CAAC;SACD,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC3B,MAAW,EACX,OAAe,EACf,UAAoD,MAAM;IAE1D,MAAM,MAAM,CAAC,GAAG;SACX,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC;SAC3D,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { AuthInfo, PendingRequest } from "./types";
2
+ export declare const INTERNAL_REQUEST_HEADER = "x-opencode-balancer-request";
3
+ export declare const BALANCER_ALIAS_INPUT = "__opencode_balancer_alias";
4
+ export declare const BALANCER_METADATA_KEY = "opencodeBalancerCommand";
5
+ export declare const RETRYABLE_STATUS: Set<number>;
6
+ export declare const state: {
7
+ fetchPatched: boolean;
8
+ pendingRequests: Map<string, PendingRequest>;
9
+ pendingOauthAlias: Map<string, string>;
10
+ suppressAuthCaptureUntil: Map<string, number>;
11
+ lastAuthSnapshot: Record<string, AuthInfo>;
12
+ watchStarted: boolean;
13
+ };
14
+ export declare function now(): number;
@@ -0,0 +1,16 @@
1
+ export const INTERNAL_REQUEST_HEADER = "x-opencode-balancer-request";
2
+ export const BALANCER_ALIAS_INPUT = "__opencode_balancer_alias";
3
+ export const BALANCER_METADATA_KEY = "opencodeBalancerCommand";
4
+ export const RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504, 529]);
5
+ export const state = {
6
+ fetchPatched: false,
7
+ pendingRequests: new Map(),
8
+ pendingOauthAlias: new Map(),
9
+ suppressAuthCaptureUntil: new Map(),
10
+ lastAuthSnapshot: {},
11
+ watchStarted: false,
12
+ };
13
+ export function now() {
14
+ return Date.now();
15
+ }
16
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/balancer/state.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,uBAAuB,GAAG,6BAA6B,CAAC;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAG,2BAA2B,CAAC;AAChE,MAAM,CAAC,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAExE,MAAM,CAAC,MAAM,KAAK,GAAG;IACjB,YAAY,EAAE,KAAK;IACnB,eAAe,EAAE,IAAI,GAAG,EAA0B;IAClD,iBAAiB,EAAE,IAAI,GAAG,EAAkB;IAC5C,wBAAwB,EAAE,IAAI,GAAG,EAAkB;IACnD,gBAAgB,EAAE,EAA8B;IAChD,YAAY,EAAE,KAAK;CACtB,CAAC;AAEF,MAAM,UAAU,GAAG;IACf,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { AuthInfo, Store } from "./types";
2
+ export declare function storePath(): string;
3
+ export declare function authPath(): string;
4
+ export declare function readStore(): Promise<Store>;
5
+ export declare function writeStore(store: Store): Promise<void>;
6
+ export declare function readNativeAuth(): Promise<Record<string, AuthInfo>>;
7
+ export declare function isAuthInfo(value: unknown): value is AuthInfo;
8
+ export declare function captureAuth(providerID: string, auth: AuthInfo, source: NonNullable<Store["lastCaptured"]>["source"]): Promise<Store>;
@@ -0,0 +1,92 @@
1
+ import { now } from "./state";
2
+ function joinPath(...parts) {
3
+ return parts
4
+ .filter(Boolean)
5
+ .join("/")
6
+ .replace(/\/+/g, "/");
7
+ }
8
+ function homeDir() {
9
+ return Bun.env.HOME || "/";
10
+ }
11
+ function xdgConfigHome() {
12
+ return Bun.env.XDG_CONFIG_HOME || joinPath(homeDir(), ".config");
13
+ }
14
+ function xdgDataHome() {
15
+ return Bun.env.XDG_DATA_HOME || joinPath(homeDir(), ".local", "share");
16
+ }
17
+ function configDir() {
18
+ return Bun.env.OPENCODE_CONFIG_DIR || joinPath(xdgConfigHome(), "opencode");
19
+ }
20
+ function dataDir() {
21
+ return joinPath(xdgDataHome(), "opencode");
22
+ }
23
+ export function storePath() {
24
+ return joinPath(configDir(), "balancer-accounts.json");
25
+ }
26
+ export function authPath() {
27
+ return joinPath(dataDir(), "auth.json");
28
+ }
29
+ function emptyStore() {
30
+ return { version: 1, providers: {} };
31
+ }
32
+ async function readJsonFile(path, fallback) {
33
+ try {
34
+ const file = Bun.file(path);
35
+ if (!(await file.exists()))
36
+ return fallback;
37
+ return JSON.parse(await file.text());
38
+ }
39
+ catch {
40
+ return fallback;
41
+ }
42
+ }
43
+ async function writeJsonFile(path, value) {
44
+ await Bun.write(path, `${JSON.stringify(value, null, 2)}\n`, {
45
+ mode: 0o600,
46
+ });
47
+ }
48
+ export async function readStore() {
49
+ const parsed = await readJsonFile(storePath(), emptyStore());
50
+ if (parsed.version !== 1 || !parsed.providers)
51
+ return emptyStore();
52
+ return parsed;
53
+ }
54
+ export async function writeStore(store) {
55
+ await writeJsonFile(storePath(), store);
56
+ }
57
+ export async function readNativeAuth() {
58
+ if (Bun.env.OPENCODE_AUTH_CONTENT) {
59
+ try {
60
+ return JSON.parse(Bun.env.OPENCODE_AUTH_CONTENT);
61
+ }
62
+ catch {
63
+ return {};
64
+ }
65
+ }
66
+ return readJsonFile(authPath(), {});
67
+ }
68
+ export function isAuthInfo(value) {
69
+ if (!value || typeof value !== "object")
70
+ return false;
71
+ const type = value.type;
72
+ if (type === "api")
73
+ return typeof value.key === "string";
74
+ if (type === "oauth") {
75
+ const item = value;
76
+ return (typeof item.refresh === "string" &&
77
+ typeof item.access === "string" &&
78
+ typeof item.expires === "number");
79
+ }
80
+ if (type === "wellknown") {
81
+ const item = value;
82
+ return typeof item.key === "string" && typeof item.token === "string";
83
+ }
84
+ return false;
85
+ }
86
+ export async function captureAuth(providerID, auth, source) {
87
+ const store = await readStore();
88
+ store.lastCaptured = { providerID, auth, capturedAt: now(), source };
89
+ await writeStore(store);
90
+ return store;
91
+ }
92
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/balancer/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export type AuthInfo = {
2
+ type: "api";
3
+ key: string;
4
+ metadata?: Record<string, string>;
5
+ } | {
6
+ type: "oauth";
7
+ refresh: string;
8
+ access: string;
9
+ expires: number;
10
+ accountId?: string;
11
+ enterpriseUrl?: string;
12
+ } | {
13
+ type: "wellknown";
14
+ key: string;
15
+ token: string;
16
+ };
17
+ export type Account = {
18
+ alias: string;
19
+ providerID: string;
20
+ auth: AuthInfo;
21
+ createdAt: number;
22
+ updatedAt: number;
23
+ lastUsedAt?: number;
24
+ rateLimitedUntil?: number;
25
+ failures?: number;
26
+ };
27
+ export type ProviderState = {
28
+ active?: string;
29
+ accounts: Record<string, Account>;
30
+ };
31
+ export type Store = {
32
+ version: 1;
33
+ providers: Record<string, ProviderState>;
34
+ lastCaptured?: {
35
+ providerID: string;
36
+ auth: AuthInfo;
37
+ capturedAt: number;
38
+ source: "auth-file" | "http" | "oauth-callback";
39
+ };
40
+ };
41
+ export type PendingRequest = {
42
+ providerID: string;
43
+ account?: Account;
44
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/balancer/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ declare const _default: ({ client }: import("@opencode-ai/plugin").PluginInput) => Promise<{
2
+ config: (cfg: import("@opencode-ai/plugin").Config) => Promise<void>;
3
+ "command.execute.before": (input: {
4
+ command: string;
5
+ sessionID: string;
6
+ arguments: string;
7
+ }, output: {
8
+ parts: import("@opencode-ai/sdk").Part[];
9
+ }) => Promise<void>;
10
+ "experimental.chat.messages.transform": (_input: {}, output: {
11
+ messages: {
12
+ info: import("@opencode-ai/sdk").Message;
13
+ parts: import("@opencode-ai/sdk").Part[];
14
+ }[];
15
+ }) => Promise<void>;
16
+ "chat.headers": (input: {
17
+ sessionID: string;
18
+ agent: string;
19
+ model: import("@opencode-ai/sdk").Model;
20
+ provider: import("@opencode-ai/plugin").ProviderContext;
21
+ message: import("@opencode-ai/sdk").UserMessage;
22
+ }, output: {
23
+ headers: Record<string, string>;
24
+ }) => Promise<void>;
25
+ tool: {
26
+ balancer_command: {
27
+ description: string;
28
+ args: {
29
+ command: import("zod").ZodString;
30
+ };
31
+ execute(args: {
32
+ command: string;
33
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
34
+ };
35
+ };
36
+ }>;
37
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { BALANCER_METADATA_KEY, INTERNAL_REQUEST_HEADER, getActiveAccount, setNativeAuth, showToast, state, } from "./balancer/core";
3
+ import { startAuthWatcher } from "./balancer/auth-watcher";
4
+ import { runBalancerCommand } from "./balancer/commands";
5
+ import { installFetchPatch } from "./balancer/fetch-patch";
6
+ export default (async ({ client }) => {
7
+ await installFetchPatch(client);
8
+ startAuthWatcher(client);
9
+ return {
10
+ config: async (cfg) => {
11
+ cfg.command = {
12
+ ...(cfg.command ?? {}),
13
+ balancer: {
14
+ description: "Manage multiple accounts per provider",
15
+ template: "Run the internal balancer command with these arguments: $ARGUMENTS",
16
+ },
17
+ };
18
+ },
19
+ "command.execute.before": async (input, output) => {
20
+ if (input.command !== "balancer")
21
+ return;
22
+ const result = await runBalancerCommand(client, input.arguments);
23
+ output.parts.length = 0;
24
+ await showToast(client, result, "info");
25
+ throw new Error(`[balancer]\n${result}`);
26
+ },
27
+ "experimental.chat.messages.transform": async (_input, output) => {
28
+ output.messages = output.messages.filter((message) => {
29
+ return !message.parts.some((part) => {
30
+ return part?.metadata?.[BALANCER_METADATA_KEY] === true;
31
+ });
32
+ });
33
+ },
34
+ "chat.headers": async (input, output) => {
35
+ const providerID = input.model.providerID;
36
+ const account = await getActiveAccount(providerID);
37
+ if (!account)
38
+ return;
39
+ if (account.auth.type === "oauth")
40
+ await setNativeAuth(client, providerID, account.auth);
41
+ const requestID = crypto.randomUUID();
42
+ state.pendingRequests.set(requestID, {
43
+ providerID,
44
+ account,
45
+ });
46
+ output.headers[INTERNAL_REQUEST_HEADER] = requestID;
47
+ },
48
+ tool: {
49
+ balancer_command: tool({
50
+ description: "Run an opencode account balancer command. Use for managing provider account aliases.",
51
+ args: {
52
+ command: tool.schema
53
+ .string()
54
+ .describe("Command, e.g. 'list', 'alias work', 'use anthropic personal'."),
55
+ },
56
+ execute: async (args) => runBalancerCommand(client, args.command),
57
+ }),
58
+ },
59
+ };
60
+ });
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@thelioo/opencode-balancer",
3
+ "version": "0.1.0",
4
+ "description": "Account balancer plugin for opencode.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "INSTALL.txt",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc --project tsconfig.json",
22
+ "check": "tsc --noEmit --project tsconfig.json",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "opencode",
27
+ "opencode-plugin",
28
+ "balancer",
29
+ "accounts"
30
+ ],
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@opencode-ai/plugin": "^1.14.51"
34
+ },
35
+ "devDependencies": {
36
+ "@types/bun": "^1.3.14",
37
+ "typescript": "^5.9.3"
38
+ }
39
+ }