@harars/opencode-switch-openai-auth-plugin 0.1.2 → 0.1.4
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 +25 -52
- package/dist/index.js +37 -8
- package/dist/tui.js +37 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,57 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
<img width="1107" height="525" alt="screenshot" src="https://github.com/user-attachments/assets/511a6118-e470-4955-b5a0-c8d9672d060b" />
|
|
4
4
|
|
|
5
|
-
OpenCode TUI plugin for
|
|
6
|
-
|
|
5
|
+
OpenCode TUI plugin for switching between saved OpenAI OAuth accounts with a single `/switch` command.
|
|
7
6
|
|
|
8
7
|
## Features
|
|
9
8
|
|
|
10
|
-
- one `/switch` command for login,
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- keeps saved-account removal separate from active
|
|
15
|
-
- supports multiple credentials for the same email by keying entries from the refresh token hash
|
|
16
|
-
|
|
17
|
-
## Behavior
|
|
18
|
-
|
|
19
|
-
The `/switch` dialog can show:
|
|
9
|
+
- one `/switch` command for login, switching, and removal
|
|
10
|
+
- uses the native OpenCode OpenAI OAuth flow
|
|
11
|
+
- supports multiple saved accounts
|
|
12
|
+
- marks the current account in the picker
|
|
13
|
+
- keeps saved-account removal separate from the active session
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
- saved accounts
|
|
23
|
-
- `logout`
|
|
15
|
+
## Installation
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
Install the plugin with the OpenCode plugin command:
|
|
26
18
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
`login` behavior:
|
|
19
|
+
```bash
|
|
20
|
+
opencode plugin @harars/opencode-switch-openai-auth-plugin
|
|
21
|
+
```
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
- rereads the active OpenAI auth after OAuth completes
|
|
34
|
-
- saves that credential into `auth-switch/accounts.json`
|
|
23
|
+
## Usage
|
|
35
24
|
|
|
36
|
-
|
|
25
|
+
Run the command below in OpenCode:
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
```text
|
|
28
|
+
/switch
|
|
29
|
+
```
|
|
40
30
|
|
|
41
|
-
|
|
31
|
+
From there you can:
|
|
42
32
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
33
|
+
- sign in with another OpenAI account
|
|
34
|
+
- switch to a saved account
|
|
35
|
+
- remove a saved account
|
|
45
36
|
|
|
46
37
|
## Storage
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
Saved accounts are stored in a plugin-managed JSON file:
|
|
49
40
|
|
|
50
41
|
- Linux: `~/.local/share/opencode/auth-switch/accounts.json`
|
|
51
42
|
- macOS: `~/Library/Application Support/opencode/auth-switch/accounts.json`
|
|
52
43
|
- Windows: `%LOCALAPPDATA%/opencode/auth-switch/accounts.json`
|
|
53
44
|
|
|
54
|
-
`OPENCODE_TEST_HOME` is supported for tests.
|
|
55
|
-
|
|
56
45
|
## Local Development
|
|
57
46
|
|
|
58
47
|
Install dependencies:
|
|
@@ -68,28 +57,13 @@ bun test
|
|
|
68
57
|
bunx tsc -p tsconfig.json --noEmit
|
|
69
58
|
```
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
After publishing to npm, add the package name directly to your OpenCode `tui.json`.
|
|
60
|
+
Build the package output:
|
|
74
61
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```json
|
|
78
|
-
{
|
|
79
|
-
"plugin": [
|
|
80
|
-
"@harars/opencode-switch-openai-auth-plugin"
|
|
81
|
-
],
|
|
82
|
-
"plugin_enabled": {
|
|
83
|
-
"harars.switch-auth": true
|
|
84
|
-
}
|
|
85
|
-
}
|
|
62
|
+
```bash
|
|
63
|
+
bun run build
|
|
86
64
|
```
|
|
87
65
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
For local development, you can still point `tui.json` at a local file path.
|
|
91
|
-
|
|
92
|
-
Example local file setup:
|
|
66
|
+
For local development, point your workspace `tui.json` at the local source entry:
|
|
93
67
|
|
|
94
68
|
```json
|
|
95
69
|
{
|
|
@@ -105,5 +79,4 @@ Example local file setup:
|
|
|
105
79
|
## Package
|
|
106
80
|
|
|
107
81
|
- package name: `@harars/opencode-switch-openai-auth-plugin`
|
|
108
|
-
- plugin id: `harars.switch-auth`
|
|
109
82
|
- license: MIT
|
package/dist/index.js
CHANGED
|
@@ -437,6 +437,37 @@ function same(prev, next) {
|
|
|
437
437
|
return false;
|
|
438
438
|
return prev.refresh === next.refresh;
|
|
439
439
|
}
|
|
440
|
+
function unwrap(input) {
|
|
441
|
+
if (!input || typeof input !== "object") {
|
|
442
|
+
return {
|
|
443
|
+
ok: true,
|
|
444
|
+
data: input
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
if ("error" in input && input.error !== undefined) {
|
|
448
|
+
return {
|
|
449
|
+
ok: false
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
if ("data" in input) {
|
|
453
|
+
return input.data === undefined ? {
|
|
454
|
+
ok: false
|
|
455
|
+
} : {
|
|
456
|
+
ok: true,
|
|
457
|
+
data: input.data
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
ok: true,
|
|
462
|
+
data: input
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
async function authMethods(api) {
|
|
466
|
+
const res = unwrap(await api.client.provider.auth());
|
|
467
|
+
if (!res.ok)
|
|
468
|
+
return [];
|
|
469
|
+
return res.data.openai ?? [];
|
|
470
|
+
}
|
|
440
471
|
function bind(api, authz) {
|
|
441
472
|
useKeyboard((evt) => {
|
|
442
473
|
if (evt.name !== "c" || evt.ctrl || evt.meta)
|
|
@@ -668,17 +699,15 @@ async function auto(api, index, method, authz) {
|
|
|
668
699
|
}
|
|
669
700
|
async function hasLogin(api) {
|
|
670
701
|
try {
|
|
671
|
-
|
|
672
|
-
const methods = res.data?.openai ?? [];
|
|
673
|
-
return methods.some((item) => item.type === "oauth");
|
|
702
|
+
return (await authMethods(api)).some((item) => item.type === "oauth");
|
|
674
703
|
} catch {
|
|
675
704
|
return false;
|
|
676
705
|
}
|
|
677
706
|
}
|
|
678
707
|
async function loginOpenAI(api) {
|
|
679
708
|
try {
|
|
680
|
-
const
|
|
681
|
-
const methods =
|
|
709
|
+
const available = await authMethods(api);
|
|
710
|
+
const methods = available.map((method2, index2) => ({
|
|
682
711
|
method: method2,
|
|
683
712
|
index: index2
|
|
684
713
|
})).filter((item) => item.method.type === "oauth");
|
|
@@ -697,12 +726,12 @@ async function loginOpenAI(api) {
|
|
|
697
726
|
const inputs = await prompts(api, method.label, method);
|
|
698
727
|
if (method.prompts?.length && !inputs)
|
|
699
728
|
return false;
|
|
700
|
-
const authz = await api.client.provider.oauth.authorize({
|
|
729
|
+
const authz = unwrap(await api.client.provider.oauth.authorize({
|
|
701
730
|
providerID: "openai",
|
|
702
731
|
method: picked.index,
|
|
703
732
|
inputs
|
|
704
|
-
});
|
|
705
|
-
if (
|
|
733
|
+
}));
|
|
734
|
+
if (!authz.ok) {
|
|
706
735
|
api.ui.toast({
|
|
707
736
|
variant: "error",
|
|
708
737
|
message: "Login failed"
|
package/dist/tui.js
CHANGED
|
@@ -437,6 +437,37 @@ function same(prev, next) {
|
|
|
437
437
|
return false;
|
|
438
438
|
return prev.refresh === next.refresh;
|
|
439
439
|
}
|
|
440
|
+
function unwrap(input) {
|
|
441
|
+
if (!input || typeof input !== "object") {
|
|
442
|
+
return {
|
|
443
|
+
ok: true,
|
|
444
|
+
data: input
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
if ("error" in input && input.error !== undefined) {
|
|
448
|
+
return {
|
|
449
|
+
ok: false
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
if ("data" in input) {
|
|
453
|
+
return input.data === undefined ? {
|
|
454
|
+
ok: false
|
|
455
|
+
} : {
|
|
456
|
+
ok: true,
|
|
457
|
+
data: input.data
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
ok: true,
|
|
462
|
+
data: input
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
async function authMethods(api) {
|
|
466
|
+
const res = unwrap(await api.client.provider.auth());
|
|
467
|
+
if (!res.ok)
|
|
468
|
+
return [];
|
|
469
|
+
return res.data.openai ?? [];
|
|
470
|
+
}
|
|
440
471
|
function bind(api, authz) {
|
|
441
472
|
useKeyboard((evt) => {
|
|
442
473
|
if (evt.name !== "c" || evt.ctrl || evt.meta)
|
|
@@ -668,17 +699,15 @@ async function auto(api, index, method, authz) {
|
|
|
668
699
|
}
|
|
669
700
|
async function hasLogin(api) {
|
|
670
701
|
try {
|
|
671
|
-
|
|
672
|
-
const methods = res.data?.openai ?? [];
|
|
673
|
-
return methods.some((item) => item.type === "oauth");
|
|
702
|
+
return (await authMethods(api)).some((item) => item.type === "oauth");
|
|
674
703
|
} catch {
|
|
675
704
|
return false;
|
|
676
705
|
}
|
|
677
706
|
}
|
|
678
707
|
async function loginOpenAI(api) {
|
|
679
708
|
try {
|
|
680
|
-
const
|
|
681
|
-
const methods =
|
|
709
|
+
const available = await authMethods(api);
|
|
710
|
+
const methods = available.map((method2, index2) => ({
|
|
682
711
|
method: method2,
|
|
683
712
|
index: index2
|
|
684
713
|
})).filter((item) => item.method.type === "oauth");
|
|
@@ -697,12 +726,12 @@ async function loginOpenAI(api) {
|
|
|
697
726
|
const inputs = await prompts(api, method.label, method);
|
|
698
727
|
if (method.prompts?.length && !inputs)
|
|
699
728
|
return false;
|
|
700
|
-
const authz = await api.client.provider.oauth.authorize({
|
|
729
|
+
const authz = unwrap(await api.client.provider.oauth.authorize({
|
|
701
730
|
providerID: "openai",
|
|
702
731
|
method: picked.index,
|
|
703
732
|
inputs
|
|
704
|
-
});
|
|
705
|
-
if (
|
|
733
|
+
}));
|
|
734
|
+
if (!authz.ok) {
|
|
706
735
|
api.ui.toast({
|
|
707
736
|
variant: "error",
|
|
708
737
|
message: "Login failed"
|