@ex-machina/opencode-anthropic-auth 1.1.1 → 1.2.0
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/dist/constants.d.ts +4 -0
- package/dist/constants.js +8 -0
- package/dist/index.js +4 -11
- package/dist/tests/auth.test.d.ts +1 -0
- package/dist/tests/auth.test.js +87 -0
- package/dist/tests/index.test.d.ts +1 -0
- package/dist/tests/index.test.js +481 -0
- package/dist/tests/pkce.test.d.ts +1 -0
- package/dist/tests/pkce.test.js +111 -0
- package/dist/tests/transform.test.d.ts +1 -0
- package/dist/tests/transform.test.js +621 -0
- package/dist/transform.d.ts +22 -0
- package/dist/transform.js +113 -2
- package/package.json +2 -1
package/dist/transform.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { REQUIRED_BETAS, TOOL_PREFIX } from './constants';
|
|
1
|
+
import { CLAUDE_CODE_IDENTITY, OPENCODE_IDENTITY, PRESERVED_TAIL_MARKERS, REQUIRED_BETAS, TOOL_PREFIX, USER_AGENT, } from './constants';
|
|
2
2
|
/**
|
|
3
3
|
* Merge headers from a Request object and/or a RequestInit headers value
|
|
4
4
|
* into a single Headers instance.
|
|
@@ -53,7 +53,7 @@ export function mergeBetaHeaders(headers) {
|
|
|
53
53
|
export function setOAuthHeaders(headers, accessToken) {
|
|
54
54
|
headers.set('authorization', `Bearer ${accessToken}`);
|
|
55
55
|
headers.set('anthropic-beta', mergeBetaHeaders(headers));
|
|
56
|
-
headers.set('user-agent',
|
|
56
|
+
headers.set('user-agent', USER_AGENT);
|
|
57
57
|
headers.delete('x-api-key');
|
|
58
58
|
return headers;
|
|
59
59
|
}
|
|
@@ -168,6 +168,117 @@ export function rewriteUrl(input) {
|
|
|
168
168
|
: requestUrl;
|
|
169
169
|
return { input: newInput, url: requestUrl };
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Remove the OpenCode identity section from system prompt text.
|
|
173
|
+
* Finds the OpenCode identity marker, then removes everything up to
|
|
174
|
+
* the earliest preserved tail marker (user instructions, code references, etc.).
|
|
175
|
+
* This preserves user-configured instructions from config.json.
|
|
176
|
+
*/
|
|
177
|
+
export function sanitizeSystemText(text, onError) {
|
|
178
|
+
const startIdx = text.indexOf(OPENCODE_IDENTITY);
|
|
179
|
+
if (startIdx === -1)
|
|
180
|
+
return text;
|
|
181
|
+
// Find the earliest preserved tail marker after the OpenCode identity
|
|
182
|
+
let endIdx = -1;
|
|
183
|
+
for (const marker of PRESERVED_TAIL_MARKERS) {
|
|
184
|
+
const idx = text.indexOf(marker, startIdx);
|
|
185
|
+
if (idx !== -1 && (endIdx === -1 || idx < endIdx)) {
|
|
186
|
+
endIdx = idx;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (endIdx === -1) {
|
|
190
|
+
onError?.('sanitizeSystemText: could not find any preserved tail marker after OpenCode identity');
|
|
191
|
+
return text;
|
|
192
|
+
}
|
|
193
|
+
// Preserve content from the marker onwards (skip leading newline if present)
|
|
194
|
+
const tail = text[endIdx] === '\n' ? text.slice(endIdx + 1) : text.slice(endIdx);
|
|
195
|
+
return text.slice(0, startIdx) + tail;
|
|
196
|
+
}
|
|
197
|
+
function isRecord(value) {
|
|
198
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Sanitize system prompt and prepend Claude Code identity.
|
|
202
|
+
* Handles all Anthropic API system formats: undefined, string, or array of text blocks.
|
|
203
|
+
*/
|
|
204
|
+
export function prependClaudeCodeIdentity(system, onError) {
|
|
205
|
+
const identityBlock = {
|
|
206
|
+
type: 'text',
|
|
207
|
+
text: CLAUDE_CODE_IDENTITY,
|
|
208
|
+
};
|
|
209
|
+
if (system == null)
|
|
210
|
+
return [identityBlock];
|
|
211
|
+
if (typeof system === 'string') {
|
|
212
|
+
const sanitized = sanitizeSystemText(system, onError);
|
|
213
|
+
if (sanitized === CLAUDE_CODE_IDENTITY)
|
|
214
|
+
return [identityBlock];
|
|
215
|
+
return [identityBlock, { type: 'text', text: sanitized }];
|
|
216
|
+
}
|
|
217
|
+
if (isRecord(system)) {
|
|
218
|
+
const type = typeof system.type === 'string' ? system.type : 'text';
|
|
219
|
+
const text = typeof system.text === 'string' ? system.text : '';
|
|
220
|
+
return [
|
|
221
|
+
identityBlock,
|
|
222
|
+
{ ...system, type, text: sanitizeSystemText(text, onError) },
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
if (!Array.isArray(system))
|
|
226
|
+
return [identityBlock];
|
|
227
|
+
const sanitized = system.map((item) => {
|
|
228
|
+
if (typeof item === 'string') {
|
|
229
|
+
return { type: 'text', text: sanitizeSystemText(item, onError) };
|
|
230
|
+
}
|
|
231
|
+
if (isRecord(item) &&
|
|
232
|
+
item.type === 'text' &&
|
|
233
|
+
typeof item.text === 'string') {
|
|
234
|
+
return {
|
|
235
|
+
...item,
|
|
236
|
+
type: 'text',
|
|
237
|
+
text: sanitizeSystemText(item.text, onError),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return { type: 'text', text: String(item) };
|
|
241
|
+
});
|
|
242
|
+
// Idempotency: don't double-prepend if first block already has the identity
|
|
243
|
+
if (sanitized[0]?.text === CLAUDE_CODE_IDENTITY) {
|
|
244
|
+
return sanitized;
|
|
245
|
+
}
|
|
246
|
+
return [identityBlock, ...sanitized];
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Rewrite the full request body: sanitize system prompt and prefix tool names.
|
|
250
|
+
*/
|
|
251
|
+
export function rewriteRequestBody(body, onError) {
|
|
252
|
+
try {
|
|
253
|
+
const parsed = JSON.parse(body);
|
|
254
|
+
// Sanitize system prompt and prepend Claude Code identity
|
|
255
|
+
parsed.system = prependClaudeCodeIdentity(parsed.system, onError);
|
|
256
|
+
// Prefix tool names
|
|
257
|
+
if (parsed.tools && Array.isArray(parsed.tools)) {
|
|
258
|
+
parsed.tools = parsed.tools.map((tool) => ({
|
|
259
|
+
...tool,
|
|
260
|
+
name: tool.name ? `${TOOL_PREFIX}${tool.name}` : tool.name,
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
264
|
+
parsed.messages = parsed.messages.map((msg) => {
|
|
265
|
+
if (msg.content && Array.isArray(msg.content)) {
|
|
266
|
+
msg.content = msg.content.map((block) => {
|
|
267
|
+
if (block.type === 'tool_use' && block.name) {
|
|
268
|
+
return { ...block, name: `${TOOL_PREFIX}${block.name}` };
|
|
269
|
+
}
|
|
270
|
+
return block;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return msg;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return JSON.stringify(parsed);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return body;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
171
282
|
/**
|
|
172
283
|
* Create a streaming response that strips the tool prefix from tool names.
|
|
173
284
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ex-machina/opencode-anthropic-auth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/ex-machina-co/opencode-anthropic-auth"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
+
"prepare": "tsc -p tsconfig.build.json",
|
|
14
15
|
"build": "tsc -p tsconfig.build.json",
|
|
15
16
|
"test": "bun test",
|
|
16
17
|
"types": "tsc",
|