opencode-kilocode-auth 1.0.1 → 1.0.3
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/package.json +1 -1
- package/src/plugin.ts +24 -89
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-kilocode-auth",
|
|
3
3
|
"module": "index.ts",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"author": "ported from Kilo Code",
|
|
6
6
|
"description": "OpenCode plugin for Kilo Code authentication with support for various models including Giga Potato",
|
|
7
7
|
"files": [
|
package/src/plugin.ts
CHANGED
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
|
2
2
|
import {
|
|
3
3
|
KILOCODE_PROVIDER_ID,
|
|
4
|
-
KILOCODE_API_BASE_URL,
|
|
5
|
-
DEFAULT_HEADERS,
|
|
6
4
|
DEFAULT_MODEL_ID,
|
|
7
|
-
DEVICE_AUTH_POLL_INTERVAL_MS,
|
|
8
5
|
} from "./constants"
|
|
9
6
|
import {
|
|
10
|
-
initiateDeviceAuth,
|
|
11
|
-
pollDeviceAuth,
|
|
12
7
|
getKilocodeProfile,
|
|
13
8
|
getKilocodeDefaultModel,
|
|
14
9
|
getKilocodeModels,
|
|
15
|
-
openBrowserUrl,
|
|
16
10
|
getApiUrl,
|
|
17
11
|
} from "./kilocode/auth"
|
|
18
12
|
|
|
19
13
|
interface KilocodeAuth {
|
|
20
|
-
type: "oauth"
|
|
21
|
-
refresh
|
|
14
|
+
type: "oauth" | "api"
|
|
15
|
+
refresh?: string // token (for oauth)
|
|
22
16
|
access?: string
|
|
23
17
|
expires?: number
|
|
24
18
|
organizationId?: string
|
|
25
19
|
model?: string
|
|
20
|
+
apiKey?: string // for api type
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
/**
|
|
@@ -40,10 +35,13 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
40
35
|
|
|
41
36
|
async loader(getAuth, provider) {
|
|
42
37
|
const auth = await getAuth()
|
|
43
|
-
if (!auth
|
|
38
|
+
if (!auth) return {}
|
|
44
39
|
|
|
45
40
|
const kilocodeAuth = auth as KilocodeAuth
|
|
46
|
-
|
|
41
|
+
// Support both OAuth (refresh token) and API key authentication
|
|
42
|
+
const token = kilocodeAuth.type === "api"
|
|
43
|
+
? kilocodeAuth.apiKey
|
|
44
|
+
: kilocodeAuth.refresh
|
|
47
45
|
|
|
48
46
|
if (!token) return {}
|
|
49
47
|
|
|
@@ -72,12 +70,18 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
72
70
|
|
|
73
71
|
async fetch(requestInput: RequestInfo | URL, init?: RequestInit) {
|
|
74
72
|
const currentAuth = await getAuth()
|
|
75
|
-
if (!currentAuth
|
|
73
|
+
if (!currentAuth) {
|
|
76
74
|
return fetch(requestInput, init)
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
const currentKilocodeAuth = currentAuth as KilocodeAuth
|
|
80
|
-
const currentToken = currentKilocodeAuth.
|
|
78
|
+
const currentToken = currentKilocodeAuth.type === "api"
|
|
79
|
+
? currentKilocodeAuth.apiKey
|
|
80
|
+
: currentKilocodeAuth.refresh
|
|
81
|
+
|
|
82
|
+
if (!currentToken) {
|
|
83
|
+
return fetch(requestInput, init)
|
|
84
|
+
}
|
|
81
85
|
|
|
82
86
|
// Build headers
|
|
83
87
|
const headers = new Headers()
|
|
@@ -95,9 +99,13 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
// Set Kilo Code authorization
|
|
102
|
+
// Set Kilo Code authorization and required headers
|
|
99
103
|
headers.set("Authorization", `Bearer ${currentToken}`)
|
|
100
|
-
headers.set("
|
|
104
|
+
headers.set("HTTP-Referer", "https://kilocode.ai")
|
|
105
|
+
headers.set("X-Title", "Kilo Code")
|
|
106
|
+
headers.set("X-KiloCode-Version", "3.16.3")
|
|
107
|
+
headers.set("User-Agent", "Kilo-Code/3.16.3")
|
|
108
|
+
headers.set("X-KiloCode-EditorName", "Visual Studio Code 1.96.0")
|
|
101
109
|
|
|
102
110
|
// Add organization header if present
|
|
103
111
|
if (currentKilocodeAuth.organizationId) {
|
|
@@ -130,81 +138,10 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
130
138
|
},
|
|
131
139
|
|
|
132
140
|
methods: [
|
|
133
|
-
|
|
134
|
-
type: "oauth",
|
|
135
|
-
label: "Login with Kilo Code",
|
|
136
|
-
async authorize() {
|
|
137
|
-
// Initiate device auth flow
|
|
138
|
-
const authData = await initiateDeviceAuth()
|
|
139
|
-
const { code, verificationUrl, expiresIn } = authData
|
|
140
|
-
|
|
141
|
-
// Open browser for user
|
|
142
|
-
openBrowserUrl(verificationUrl)
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
url: verificationUrl,
|
|
146
|
-
instructions: `Enter code: ${code}`,
|
|
147
|
-
method: "auto" as const,
|
|
148
|
-
|
|
149
|
-
async callback() {
|
|
150
|
-
// Poll for authorization
|
|
151
|
-
const maxAttempts = Math.ceil((expiresIn * 1000) / DEVICE_AUTH_POLL_INTERVAL_MS)
|
|
152
|
-
let attempt = 0
|
|
153
|
-
|
|
154
|
-
while (attempt < maxAttempts) {
|
|
155
|
-
await new Promise((resolve) => setTimeout(resolve, DEVICE_AUTH_POLL_INTERVAL_MS))
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const pollResult = await pollDeviceAuth(code)
|
|
159
|
-
|
|
160
|
-
if (pollResult.status === "approved" && pollResult.token) {
|
|
161
|
-
// Get profile for organization
|
|
162
|
-
let organizationId: string | undefined
|
|
163
|
-
try {
|
|
164
|
-
const profile = await getKilocodeProfile(pollResult.token)
|
|
165
|
-
if (profile.organizations && profile.organizations.length > 0) {
|
|
166
|
-
organizationId = profile.organizations[0].id
|
|
167
|
-
}
|
|
168
|
-
} catch {
|
|
169
|
-
// Continue without organization
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Get default model
|
|
173
|
-
const model = await getKilocodeDefaultModel(pollResult.token, organizationId)
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
type: "success" as const,
|
|
177
|
-
refresh: pollResult.token,
|
|
178
|
-
access: pollResult.token,
|
|
179
|
-
expires: Date.now() + 365 * 24 * 60 * 60 * 1000, // 1 year (tokens don't expire normally)
|
|
180
|
-
// Store extra info in the auth
|
|
181
|
-
organizationId,
|
|
182
|
-
model,
|
|
183
|
-
} as any
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (pollResult.status === "denied") {
|
|
187
|
-
return { type: "failed" as const }
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (pollResult.status === "expired") {
|
|
191
|
-
return { type: "failed" as const }
|
|
192
|
-
}
|
|
193
|
-
} catch {
|
|
194
|
-
// Continue polling on error
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
attempt++
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return { type: "failed" as const }
|
|
201
|
-
},
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
},
|
|
141
|
+
// API key is the primary method - get it from https://app.kilo.ai
|
|
205
142
|
{
|
|
206
143
|
type: "api",
|
|
207
|
-
label: "Enter
|
|
144
|
+
label: "Enter API Key (get from app.kilo.ai)",
|
|
208
145
|
},
|
|
209
146
|
],
|
|
210
147
|
},
|
|
@@ -221,8 +158,6 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
221
158
|
|
|
222
159
|
// Export utilities for external use
|
|
223
160
|
export {
|
|
224
|
-
initiateDeviceAuth,
|
|
225
|
-
pollDeviceAuth,
|
|
226
161
|
getKilocodeProfile,
|
|
227
162
|
getKilocodeDefaultModel,
|
|
228
163
|
getKilocodeModels,
|