opencode-kilocode-auth 1.0.8 → 1.0.10
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/constants.ts +16 -6
- package/src/kilocode/auth.ts +12 -1
- package/src/plugin.ts +95 -12
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.10",
|
|
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/constants.ts
CHANGED
|
@@ -17,21 +17,31 @@ export const KILOCODE_API_BASE_URL = "https://api.kilo.ai";
|
|
|
17
17
|
*/
|
|
18
18
|
export const KILOCODE_PROVIDER_ID = "kilocode";
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Kilo Code extension version (must match official extension)
|
|
22
|
+
*/
|
|
23
|
+
export const KILOCODE_VERSION = "4.151.0";
|
|
24
|
+
|
|
20
25
|
/**
|
|
21
26
|
* Default headers for Kilo Code API requests
|
|
27
|
+
* EXACT match from kilocode/src/api/providers/constants.ts DEFAULT_HEADERS
|
|
22
28
|
*/
|
|
23
29
|
export const DEFAULT_HEADERS = {
|
|
24
30
|
"Content-Type": "application/json",
|
|
25
|
-
"
|
|
31
|
+
"HTTP-Referer": "https://kilocode.ai",
|
|
32
|
+
"X-Title": "Kilo Code",
|
|
33
|
+
"X-KiloCode-Version": KILOCODE_VERSION,
|
|
34
|
+
"User-Agent": `Kilo-Code/${KILOCODE_VERSION}`,
|
|
26
35
|
} as const;
|
|
27
36
|
|
|
28
37
|
/**
|
|
29
|
-
* Kilo Code custom headers
|
|
38
|
+
* Kilo Code custom headers (EXACT casing from kilocode/src/shared/kilocode/headers.ts)
|
|
30
39
|
*/
|
|
31
|
-
export const
|
|
32
|
-
export const
|
|
33
|
-
export const
|
|
34
|
-
export const
|
|
40
|
+
export const X_KILOCODE_VERSION = "X-KiloCode-Version";
|
|
41
|
+
export const X_KILOCODE_ORGANIZATIONID = "X-KiloCode-OrganizationId";
|
|
42
|
+
export const X_KILOCODE_TASKID = "X-KiloCode-TaskId";
|
|
43
|
+
export const X_KILOCODE_PROJECTID = "X-KiloCode-ProjectId";
|
|
44
|
+
export const X_KILOCODE_EDITORNAME = "X-KiloCode-EditorName";
|
|
35
45
|
|
|
36
46
|
/**
|
|
37
47
|
* Default model ID if none is specified
|
package/src/kilocode/auth.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
DEFAULT_HEADERS,
|
|
5
5
|
DEVICE_AUTH_POLL_INTERVAL_MS,
|
|
6
6
|
DEFAULT_MODEL_ID,
|
|
7
|
+
X_KILOCODE_EDITORNAME,
|
|
7
8
|
} from "../constants";
|
|
8
9
|
import type {
|
|
9
10
|
DeviceAuthInitiateResponse,
|
|
@@ -82,6 +83,13 @@ export async function pollDeviceAuth(code: string): Promise<DeviceAuthPollRespon
|
|
|
82
83
|
return (await response.json()) as DeviceAuthPollResponse;
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Get editor name header value (matches Kilo Code VSCode extension)
|
|
88
|
+
*/
|
|
89
|
+
function getEditorNameHeader(): string {
|
|
90
|
+
return "Visual Studio Code 1.96.0";
|
|
91
|
+
}
|
|
92
|
+
|
|
85
93
|
/**
|
|
86
94
|
* Fetch user profile from Kilo Code API
|
|
87
95
|
*/
|
|
@@ -90,6 +98,7 @@ export async function getKilocodeProfile(token: string): Promise<KilocodeProfile
|
|
|
90
98
|
headers: {
|
|
91
99
|
...DEFAULT_HEADERS,
|
|
92
100
|
Authorization: `Bearer ${token}`,
|
|
101
|
+
[X_KILOCODE_EDITORNAME]: getEditorNameHeader(),
|
|
93
102
|
},
|
|
94
103
|
});
|
|
95
104
|
|
|
@@ -119,6 +128,7 @@ export async function getKilocodeDefaultModel(
|
|
|
119
128
|
headers: {
|
|
120
129
|
...DEFAULT_HEADERS,
|
|
121
130
|
Authorization: `Bearer ${token}`,
|
|
131
|
+
[X_KILOCODE_EDITORNAME]: getEditorNameHeader(),
|
|
122
132
|
},
|
|
123
133
|
});
|
|
124
134
|
|
|
@@ -146,10 +156,11 @@ export async function getKilocodeModels(
|
|
|
146
156
|
const headers: Record<string, string> = {
|
|
147
157
|
...DEFAULT_HEADERS,
|
|
148
158
|
Authorization: `Bearer ${token}`,
|
|
159
|
+
[X_KILOCODE_EDITORNAME]: getEditorNameHeader(),
|
|
149
160
|
};
|
|
150
161
|
|
|
151
162
|
if (organizationId) {
|
|
152
|
-
headers["X-
|
|
163
|
+
headers["X-KiloCode-OrganizationId"] = organizationId;
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
const response = await fetch(baseUrl, { headers });
|
package/src/plugin.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
KILOCODE_PROVIDER_ID,
|
|
4
4
|
DEFAULT_MODEL_ID,
|
|
5
5
|
DEVICE_AUTH_POLL_INTERVAL_MS,
|
|
6
|
+
KILOCODE_VERSION,
|
|
6
7
|
} from "./constants"
|
|
7
8
|
import {
|
|
8
9
|
initiateDeviceAuth,
|
|
@@ -24,6 +25,59 @@ interface KilocodeAuth {
|
|
|
24
25
|
key?: string // for api type (OpenCode uses "key" field)
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* OpenAI SDK version used by Kilo Code VSCode extension
|
|
30
|
+
* From: kilocode/src/package.json -> "openai": "^5.12.2"
|
|
31
|
+
*/
|
|
32
|
+
const OPENAI_SDK_VERSION = "0.51.0"
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get X-Stainless-* headers (EXACT match from OpenAI SDK)
|
|
36
|
+
* These are automatically added by the OpenAI SDK and MUST be included
|
|
37
|
+
*/
|
|
38
|
+
function getStainlessHeaders(): Record<string, string> {
|
|
39
|
+
const platform = process.platform
|
|
40
|
+
const arch = process.arch
|
|
41
|
+
const nodeVersion = process.version
|
|
42
|
+
|
|
43
|
+
// Map platform to OS name (EXACT from OpenAI SDK)
|
|
44
|
+
const osMap: Record<string, string> = {
|
|
45
|
+
darwin: "MacOS",
|
|
46
|
+
win32: "Windows",
|
|
47
|
+
linux: "Linux",
|
|
48
|
+
freebsd: "FreeBSD",
|
|
49
|
+
openbsd: "OpenBSD",
|
|
50
|
+
}
|
|
51
|
+
const os = osMap[platform] || `Other:${platform}`
|
|
52
|
+
|
|
53
|
+
// Map arch (EXACT from OpenAI SDK)
|
|
54
|
+
const archMap: Record<string, string> = {
|
|
55
|
+
x32: "x32",
|
|
56
|
+
x64: "x64",
|
|
57
|
+
arm: "arm",
|
|
58
|
+
arm64: "arm64",
|
|
59
|
+
}
|
|
60
|
+
const archStr = archMap[arch] || `other:${arch}`
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
"X-Stainless-Lang": "js",
|
|
64
|
+
"X-Stainless-Package-Version": OPENAI_SDK_VERSION,
|
|
65
|
+
"X-Stainless-OS": os,
|
|
66
|
+
"X-Stainless-Arch": archStr,
|
|
67
|
+
"X-Stainless-Runtime": "node",
|
|
68
|
+
"X-Stainless-Runtime-Version": nodeVersion,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get editor name header (EXACT match from Kilo Code VSCode extension)
|
|
74
|
+
* From: kilocode/src/core/kilocode/wrapper.ts -> getEditorNameHeader()
|
|
75
|
+
*/
|
|
76
|
+
function getEditorNameHeader(): string {
|
|
77
|
+
// Exact format: [appName, version].join(" ")
|
|
78
|
+
return "Visual Studio Code 1.96.0"
|
|
79
|
+
}
|
|
80
|
+
|
|
27
81
|
/**
|
|
28
82
|
* Kilo Code Authentication Plugin for OpenCode
|
|
29
83
|
*
|
|
@@ -87,8 +141,10 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
87
141
|
return fetch(requestInput, init)
|
|
88
142
|
}
|
|
89
143
|
|
|
90
|
-
// Build headers
|
|
144
|
+
// Build headers - EXACT match from Kilo Code VSCode extension
|
|
91
145
|
const headers = new Headers()
|
|
146
|
+
|
|
147
|
+
// First, copy any existing headers from the request
|
|
92
148
|
if (init?.headers) {
|
|
93
149
|
if (init.headers instanceof Headers) {
|
|
94
150
|
init.headers.forEach((value, key) => headers.set(key, value))
|
|
@@ -103,24 +159,38 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
103
159
|
}
|
|
104
160
|
}
|
|
105
161
|
|
|
106
|
-
//
|
|
107
|
-
//
|
|
162
|
+
// ============================================================
|
|
163
|
+
// EXACT HEADERS FROM KILO CODE VSCODE EXTENSION (decompiled)
|
|
164
|
+
// ============================================================
|
|
165
|
+
|
|
166
|
+
// 1. OpenAI SDK standard headers
|
|
108
167
|
headers.set("Authorization", `Bearer ${currentToken}`)
|
|
168
|
+
headers.set("Content-Type", "application/json")
|
|
109
169
|
|
|
110
|
-
// DEFAULT_HEADERS from kilocode/src/api/providers/constants.ts
|
|
170
|
+
// 2. DEFAULT_HEADERS from kilocode/src/api/providers/constants.ts
|
|
171
|
+
// EXACT: vl = {"HTTP-Referer":"https://kilocode.ai","X-Title":"Kilo Code",[sFa]:Ca.version,"User-Agent":`Kilo-Code/${Ca.version}`}
|
|
111
172
|
headers.set("HTTP-Referer", "https://kilocode.ai")
|
|
112
173
|
headers.set("X-Title", "Kilo Code")
|
|
113
|
-
headers.set("X-KiloCode-Version",
|
|
114
|
-
headers.set("User-Agent",
|
|
174
|
+
headers.set("X-KiloCode-Version", KILOCODE_VERSION)
|
|
175
|
+
headers.set("User-Agent", `Kilo-Code/${KILOCODE_VERSION}`)
|
|
115
176
|
|
|
116
|
-
// customRequestOptions from KilocodeOpenrouterHandler
|
|
117
|
-
|
|
177
|
+
// 3. customRequestOptions headers from KilocodeOpenrouterHandler
|
|
178
|
+
// EXACT: lFa = "X-KiloCode-EditorName"
|
|
179
|
+
headers.set("X-KiloCode-EditorName", getEditorNameHeader())
|
|
118
180
|
|
|
119
|
-
// Organization header (
|
|
181
|
+
// 4. Organization header (if present)
|
|
182
|
+
// EXACT: dAe = "X-KiloCode-OrganizationId"
|
|
120
183
|
if (currentKilocodeAuth.organizationId) {
|
|
121
184
|
headers.set("X-KiloCode-OrganizationId", currentKilocodeAuth.organizationId)
|
|
122
185
|
}
|
|
123
186
|
|
|
187
|
+
// 5. X-Stainless-* headers (AUTOMATICALLY added by OpenAI SDK)
|
|
188
|
+
// These are CRITICAL - the server may check for these
|
|
189
|
+
const stainlessHeaders = getStainlessHeaders()
|
|
190
|
+
for (const [key, value] of Object.entries(stainlessHeaders)) {
|
|
191
|
+
headers.set(key, value)
|
|
192
|
+
}
|
|
193
|
+
|
|
124
194
|
// Rewrite URL to Kilo Code endpoint
|
|
125
195
|
const parsed =
|
|
126
196
|
requestInput instanceof URL
|
|
@@ -128,17 +198,29 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
128
198
|
: new URL(typeof requestInput === "string" ? requestInput : (requestInput as Request).url)
|
|
129
199
|
|
|
130
200
|
// Map to Kilo Code OpenRouter-compatible endpoint
|
|
131
|
-
// Kilo Code uses: https://api.kilo.ai/api/openrouter/chat/completions
|
|
132
201
|
let url: URL
|
|
133
202
|
let body = init?.body
|
|
134
203
|
|
|
135
204
|
if (parsed.pathname.includes("/chat/completions")) {
|
|
136
205
|
url = new URL(getApiUrl("/api/openrouter/chat/completions", currentToken))
|
|
137
206
|
|
|
138
|
-
// Modify request body to
|
|
207
|
+
// Modify request body to match Kilo Code format EXACTLY
|
|
139
208
|
if (body && typeof body === "string") {
|
|
140
209
|
try {
|
|
141
210
|
const payload = JSON.parse(body)
|
|
211
|
+
|
|
212
|
+
// Add stream_options like Kilo Code does
|
|
213
|
+
// EXACT: stream_options:{include_usage:!0}
|
|
214
|
+
if (payload.stream !== false) {
|
|
215
|
+
payload.stream_options = { include_usage: true }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 6. For Anthropic models, add x-anthropic-beta header
|
|
219
|
+
// EXACT: o.startsWith("anthropic/")&&(v.headers["x-anthropic-beta"]="fine-grained-tool-streaming-2025-05-14")
|
|
220
|
+
if (payload.model && payload.model.startsWith("anthropic/")) {
|
|
221
|
+
headers.set("x-anthropic-beta", "fine-grained-tool-streaming-2025-05-14")
|
|
222
|
+
}
|
|
223
|
+
|
|
142
224
|
if (payload.messages && Array.isArray(payload.messages)) {
|
|
143
225
|
// Find system message and prepend Kilo Code role
|
|
144
226
|
const systemIdx = payload.messages.findIndex((m: any) => m.role === "system")
|
|
@@ -151,8 +233,9 @@ export async function KilocodeAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|
|
151
233
|
// Add system message at the beginning
|
|
152
234
|
payload.messages.unshift({ role: "system", content: kiloCodeRole })
|
|
153
235
|
}
|
|
154
|
-
body = JSON.stringify(payload)
|
|
155
236
|
}
|
|
237
|
+
|
|
238
|
+
body = JSON.stringify(payload)
|
|
156
239
|
} catch {
|
|
157
240
|
// Keep original body if parsing fails
|
|
158
241
|
}
|