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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-kilocode-auth",
3
3
  "module": "index.ts",
4
- "version": "1.0.8",
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
- "User-Agent": "opencode-kilocode-auth/1.0.0",
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 X_KILOCODE_ORGANIZATIONID = "X-KILOCODE-ORGANIZATIONID";
32
- export const X_KILOCODE_TASKID = "X-KILOCODE-TASKID";
33
- export const X_KILOCODE_PROJECTID = "X-KILOCODE-PROJECTID";
34
- export const X_KILOCODE_EDITORNAME = "X-KILOCODE-EDITORNAME";
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
@@ -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-KILOCODE-ORGANIZATIONID"] = organizationId;
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
- // EXACT headers from Kilo Code VSCode extension (DEFAULT_HEADERS + customRequestOptions)
107
- // Authorization is set by OpenAI SDK internally
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", "4.151.0")
114
- headers.set("User-Agent", "Kilo-Code/4.151.0")
174
+ headers.set("X-KiloCode-Version", KILOCODE_VERSION)
175
+ headers.set("User-Agent", `Kilo-Code/${KILOCODE_VERSION}`)
115
176
 
116
- // customRequestOptions from KilocodeOpenrouterHandler
117
- headers.set("X-KiloCode-EditorName", "Visual Studio Code 1.96.0")
177
+ // 3. customRequestOptions headers from KilocodeOpenrouterHandler
178
+ // EXACT: lFa = "X-KiloCode-EditorName"
179
+ headers.set("X-KiloCode-EditorName", getEditorNameHeader())
118
180
 
119
- // Organization header (exact casing from headers.ts)
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 include Kilo Code system prompt prefix
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
  }