opencode-openai-profiles 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.
- package/README.md +150 -0
- package/dist/auth-store.d.ts +21 -0
- package/dist/auth-store.js +270 -0
- package/dist/auth-store.js.map +1 -0
- package/dist/codex-oauth.d.ts +9 -0
- package/dist/codex-oauth.js +126 -0
- package/dist/codex-oauth.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +573 -0
- package/dist/index.js.map +1 -0
- package/dist/openai-auth-method.d.ts +9 -0
- package/dist/openai-auth-method.js +31 -0
- package/dist/openai-auth-method.js.map +1 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.js +20 -0
- package/dist/paths.js.map +1 -0
- package/dist/profile-name.d.ts +1 -0
- package/dist/profile-name.js +12 -0
- package/dist/profile-name.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/dist/tui.d.ts +5 -0
- package/dist/tui.js +7 -0
- package/dist/tui.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# opencode-openai-profiles
|
|
2
|
+
|
|
3
|
+
OpenCode TUI plugin for switching between saved OpenAI ChatGPT Pro/Plus OAuth profiles.
|
|
4
|
+
|
|
5
|
+
OpenCode has one active `openai` auth slot. This plugin saves named copies of that auth object and swaps one into `auth.json` when you switch profiles.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
Early local-first plugin. Profile switches apply live when your OpenCode version supports runtime auth updates.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
Build from this repo:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm install
|
|
17
|
+
pnpm build
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Install the local plugin:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
opencode plugin "$PWD" --global --force
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The command adds the package to OpenCode's server and TUI plugin configs when OpenCode exposes both targets.
|
|
27
|
+
|
|
28
|
+
After publishing, add the package to your OpenCode config:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"plugin": ["opencode-openai-profiles"]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For OpenCode versions with external TUI plugin targets, add the TUI target:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"plugin": ["opencode-openai-profiles/tui"]
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
Open the account picker:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
/openai-profiles
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Short alias:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
/oa
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If the TUI target is unavailable, use the CLI fallback:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
/openai-account-cli save <name>
|
|
62
|
+
/openai-account-cli switch <name>
|
|
63
|
+
/openai-account-cli rename <old> <new>
|
|
64
|
+
/openai-account-cli list
|
|
65
|
+
/openai-account-cli active
|
|
66
|
+
/openai-account-cli login [browser|headless]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## First Setup
|
|
70
|
+
|
|
71
|
+
Save your current OpenAI account:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
/openai-profiles
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Choose `Save Current Profile`, then enter `account-1` or another name.
|
|
78
|
+
|
|
79
|
+
If you skip this step, the plugin saves the active unsaved account as `account-1` before starting a new OpenAI login.
|
|
80
|
+
|
|
81
|
+
Log in to another account:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
/openai-profiles
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Choose `Login to OpenAI`. Select `ChatGPT Pro/Plus (headless)` if you want to paste the final callback URL by hand.
|
|
88
|
+
|
|
89
|
+
CLI fallback:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
/openai-account-cli login browser
|
|
93
|
+
/openai-account-cli login headless
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If OpenCode prompts you to restart after login, restart it before saving the new active account:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
/openai-profiles
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Choose `Save Current Profile`, then enter `account-2` or another name.
|
|
103
|
+
|
|
104
|
+
Switch profiles:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
/openai-profiles
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Choose `Switch Profile`, then select a saved profile.
|
|
111
|
+
|
|
112
|
+
If OpenCode cannot apply the switch live, the plugin will tell you to restart.
|
|
113
|
+
|
|
114
|
+
CLI fallback:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
/openai-account-cli switch account-1
|
|
118
|
+
/openai-account-cli switch account-2
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Storage
|
|
122
|
+
|
|
123
|
+
OpenCode stores the active auth file here:
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
~/.local/share/opencode/auth.json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This plugin stores saved OpenAI profiles here:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
~/.local/share/opencode/auth-profiles/
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Example files:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
openai-account-1.json
|
|
139
|
+
openai-account-2.json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Each profile file contains only the `openai` OAuth object. The plugin preserves other providers in `auth.json`.
|
|
143
|
+
|
|
144
|
+
## Security
|
|
145
|
+
|
|
146
|
+
Profile files contain OAuth tokens. Treat them as secrets.
|
|
147
|
+
|
|
148
|
+
Do not commit `auth.json` or `auth-profiles`.
|
|
149
|
+
|
|
150
|
+
The plugin never displays token values. It only shows the OpenAI `accountId` when available.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { OAuth } from "@opencode-ai/sdk/v2";
|
|
2
|
+
import { type OpenCodeAuthPaths } from "./paths.js";
|
|
3
|
+
export type OpenAIAuthProfile = OAuth;
|
|
4
|
+
export type AuthJson = Record<string, unknown> & {
|
|
5
|
+
openai?: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type SavedProfile = {
|
|
8
|
+
name: string;
|
|
9
|
+
accountId?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function getActiveOpenAIProfile(paths: OpenCodeAuthPaths): Promise<OpenAIAuthProfile>;
|
|
12
|
+
export declare function saveActiveOpenAIProfile(paths: OpenCodeAuthPaths, inputProfileName: string): Promise<SavedProfile>;
|
|
13
|
+
export declare function saveActiveOpenAIProfileIfUnsaved(paths: OpenCodeAuthPaths): Promise<SavedProfile | undefined>;
|
|
14
|
+
export declare function switchActiveOpenAIProfile(paths: OpenCodeAuthPaths, inputProfileName: string): Promise<SavedProfile>;
|
|
15
|
+
export declare function renameOpenAIProfile(paths: OpenCodeAuthPaths, inputCurrentName: string, inputNextName: string): Promise<SavedProfile>;
|
|
16
|
+
export declare function setActiveOpenAIProfile(paths: OpenCodeAuthPaths, profile: OpenAIAuthProfile): Promise<OpenAIAuthProfile>;
|
|
17
|
+
export declare function listOpenAIProfiles(paths: OpenCodeAuthPaths): Promise<SavedProfile[]>;
|
|
18
|
+
export declare function openProfilesFolder(paths: OpenCodeAuthPaths): Promise<void>;
|
|
19
|
+
export declare function parseOpenAIAuthProfile(value: unknown): OpenAIAuthProfile;
|
|
20
|
+
export declare function replaceOpenAIAuthProfile(authJson: AuthJson, selectedProfile: OpenAIAuthProfile): AuthJson;
|
|
21
|
+
export declare function authFileExists(paths: OpenCodeAuthPaths): Promise<boolean>;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { access, chmod, mkdir, readdir, readFile, rename, stat, unlink, writeFile, } from "node:fs/promises";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import { getProfileFileName, OPENAI_PROVIDER_ID, PROFILE_FILE_EXTENSION, PROFILE_FILE_PREFIX, } from "./paths.js";
|
|
6
|
+
import { parseProfileName } from "./profile-name.js";
|
|
7
|
+
const SECRET_FILE_MODE = 0o600;
|
|
8
|
+
const SECRET_DIRECTORY_MODE = 0o700;
|
|
9
|
+
const JSON_INDENT_SPACES = 2;
|
|
10
|
+
export async function getActiveOpenAIProfile(paths) {
|
|
11
|
+
const authJson = await readAuthJson(paths.authFilePath);
|
|
12
|
+
return parseOpenAIAuthProfile(authJson[OPENAI_PROVIDER_ID]);
|
|
13
|
+
}
|
|
14
|
+
export async function saveActiveOpenAIProfile(paths, inputProfileName) {
|
|
15
|
+
const profileName = parseProfileName(inputProfileName);
|
|
16
|
+
const activeProfile = await getActiveOpenAIProfile(paths);
|
|
17
|
+
const profileFilePath = getProfileFilePath(paths, profileName);
|
|
18
|
+
await ensureProfileDirectory(paths.profileDirectoryPath);
|
|
19
|
+
await writeSecretJsonFile(profileFilePath, activeProfile);
|
|
20
|
+
return createSavedProfile(profileName, activeProfile);
|
|
21
|
+
}
|
|
22
|
+
export async function saveActiveOpenAIProfileIfUnsaved(paths) {
|
|
23
|
+
const activeProfile = await getActiveOpenAIProfileIfPresent(paths);
|
|
24
|
+
if (!activeProfile) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const savedProfiles = await listOpenAIProfiles(paths);
|
|
28
|
+
for (const savedProfile of savedProfiles) {
|
|
29
|
+
const profile = await readOpenAIProfile(paths, savedProfile.name);
|
|
30
|
+
if (isSameOpenAIProfile(activeProfile, profile)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const profileName = getNextDefaultProfileName(savedProfiles);
|
|
35
|
+
const profileFilePath = getProfileFilePath(paths, profileName);
|
|
36
|
+
await ensureProfileDirectory(paths.profileDirectoryPath);
|
|
37
|
+
await writeSecretJsonFile(profileFilePath, activeProfile);
|
|
38
|
+
return createSavedProfile(profileName, activeProfile);
|
|
39
|
+
}
|
|
40
|
+
export async function switchActiveOpenAIProfile(paths, inputProfileName) {
|
|
41
|
+
const profileName = parseProfileName(inputProfileName);
|
|
42
|
+
const selectedProfile = await readOpenAIProfile(paths, profileName);
|
|
43
|
+
const authJson = await readAuthJson(paths.authFilePath);
|
|
44
|
+
authJson[OPENAI_PROVIDER_ID] = selectedProfile;
|
|
45
|
+
await writeAuthJsonAtomically(paths.authFilePath, authJson);
|
|
46
|
+
return createSavedProfile(profileName, selectedProfile);
|
|
47
|
+
}
|
|
48
|
+
export async function renameOpenAIProfile(paths, inputCurrentName, inputNextName) {
|
|
49
|
+
const currentName = parseProfileName(inputCurrentName);
|
|
50
|
+
const nextName = parseProfileName(inputNextName);
|
|
51
|
+
if (currentName === nextName) {
|
|
52
|
+
throw new Error("Profile names must be different");
|
|
53
|
+
}
|
|
54
|
+
const currentProfileFilePath = getProfileFilePath(paths, currentName);
|
|
55
|
+
const nextProfileFilePath = getProfileFilePath(paths, nextName);
|
|
56
|
+
const profile = await readOpenAIProfile(paths, currentName);
|
|
57
|
+
if (await fileExists(nextProfileFilePath)) {
|
|
58
|
+
throw new Error(`Profile already exists: ${nextName}`);
|
|
59
|
+
}
|
|
60
|
+
await ensureProfileDirectory(paths.profileDirectoryPath);
|
|
61
|
+
await rename(currentProfileFilePath, nextProfileFilePath);
|
|
62
|
+
await chmod(nextProfileFilePath, SECRET_FILE_MODE);
|
|
63
|
+
return createSavedProfile(nextName, profile);
|
|
64
|
+
}
|
|
65
|
+
export async function setActiveOpenAIProfile(paths, profile) {
|
|
66
|
+
const authJson = await readAuthJson(paths.authFilePath);
|
|
67
|
+
authJson[OPENAI_PROVIDER_ID] = profile;
|
|
68
|
+
await writeAuthJsonAtomically(paths.authFilePath, authJson);
|
|
69
|
+
return profile;
|
|
70
|
+
}
|
|
71
|
+
export async function listOpenAIProfiles(paths) {
|
|
72
|
+
let profileFileNames;
|
|
73
|
+
try {
|
|
74
|
+
profileFileNames = await readdir(paths.profileDirectoryPath);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (isNodeErrorWithCode(error, "ENOENT")) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
const savedProfiles = [];
|
|
83
|
+
for (const profileFileName of profileFileNames) {
|
|
84
|
+
const profileName = parseProfileNameFromFileName(profileFileName);
|
|
85
|
+
if (!profileName) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const profile = await readOpenAIProfile(paths, profileName);
|
|
89
|
+
savedProfiles.push(createSavedProfile(profileName, profile));
|
|
90
|
+
}
|
|
91
|
+
return savedProfiles.sort((leftProfile, rightProfile) => leftProfile.name.localeCompare(rightProfile.name));
|
|
92
|
+
}
|
|
93
|
+
export async function openProfilesFolder(paths) {
|
|
94
|
+
await ensureProfileDirectory(paths.profileDirectoryPath);
|
|
95
|
+
}
|
|
96
|
+
export function parseOpenAIAuthProfile(value) {
|
|
97
|
+
if (!isRecord(value)) {
|
|
98
|
+
throw new Error("OpenAI auth profile is missing or invalid");
|
|
99
|
+
}
|
|
100
|
+
if (value.type !== "oauth") {
|
|
101
|
+
throw new Error("OpenAI auth profile must be an oauth profile");
|
|
102
|
+
}
|
|
103
|
+
if (typeof value.refresh !== "string" || value.refresh.length === 0) {
|
|
104
|
+
throw new Error("OpenAI auth profile is missing a refresh token");
|
|
105
|
+
}
|
|
106
|
+
if (typeof value.access !== "string" || value.access.length === 0) {
|
|
107
|
+
throw new Error("OpenAI auth profile is missing an access token");
|
|
108
|
+
}
|
|
109
|
+
if (typeof value.expires !== "number" || !Number.isFinite(value.expires)) {
|
|
110
|
+
throw new Error("OpenAI auth profile is missing a valid expiration timestamp");
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
export function replaceOpenAIAuthProfile(authJson, selectedProfile) {
|
|
115
|
+
return {
|
|
116
|
+
...authJson,
|
|
117
|
+
[OPENAI_PROVIDER_ID]: selectedProfile,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function createSavedProfile(profileName, profile) {
|
|
121
|
+
if (profile.accountId) {
|
|
122
|
+
return {
|
|
123
|
+
name: profileName,
|
|
124
|
+
accountId: profile.accountId,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
name: profileName,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function readOpenAIProfile(paths, profileName) {
|
|
132
|
+
const profileFilePath = getProfileFilePath(paths, profileName);
|
|
133
|
+
const profileJson = await readJsonFile(profileFilePath);
|
|
134
|
+
return parseOpenAIAuthProfile(profileJson);
|
|
135
|
+
}
|
|
136
|
+
async function readAuthJson(authFilePath) {
|
|
137
|
+
const authJson = await readJsonFile(authFilePath);
|
|
138
|
+
if (!isRecord(authJson)) {
|
|
139
|
+
throw new Error("opencode auth.json must contain a JSON object");
|
|
140
|
+
}
|
|
141
|
+
return authJson;
|
|
142
|
+
}
|
|
143
|
+
async function getActiveOpenAIProfileIfPresent(paths) {
|
|
144
|
+
let authJson;
|
|
145
|
+
try {
|
|
146
|
+
authJson = await readAuthJson(paths.authFilePath);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (isNodeErrorWithCode(error, "ENOENT")) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
if (!(OPENAI_PROVIDER_ID in authJson)) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return parseOpenAIAuthProfile(authJson[OPENAI_PROVIDER_ID]);
|
|
158
|
+
}
|
|
159
|
+
function getNextDefaultProfileName(savedProfiles) {
|
|
160
|
+
const usedProfileNames = new Set(savedProfiles.map((profile) => profile.name));
|
|
161
|
+
let nextProfileIndex = 1;
|
|
162
|
+
while (usedProfileNames.has(`account-${nextProfileIndex}`)) {
|
|
163
|
+
nextProfileIndex += 1;
|
|
164
|
+
}
|
|
165
|
+
return `account-${nextProfileIndex}`;
|
|
166
|
+
}
|
|
167
|
+
function isSameOpenAIProfile(leftProfile, rightProfile) {
|
|
168
|
+
if (leftProfile.accountId && rightProfile.accountId) {
|
|
169
|
+
return leftProfile.accountId === rightProfile.accountId;
|
|
170
|
+
}
|
|
171
|
+
return leftProfile.refresh === rightProfile.refresh;
|
|
172
|
+
}
|
|
173
|
+
async function readJsonFile(filePath) {
|
|
174
|
+
const fileContents = await readFile(filePath, "utf8");
|
|
175
|
+
return JSON.parse(fileContents);
|
|
176
|
+
}
|
|
177
|
+
async function writeAuthJsonAtomically(authFilePath, authJson) {
|
|
178
|
+
const temporaryFilePath = `${authFilePath}.tmp-${randomUUID()}`;
|
|
179
|
+
const fileMode = await getExistingFileMode(authFilePath);
|
|
180
|
+
try {
|
|
181
|
+
await writeFile(temporaryFilePath, `${JSON.stringify(authJson, null, JSON_INDENT_SPACES)}\n`, { mode: fileMode });
|
|
182
|
+
await chmod(temporaryFilePath, fileMode);
|
|
183
|
+
await rename(temporaryFilePath, authFilePath);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
await unlinkIfExists(temporaryFilePath);
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function writeSecretJsonFile(filePath, value) {
|
|
191
|
+
await writeFile(filePath, `${JSON.stringify(value, null, JSON_INDENT_SPACES)}\n`, { mode: SECRET_FILE_MODE });
|
|
192
|
+
await chmod(filePath, SECRET_FILE_MODE);
|
|
193
|
+
}
|
|
194
|
+
async function ensureProfileDirectory(profileDirectoryPath) {
|
|
195
|
+
await mkdir(profileDirectoryPath, {
|
|
196
|
+
recursive: true,
|
|
197
|
+
mode: SECRET_DIRECTORY_MODE,
|
|
198
|
+
});
|
|
199
|
+
await chmod(profileDirectoryPath, SECRET_DIRECTORY_MODE);
|
|
200
|
+
}
|
|
201
|
+
async function getExistingFileMode(filePath) {
|
|
202
|
+
try {
|
|
203
|
+
const fileStats = await stat(filePath);
|
|
204
|
+
return fileStats.mode & 0o777;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (isNodeErrorWithCode(error, "ENOENT")) {
|
|
208
|
+
return SECRET_FILE_MODE;
|
|
209
|
+
}
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function unlinkIfExists(filePath) {
|
|
214
|
+
try {
|
|
215
|
+
await unlink(filePath);
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (!isNodeErrorWithCode(error, "ENOENT")) {
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function fileExists(filePath) {
|
|
224
|
+
try {
|
|
225
|
+
await access(filePath, constants.F_OK);
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
if (isNodeErrorWithCode(error, "ENOENT")) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function getProfileFilePath(paths, profileName) {
|
|
236
|
+
return join(paths.profileDirectoryPath, getProfileFileName(profileName));
|
|
237
|
+
}
|
|
238
|
+
function parseProfileNameFromFileName(profileFileName) {
|
|
239
|
+
const safeFileName = basename(profileFileName);
|
|
240
|
+
if (!safeFileName.startsWith(PROFILE_FILE_PREFIX) ||
|
|
241
|
+
!safeFileName.endsWith(PROFILE_FILE_EXTENSION)) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
const profileName = safeFileName.slice(PROFILE_FILE_PREFIX.length, -PROFILE_FILE_EXTENSION.length);
|
|
245
|
+
try {
|
|
246
|
+
return parseProfileName(profileName);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function isRecord(value) {
|
|
253
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
254
|
+
}
|
|
255
|
+
function isNodeErrorWithCode(error, code) {
|
|
256
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
257
|
+
}
|
|
258
|
+
export async function authFileExists(paths) {
|
|
259
|
+
try {
|
|
260
|
+
await access(paths.authFilePath, constants.R_OK);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
if (isNodeErrorWithCode(error, "ENOENT")) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=auth-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-store.js","sourceRoot":"","sources":["../src/auth-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OpenAIAuthProfile } from "./auth-store.js";
|
|
2
|
+
export type ManualCodexLogin = {
|
|
3
|
+
url: string;
|
|
4
|
+
state: string;
|
|
5
|
+
verifier: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function createManualCodexLogin(): Promise<ManualCodexLogin>;
|
|
8
|
+
export declare function completeManualCodexLogin(login: ManualCodexLogin, callbackUrlOrCode: string): Promise<OpenAIAuthProfile>;
|
|
9
|
+
export declare function extractAuthorizationCode(callbackUrlOrCode: string, expectedState: string): string;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
3
|
+
const ISSUER = "https://auth.openai.com";
|
|
4
|
+
const REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
5
|
+
const PKCE_VERIFIER_LENGTH = 43;
|
|
6
|
+
const STATE_BYTE_LENGTH = 32;
|
|
7
|
+
const OAUTH_TOKEN_PATH = "/oauth/token";
|
|
8
|
+
export async function createManualCodexLogin() {
|
|
9
|
+
const pkce = createPkceCodes();
|
|
10
|
+
const state = createState();
|
|
11
|
+
return {
|
|
12
|
+
url: buildAuthorizeUrl(pkce.challenge, state),
|
|
13
|
+
state,
|
|
14
|
+
verifier: pkce.verifier,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export async function completeManualCodexLogin(login, callbackUrlOrCode) {
|
|
18
|
+
const code = extractAuthorizationCode(callbackUrlOrCode, login.state);
|
|
19
|
+
const tokenResponse = await exchangeCodeForTokens(code, login.verifier);
|
|
20
|
+
const accountId = extractAccountId(tokenResponse);
|
|
21
|
+
return {
|
|
22
|
+
type: "oauth",
|
|
23
|
+
refresh: tokenResponse.refresh_token,
|
|
24
|
+
access: tokenResponse.access_token,
|
|
25
|
+
expires: Date.now() + (tokenResponse.expires_in ?? 3600) * 1000,
|
|
26
|
+
...(accountId ? { accountId } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function extractAuthorizationCode(callbackUrlOrCode, expectedState) {
|
|
30
|
+
const trimmedInput = callbackUrlOrCode.trim();
|
|
31
|
+
if (trimmedInput.length === 0) {
|
|
32
|
+
throw new Error("Paste the callback URL or authorization code");
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const callbackUrl = new URL(trimmedInput);
|
|
36
|
+
const callbackError = callbackUrl.searchParams.get("error");
|
|
37
|
+
if (callbackError) {
|
|
38
|
+
throw new Error(callbackUrl.searchParams.get("error_description") ?? callbackError);
|
|
39
|
+
}
|
|
40
|
+
const state = callbackUrl.searchParams.get("state");
|
|
41
|
+
if (state !== expectedState) {
|
|
42
|
+
throw new Error("Callback URL state does not match this login attempt");
|
|
43
|
+
}
|
|
44
|
+
const code = callbackUrl.searchParams.get("code");
|
|
45
|
+
if (!code) {
|
|
46
|
+
throw new Error("Callback URL is missing an authorization code");
|
|
47
|
+
}
|
|
48
|
+
return code;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof TypeError) {
|
|
52
|
+
return trimmedInput;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function createPkceCodes() {
|
|
58
|
+
const verifier = generateRandomString(PKCE_VERIFIER_LENGTH);
|
|
59
|
+
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
60
|
+
return { verifier, challenge };
|
|
61
|
+
}
|
|
62
|
+
function createState() {
|
|
63
|
+
return base64UrlEncode(randomBytes(STATE_BYTE_LENGTH));
|
|
64
|
+
}
|
|
65
|
+
function buildAuthorizeUrl(challenge, state) {
|
|
66
|
+
const params = new URLSearchParams({
|
|
67
|
+
response_type: "code",
|
|
68
|
+
client_id: CLIENT_ID,
|
|
69
|
+
redirect_uri: REDIRECT_URI,
|
|
70
|
+
scope: "openid profile email offline_access",
|
|
71
|
+
code_challenge: challenge,
|
|
72
|
+
code_challenge_method: "S256",
|
|
73
|
+
id_token_add_organizations: "true",
|
|
74
|
+
codex_cli_simplified_flow: "true",
|
|
75
|
+
state,
|
|
76
|
+
originator: "opencode",
|
|
77
|
+
});
|
|
78
|
+
return `${ISSUER}/oauth/authorize?${params.toString()}`;
|
|
79
|
+
}
|
|
80
|
+
async function exchangeCodeForTokens(code, verifier) {
|
|
81
|
+
const response = await fetch(`${ISSUER}${OAUTH_TOKEN_PATH}`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
84
|
+
body: new URLSearchParams({
|
|
85
|
+
grant_type: "authorization_code",
|
|
86
|
+
code,
|
|
87
|
+
redirect_uri: REDIRECT_URI,
|
|
88
|
+
client_id: CLIENT_ID,
|
|
89
|
+
code_verifier: verifier,
|
|
90
|
+
}).toString(),
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error(`Token exchange failed: ${response.status}`);
|
|
94
|
+
}
|
|
95
|
+
return (await response.json());
|
|
96
|
+
}
|
|
97
|
+
function extractAccountId(tokenResponse) {
|
|
98
|
+
if (tokenResponse.id_token) {
|
|
99
|
+
return extractAccountIdFromToken(tokenResponse.id_token);
|
|
100
|
+
}
|
|
101
|
+
return extractAccountIdFromToken(tokenResponse.access_token);
|
|
102
|
+
}
|
|
103
|
+
function extractAccountIdFromToken(token) {
|
|
104
|
+
const tokenParts = token.split(".");
|
|
105
|
+
if (tokenParts.length !== 3 || !tokenParts[1]) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const claims = JSON.parse(Buffer.from(tokenParts[1], "base64url").toString());
|
|
110
|
+
return (claims.chatgpt_account_id ??
|
|
111
|
+
claims["https://api.openai.com/auth"]?.chatgpt_account_id ??
|
|
112
|
+
claims.organizations?.[0]?.id);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function generateRandomString(length) {
|
|
119
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
120
|
+
const bytes = randomBytes(length);
|
|
121
|
+
return Array.from(bytes, (byte) => chars[byte % chars.length]).join("");
|
|
122
|
+
}
|
|
123
|
+
function base64UrlEncode(buffer) {
|
|
124
|
+
return buffer.toString("base64url");
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=codex-oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-oauth.js","sourceRoot":"","sources":["../src/codex-oauth.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
3
|
+
export declare const id = "opencode-openai-profiles";
|
|
4
|
+
export declare const OpenAIAccountSwitcherPlugin: Plugin;
|
|
5
|
+
export default OpenAIAccountSwitcherPlugin;
|
|
6
|
+
export declare const tui: (inputApi: TuiPluginApi | Record<string, unknown>) => Promise<void>;
|