-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathopencode2.ts
More file actions
99 lines (75 loc) · 3.23 KB
/
Copy pathopencode2.ts
File metadata and controls
99 lines (75 loc) · 3.23 KB
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
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
import { Plugin } from "@opencode/plugin";
import { probeQuota, type ProbeQuotaOptions, type ProbeSnapshot } from "./lib/codex-usage-probe.js";
import { CODEX_USAGE_RPC, type UsageRequest } from "./lib/opencode2-rpc.js";
const CODEX_USAGE_DESCRIPTION =
"Get current Codex quota usage and reset times for the connected ChatGPT account. Use whenever the user asks about Codex usage, ChatGPT usage, remaining quota, limits, or reset times. Does not report OpenAI API billing or general ChatGPT message limits.";
const EMPTY_OBJECT_SCHEMA = {
type: "object",
properties: {},
required: [],
additionalProperties: false,
} as const;
const NO_ACTIVE_OPENAI_CONNECTION: ProbeSnapshot = {
status: "error",
statusCode: "auth",
error:
"No active OpenAI OAuth connection is available in OpenCode 2. Connect or reauthenticate your ChatGPT account.",
};
type QuotaProbe = (options?: ProbeQuotaOptions) => Promise<ProbeSnapshot>;
const activeCredentials = async (
ctx: Plugin.Context,
): Promise<ProbeQuotaOptions["credentials"] | undefined> => {
const connection = await ctx.integration.connection.active("openai");
if (!connection) return undefined;
const credential = await ctx.integration.connection.resolve(connection);
if (credential?.type !== "oauth") return undefined;
const accountId = credential.metadata?.accountID ?? credential.metadata?.accountId;
return {
accessToken: credential.access,
accountId: typeof accountId === "string" ? accountId : undefined,
};
};
export const createOpenCode2Plugin = (probe: QuotaProbe = probeQuota) =>
Plugin.define({
id: "opencode-codex-usage",
async setup(ctx) {
const disposers: Array<() => Promise<void>> = [];
const readQuota = async (input: UsageRequest) => {
const credentials = await activeCredentials(ctx);
if (!credentials) return NO_ACTIVE_OPENAI_CONNECTION;
return probe({ ...input, credentials });
};
try {
const rpcRegistration = await ctx.rpc.register(CODEX_USAGE_RPC, {
usage: readQuota,
pollingEligible: async ({ sessionID }) => {
const session = await ctx.session.get({ sessionID });
const model =
session.model ?? (await ctx.model.default({ location: session.location })).data;
if (model?.providerID !== "openai") return false;
return (await activeCredentials(ctx)) !== undefined;
},
});
disposers.push(() => rpcRegistration.dispose());
const toolRegistration = await ctx.tool.transform((editor) =>
editor.add({
name: "codex_usage",
description: CODEX_USAGE_DESCRIPTION,
input: EMPTY_OBJECT_SCHEMA,
execute: async () => ({ content: JSON.stringify(await readQuota({})) }),
}),
);
disposers.push(() => toolRegistration.dispose());
} catch (error) {
await Promise.allSettled([...disposers].reverse().map((dispose) => dispose()));
throw error;
}
let disposed = false;
return async () => {
if (disposed) return;
disposed = true;
await Promise.all([...disposers].reverse().map((dispose) => dispose()));
};
},
});
export default createOpenCode2Plugin();