@harars/opencode-switch-openai-auth-plugin 0.1.0 → 0.1.2
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 +4 -1
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/index.js +922 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/tui.js +922 -0
- package/package.json +6 -5
- package/src/format.ts +0 -27
- package/src/index.ts +0 -1
- package/src/login.tsx +0 -297
- package/src/parse.ts +0 -73
- package/src/paths.ts +0 -27
- package/src/store.ts +0 -244
- package/src/tui.tsx +0 -194
- package/src/types.ts +0 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harars/opencode-switch-openai-auth-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "OpenCode TUI plugin for switching saved OpenAI OAuth accounts",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
23
|
"license": "MIT",
|
|
24
|
-
"main": "./
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
25
|
"exports": {
|
|
26
|
-
".": "./
|
|
27
|
-
"./tui": "./
|
|
26
|
+
".": "./dist/index.js",
|
|
27
|
+
"./tui": "./dist/tui.js"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"opencode": "^1.0.0"
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
}]],
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "bun ./scripts/build.ts",
|
|
37
|
+
"prepack": "bun run build",
|
|
37
38
|
"typecheck": "bunx tsc -p tsconfig.json --noEmit",
|
|
38
39
|
"test": "bun test"
|
|
39
40
|
},
|
|
40
41
|
"files": [
|
|
41
|
-
"
|
|
42
|
+
"dist",
|
|
42
43
|
"README.md",
|
|
43
44
|
"LICENSE"
|
|
44
45
|
],
|
package/src/format.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { StoredAccount } from "./types"
|
|
2
|
-
|
|
3
|
-
function short(id?: string) {
|
|
4
|
-
if (!id) return
|
|
5
|
-
if (id.length <= 12) return id
|
|
6
|
-
return `${id.slice(0, 6)}...${id.slice(-4)}`
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function accountTitle(account: StoredAccount) {
|
|
10
|
-
return account.email || account.accountId || account.id
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function accountDescription(account: StoredAccount, current: boolean) {
|
|
14
|
-
return short(account.accountId)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function accountFooter(current: boolean) {
|
|
18
|
-
return current ? "Current" : undefined
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function loginTitle() {
|
|
22
|
-
return "login"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function logoutTitle(has: boolean) {
|
|
26
|
-
return has ? "logout" : "logout unavailable"
|
|
27
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./tui"
|
package/src/login.tsx
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/solid */
|
|
2
|
-
import { TextAttributes } from "@opentui/core"
|
|
3
|
-
import type { TuiPluginApi } from "@opencode-ai/plugin/tui"
|
|
4
|
-
import { useKeyboard } from "@opentui/solid"
|
|
5
|
-
import type { OAuthAuthz, ProviderMethod, ProviderPrompt } from "./types"
|
|
6
|
-
import { readCurrentAuth, upsertSavedAccount } from "./store"
|
|
7
|
-
|
|
8
|
-
type OAuthMethod = {
|
|
9
|
-
index: number
|
|
10
|
-
method: ProviderMethod
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function visible(prompt: ProviderPrompt, values: Record<string, string>) {
|
|
14
|
-
if (!prompt.when) return true
|
|
15
|
-
const cur = values[prompt.when.key]
|
|
16
|
-
if (prompt.when.op === "eq") return cur === prompt.when.value
|
|
17
|
-
return cur !== prompt.when.value
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function same(prev: Awaited<ReturnType<typeof readCurrentAuth>>, next: Awaited<ReturnType<typeof readCurrentAuth>>) {
|
|
21
|
-
if (!prev || !next) return false
|
|
22
|
-
return prev.refresh === next.refresh
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function clip(text: string) {
|
|
26
|
-
if (process.stdout.isTTY) {
|
|
27
|
-
const base64 = Buffer.from(text).toString("base64")
|
|
28
|
-
const osc52 = `\x1b]52;c;${base64}\x07`
|
|
29
|
-
const seq = process.env.TMUX || process.env.STY ? `\x1bPtmux;\x1b${osc52}\x1b\\` : osc52
|
|
30
|
-
process.stdout.write(seq)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const cmds = process.platform === "darwin"
|
|
34
|
-
? [["pbcopy"]]
|
|
35
|
-
: process.platform === "win32"
|
|
36
|
-
? [["clip"]]
|
|
37
|
-
: [["wl-copy"], ["xclip", "-selection", "clipboard"], ["xsel", "--clipboard", "--input"]]
|
|
38
|
-
|
|
39
|
-
return Promise.any(
|
|
40
|
-
cmds.map(async (cmd) => {
|
|
41
|
-
const proc = Bun.spawn({
|
|
42
|
-
cmd,
|
|
43
|
-
stdin: "pipe",
|
|
44
|
-
stdout: "ignore",
|
|
45
|
-
stderr: "ignore",
|
|
46
|
-
})
|
|
47
|
-
await proc.stdin.write(new TextEncoder().encode(text))
|
|
48
|
-
proc.stdin.end()
|
|
49
|
-
const code = await proc.exited
|
|
50
|
-
if (code !== 0) throw new Error("copy failed")
|
|
51
|
-
}),
|
|
52
|
-
).catch(() => {})
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function target(authz: OAuthAuthz) {
|
|
56
|
-
return authz.instructions.match(/[A-Z0-9]{4}-[A-Z0-9]{4,5}/)?.[0] ?? authz.url
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function bind(api: TuiPluginApi, authz: OAuthAuthz) {
|
|
60
|
-
useKeyboard((evt) => {
|
|
61
|
-
if (evt.name !== "c" || evt.ctrl || evt.meta) return
|
|
62
|
-
evt.preventDefault()
|
|
63
|
-
evt.stopPropagation()
|
|
64
|
-
void clip(target(authz))
|
|
65
|
-
.then(() => api.ui.toast({ variant: "info", message: "Copied to clipboard" }))
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function WaitView(props: { api: TuiPluginApi; title: string; authz: OAuthAuthz }) {
|
|
70
|
-
bind(props.api, props.authz)
|
|
71
|
-
return (
|
|
72
|
-
<box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
|
|
73
|
-
<text attributes={TextAttributes.BOLD}>{props.title}</text>
|
|
74
|
-
<text>{props.authz.instructions}</text>
|
|
75
|
-
<text>{props.authz.url}</text>
|
|
76
|
-
<text>Waiting for authorization...</text>
|
|
77
|
-
<text>Press c to copy the link</text>
|
|
78
|
-
</box>
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function CodePrompt(props: {
|
|
83
|
-
api: TuiPluginApi
|
|
84
|
-
title: string
|
|
85
|
-
authz: OAuthAuthz
|
|
86
|
-
onConfirm: (value: string) => void
|
|
87
|
-
onCancel: () => void
|
|
88
|
-
}) {
|
|
89
|
-
bind(props.api, props.authz)
|
|
90
|
-
return (
|
|
91
|
-
<props.api.ui.DialogPrompt
|
|
92
|
-
title={props.title}
|
|
93
|
-
placeholder="Authorization code"
|
|
94
|
-
onConfirm={props.onConfirm}
|
|
95
|
-
onCancel={props.onCancel}
|
|
96
|
-
description={() => (
|
|
97
|
-
<box gap={1}>
|
|
98
|
-
<text>{props.authz.instructions}</text>
|
|
99
|
-
<text>{props.authz.url}</text>
|
|
100
|
-
<text>Press c to copy the code</text>
|
|
101
|
-
</box>
|
|
102
|
-
)}
|
|
103
|
-
/>
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function wait(api: TuiPluginApi, title: string, authz: OAuthAuthz, run: () => Promise<void>) {
|
|
108
|
-
api.ui.dialog.replace(() => <WaitView api={api} title={title} authz={authz} />)
|
|
109
|
-
void run()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function choose(api: TuiPluginApi, methods: OAuthMethod[]) {
|
|
113
|
-
if (methods.length === 1) return 0
|
|
114
|
-
return await new Promise<number | null>((resolve) => {
|
|
115
|
-
api.ui.dialog.replace(
|
|
116
|
-
() => (
|
|
117
|
-
<api.ui.DialogSelect
|
|
118
|
-
title="Select auth method"
|
|
119
|
-
options={methods.map((item, index) => ({ title: item.method.label, value: index }))}
|
|
120
|
-
onSelect={(item) => resolve(item.value)}
|
|
121
|
-
/>
|
|
122
|
-
),
|
|
123
|
-
() => resolve(null),
|
|
124
|
-
)
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function ask(api: TuiPluginApi, title: string, prompt: ProviderPrompt) {
|
|
129
|
-
if (prompt.type === "text") {
|
|
130
|
-
return await new Promise<string | null>((resolve) => {
|
|
131
|
-
api.ui.dialog.replace(
|
|
132
|
-
() => (
|
|
133
|
-
<box paddingLeft={2} paddingRight={2} paddingTop={2} paddingBottom={2}>
|
|
134
|
-
<api.ui.DialogPrompt
|
|
135
|
-
title={title}
|
|
136
|
-
placeholder={prompt.placeholder}
|
|
137
|
-
onConfirm={(value) => resolve(value)}
|
|
138
|
-
onCancel={() => resolve(null)}
|
|
139
|
-
description={() => <text>{prompt.message}</text>}
|
|
140
|
-
/>
|
|
141
|
-
</box>
|
|
142
|
-
),
|
|
143
|
-
() => resolve(null),
|
|
144
|
-
)
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
return await new Promise<string | null>((resolve) => {
|
|
148
|
-
api.ui.dialog.replace(
|
|
149
|
-
() => (
|
|
150
|
-
<box paddingLeft={2} paddingRight={2} paddingTop={2} paddingBottom={2}>
|
|
151
|
-
<api.ui.DialogSelect
|
|
152
|
-
title={prompt.message}
|
|
153
|
-
options={(prompt.options ?? []).map((item) => ({
|
|
154
|
-
title: item.label,
|
|
155
|
-
value: item.value,
|
|
156
|
-
description: item.hint,
|
|
157
|
-
}))}
|
|
158
|
-
onSelect={(item) => resolve(item.value)}
|
|
159
|
-
/>
|
|
160
|
-
</box>
|
|
161
|
-
),
|
|
162
|
-
() => resolve(null),
|
|
163
|
-
)
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async function prompts(api: TuiPluginApi, title: string, method: ProviderMethod) {
|
|
168
|
-
const values: Record<string, string> = {}
|
|
169
|
-
for (const prompt of method.prompts ?? []) {
|
|
170
|
-
if (!visible(prompt, values)) continue
|
|
171
|
-
const value = await ask(api, title, prompt)
|
|
172
|
-
if (value == null) return
|
|
173
|
-
values[prompt.key] = value
|
|
174
|
-
}
|
|
175
|
-
return values
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function save(api: TuiPluginApi, prev?: Awaited<ReturnType<typeof readCurrentAuth>>) {
|
|
179
|
-
let activeUpdated = false
|
|
180
|
-
try {
|
|
181
|
-
const client = api.client as TuiPluginApi["client"] & {
|
|
182
|
-
sync?: { bootstrap?: () => Promise<void> }
|
|
183
|
-
}
|
|
184
|
-
if (typeof api.client.instance.dispose === "function") {
|
|
185
|
-
await api.client.instance.dispose()
|
|
186
|
-
}
|
|
187
|
-
if (typeof client.sync?.bootstrap === "function") {
|
|
188
|
-
await client.sync.bootstrap()
|
|
189
|
-
}
|
|
190
|
-
const auth = await readCurrentAuth()
|
|
191
|
-
if (!auth) {
|
|
192
|
-
api.ui.toast({ variant: "error", message: "Login completed, but saved account could not be loaded" })
|
|
193
|
-
return false
|
|
194
|
-
}
|
|
195
|
-
activeUpdated = true
|
|
196
|
-
if (same(prev, auth)) {
|
|
197
|
-
api.ui.toast({ variant: "warning", message: "Login completed, but OpenAI account did not change" })
|
|
198
|
-
return false
|
|
199
|
-
}
|
|
200
|
-
await upsertSavedAccount(auth)
|
|
201
|
-
return true
|
|
202
|
-
} catch {
|
|
203
|
-
api.ui.toast({
|
|
204
|
-
variant: "error",
|
|
205
|
-
message: activeUpdated
|
|
206
|
-
? "Login completed, but saving the account failed"
|
|
207
|
-
: "Login completed, but account sync failed",
|
|
208
|
-
})
|
|
209
|
-
return false
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
async function code(api: TuiPluginApi, index: number, method: ProviderMethod, authz: OAuthAuthz) {
|
|
214
|
-
const prev = await readCurrentAuth()
|
|
215
|
-
const ok = await new Promise<boolean>((resolve) => {
|
|
216
|
-
api.ui.dialog.replace(
|
|
217
|
-
() => (
|
|
218
|
-
<CodePrompt
|
|
219
|
-
api={api}
|
|
220
|
-
title={method.label}
|
|
221
|
-
authz={authz}
|
|
222
|
-
onConfirm={async (value) => {
|
|
223
|
-
const res = await api.client.provider.oauth.callback({ providerID: "openai", method: index, code: value })
|
|
224
|
-
resolve(!res.error)
|
|
225
|
-
}}
|
|
226
|
-
onCancel={() => resolve(false)}
|
|
227
|
-
/>
|
|
228
|
-
),
|
|
229
|
-
() => resolve(false),
|
|
230
|
-
)
|
|
231
|
-
})
|
|
232
|
-
if (!ok) {
|
|
233
|
-
api.ui.toast({ variant: "error", message: "Login failed" })
|
|
234
|
-
return false
|
|
235
|
-
}
|
|
236
|
-
return save(api, prev)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
async function auto(api: TuiPluginApi, index: number, method: ProviderMethod, authz: OAuthAuthz) {
|
|
240
|
-
const prev = await readCurrentAuth()
|
|
241
|
-
const ok = await new Promise<boolean>((resolve) => {
|
|
242
|
-
wait(api, method.label, authz, async () => {
|
|
243
|
-
const res = await api.client.provider.oauth.callback({ providerID: "openai", method: index })
|
|
244
|
-
resolve(!res.error)
|
|
245
|
-
})
|
|
246
|
-
})
|
|
247
|
-
if (!ok) {
|
|
248
|
-
api.ui.toast({ variant: "error", message: "Login failed" })
|
|
249
|
-
return false
|
|
250
|
-
}
|
|
251
|
-
return save(api, prev)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export async function hasLogin(api: TuiPluginApi) {
|
|
255
|
-
try {
|
|
256
|
-
const res = await api.client.provider.auth()
|
|
257
|
-
const methods = (res.data?.openai ?? []) as ProviderMethod[]
|
|
258
|
-
return methods.some((item) => item.type === "oauth")
|
|
259
|
-
} catch {
|
|
260
|
-
return false
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export async function loginOpenAI(api: TuiPluginApi) {
|
|
265
|
-
try {
|
|
266
|
-
const auth = await api.client.provider.auth()
|
|
267
|
-
const methods = ((auth.data?.openai ?? []) as ProviderMethod[])
|
|
268
|
-
.map((method, index) => ({ method, index }))
|
|
269
|
-
.filter((item) => item.method.type === "oauth")
|
|
270
|
-
if (!methods.length) {
|
|
271
|
-
api.ui.toast({ variant: "error", message: "OpenAI OAuth login unavailable" })
|
|
272
|
-
return false
|
|
273
|
-
}
|
|
274
|
-
const index = await choose(api, methods)
|
|
275
|
-
if (index == null) return false
|
|
276
|
-
const picked = methods[index]
|
|
277
|
-
const method = picked.method
|
|
278
|
-
const inputs = await prompts(api, method.label, method)
|
|
279
|
-
if (method.prompts?.length && !inputs) return false
|
|
280
|
-
const authz = await api.client.provider.oauth.authorize({
|
|
281
|
-
providerID: "openai",
|
|
282
|
-
method: picked.index,
|
|
283
|
-
inputs,
|
|
284
|
-
})
|
|
285
|
-
if (authz.error || !authz.data) {
|
|
286
|
-
api.ui.toast({ variant: "error", message: "Login failed" })
|
|
287
|
-
return false
|
|
288
|
-
}
|
|
289
|
-
if (authz.data.method === "code") return code(api, picked.index, method, authz.data as OAuthAuthz)
|
|
290
|
-
if (authz.data.method === "auto") return auto(api, picked.index, method, authz.data as OAuthAuthz)
|
|
291
|
-
api.ui.toast({ variant: "error", message: "Unsupported auth method" })
|
|
292
|
-
return false
|
|
293
|
-
} catch {
|
|
294
|
-
api.ui.toast({ variant: "error", message: "Login failed" })
|
|
295
|
-
return false
|
|
296
|
-
}
|
|
297
|
-
}
|
package/src/parse.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto"
|
|
2
|
-
import type { Claims, OpenAIAuth, StoreEntry } from "./types"
|
|
3
|
-
|
|
4
|
-
function text(v: unknown) {
|
|
5
|
-
return typeof v === "string" && v.trim() ? v : undefined
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function obj(v: unknown) {
|
|
9
|
-
return v && typeof v === "object" ? (v as Record<string, unknown>) : undefined
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function parseJwtClaims(token: string): Claims {
|
|
13
|
-
const part = token.split(".")[1]
|
|
14
|
-
if (!part) return {}
|
|
15
|
-
try {
|
|
16
|
-
return JSON.parse(Buffer.from(part, "base64url").toString("utf8")) as Claims
|
|
17
|
-
} catch {
|
|
18
|
-
return {}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function extractEmail(auth: OpenAIAuth) {
|
|
23
|
-
const claims = parseJwtClaims(auth.access)
|
|
24
|
-
const direct = text(claims["https://api.openai.com/profile.email"])
|
|
25
|
-
if (direct) return direct
|
|
26
|
-
return text(obj(claims["https://api.openai.com/profile"] as unknown)?.email)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function extractPlan(auth: OpenAIAuth) {
|
|
30
|
-
return text(parseJwtClaims(auth.access)["https://api.openai.com/auth.chatgpt_plan_type"])
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function extractAccountId(auth: OpenAIAuth) {
|
|
34
|
-
if (text(auth.accountId)) return text(auth.accountId)
|
|
35
|
-
const claims = parseJwtClaims(auth.access)
|
|
36
|
-
const direct = text(claims.chatgpt_account_id)
|
|
37
|
-
if (direct) return direct
|
|
38
|
-
const namespaced = text(claims["https://api.openai.com/auth.chatgpt_account_id"])
|
|
39
|
-
if (namespaced) return namespaced
|
|
40
|
-
const orgs = claims.organizations
|
|
41
|
-
if (!Array.isArray(orgs) || !orgs.length) return
|
|
42
|
-
return text(obj(orgs[0])?.id)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function fallback(refresh: string) {
|
|
46
|
-
return `fallback:${createHash("sha256").update(refresh).digest("hex").slice(0, 16)}`
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function key(auth: OpenAIAuth) {
|
|
50
|
-
return fallback(auth.refresh)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function entryFromAuth(auth: OpenAIAuth, now = Date.now()): StoreEntry {
|
|
54
|
-
return {
|
|
55
|
-
key: key(auth),
|
|
56
|
-
email: extractEmail(auth),
|
|
57
|
-
accountId: extractAccountId(auth),
|
|
58
|
-
plan: extractPlan(auth),
|
|
59
|
-
savedAt: now,
|
|
60
|
-
auth: clean(auth),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function clean(auth: OpenAIAuth): OpenAIAuth {
|
|
65
|
-
return {
|
|
66
|
-
type: "oauth",
|
|
67
|
-
refresh: auth.refresh,
|
|
68
|
-
access: auth.access,
|
|
69
|
-
expires: auth.expires,
|
|
70
|
-
...(text(auth.accountId) ? { accountId: auth.accountId } : {}),
|
|
71
|
-
...(text(auth.enterpriseUrl) ? { enterpriseUrl: auth.enterpriseUrl } : {}),
|
|
72
|
-
}
|
|
73
|
-
}
|
package/src/paths.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import os from "node:os"
|
|
2
|
-
import path from "node:path"
|
|
3
|
-
|
|
4
|
-
export function dataPath() {
|
|
5
|
-
if (process.env.OPENCODE_TEST_HOME) {
|
|
6
|
-
return path.join(process.env.OPENCODE_TEST_HOME, ".local", "share", "opencode")
|
|
7
|
-
}
|
|
8
|
-
if (process.env.XDG_DATA_HOME) return path.join(process.env.XDG_DATA_HOME, "opencode")
|
|
9
|
-
if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "opencode")
|
|
10
|
-
if (process.platform === "win32") {
|
|
11
|
-
const root = process.env.LOCALAPPDATA || process.env.APPDATA
|
|
12
|
-
if (root) return path.join(root, "opencode")
|
|
13
|
-
}
|
|
14
|
-
return path.join(os.homedir(), ".local", "share", "opencode")
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function authPath() {
|
|
18
|
-
return path.join(dataPath(), "auth.json")
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function storeDir() {
|
|
22
|
-
return path.join(dataPath(), "auth-switch")
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function storePath() {
|
|
26
|
-
return path.join(storeDir(), "accounts.json")
|
|
27
|
-
}
|