@askalf/dario 2.9.5 → 2.10.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/proxy.js +95 -6
- package/package.json +2 -2
package/dist/proxy.js
CHANGED
|
@@ -267,6 +267,84 @@ function stripThinkingFromHistory(body) {
|
|
|
267
267
|
* specific order. We rebuild the object to match.
|
|
268
268
|
*/
|
|
269
269
|
const NON_CC_FIELDS = new Set(['service_tier', 'top_p', 'top_k', 'stop_sequences', 'temperature']);
|
|
270
|
+
// ── Tool name rewriting ──
|
|
271
|
+
// Anthropic fingerprints on tool names — non-CC names trigger overage classification.
|
|
272
|
+
// Map third-party tool names to CC equivalents on the way in, reverse on the way out.
|
|
273
|
+
const CC_TOOLS = new Set([
|
|
274
|
+
'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep', 'Browser', 'WebFetch', 'WebSearch',
|
|
275
|
+
'NotebookEdit', 'NotebookRead', 'TodoRead', 'TodoWrite',
|
|
276
|
+
'Agent', 'MCPListTools', 'MCPCallTool',
|
|
277
|
+
'AskUserQuestion', 'EnterPlanMode', 'ExitPlanMode',
|
|
278
|
+
'EnterWorktree', 'ExitWorktree', 'TaskCreate', 'TaskUpdate',
|
|
279
|
+
]);
|
|
280
|
+
// Common third-party tool names → CC equivalents
|
|
281
|
+
const TOOL_NAME_MAP = {
|
|
282
|
+
bash: 'Bash', sh: 'Bash', exec: 'Bash', shell: 'Bash', run: 'Bash', execute: 'Bash',
|
|
283
|
+
command: 'Bash', terminal: 'Bash', process: 'Bash',
|
|
284
|
+
read: 'Read', read_file: 'Read', file_read: 'Read', get_file: 'Read',
|
|
285
|
+
write: 'Write', write_file: 'Write', file_write: 'Write', create_file: 'Write', save_file: 'Write',
|
|
286
|
+
edit: 'Edit', edit_file: 'Edit', modify_file: 'Edit', patch: 'Edit', replace: 'Edit',
|
|
287
|
+
glob: 'Glob', find_files: 'Glob', list_files: 'Glob', ls: 'Glob',
|
|
288
|
+
grep: 'Grep', search: 'Grep', search_files: 'Grep', find_in_files: 'Grep', rg: 'Grep',
|
|
289
|
+
web_search: 'WebSearch', websearch: 'WebSearch', google: 'WebSearch',
|
|
290
|
+
web_fetch: 'WebFetch', webfetch: 'WebFetch', fetch: 'WebFetch', http: 'WebFetch', curl: 'WebFetch',
|
|
291
|
+
browse: 'Browser', browser: 'Browser', open_url: 'Browser',
|
|
292
|
+
notebook: 'NotebookEdit', notebook_edit: 'NotebookEdit',
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Rewrite tool names in the request to match CC toolset.
|
|
296
|
+
* Returns the mapping so we can reverse it in the response.
|
|
297
|
+
* Tools that don't map to a known CC name get wrapped as MCPCallTool.
|
|
298
|
+
*/
|
|
299
|
+
function rewriteToolNames(body) {
|
|
300
|
+
const tools = body.tools;
|
|
301
|
+
if (!tools || !Array.isArray(tools))
|
|
302
|
+
return [];
|
|
303
|
+
const mappings = [];
|
|
304
|
+
const usedNames = new Set();
|
|
305
|
+
// First pass: collect CC tool names already in the list
|
|
306
|
+
for (const tool of tools) {
|
|
307
|
+
if (CC_TOOLS.has(tool.name))
|
|
308
|
+
usedNames.add(tool.name);
|
|
309
|
+
}
|
|
310
|
+
let mcpIndex = 0;
|
|
311
|
+
for (const tool of tools) {
|
|
312
|
+
const originalName = tool.name;
|
|
313
|
+
if (!originalName)
|
|
314
|
+
continue;
|
|
315
|
+
// Already a CC tool name
|
|
316
|
+
if (CC_TOOLS.has(originalName))
|
|
317
|
+
continue;
|
|
318
|
+
// Check direct map — but avoid duplicates
|
|
319
|
+
const directMap = TOOL_NAME_MAP[originalName.toLowerCase()];
|
|
320
|
+
if (directMap && !usedNames.has(directMap)) {
|
|
321
|
+
mappings.push({ original: originalName, mapped: directMap });
|
|
322
|
+
tool.name = directMap;
|
|
323
|
+
usedNames.add(directMap);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
// Wrap as mcp_<original_name> — MCP tools use this prefix in real CC
|
|
327
|
+
const mcpName = `mcp_${originalName}`;
|
|
328
|
+
mappings.push({ original: originalName, mapped: mcpName });
|
|
329
|
+
tool.name = mcpName;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return mappings;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Reverse tool name mapping in the response body.
|
|
336
|
+
* Restores original tool names in tool_use content blocks.
|
|
337
|
+
*/
|
|
338
|
+
function reverseToolNames(body, mappings) {
|
|
339
|
+
if (mappings.length === 0)
|
|
340
|
+
return body;
|
|
341
|
+
let result = body;
|
|
342
|
+
for (const { original, mapped } of mappings) {
|
|
343
|
+
// Replace in tool_use blocks: "name":"MCPCallTool" → "name":"original"
|
|
344
|
+
result = result.replace(new RegExp(`"name"\\s*:\\s*"${mapped}"`, 'g'), `"name":"${original}"`);
|
|
345
|
+
}
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
270
348
|
// Claude Code's field order (from MITM capture). Fields not in this list are appended at end.
|
|
271
349
|
const CC_FIELD_ORDER = [
|
|
272
350
|
'model', 'messages', 'system', 'max_tokens', 'thinking', 'output_config',
|
|
@@ -752,6 +830,7 @@ export async function startProxy(opts = {}) {
|
|
|
752
830
|
}
|
|
753
831
|
// Parse body once, apply OpenAI translation, model override, and sanitization
|
|
754
832
|
let finalBody = body.length > 0 ? body : undefined;
|
|
833
|
+
let toolMappings = [];
|
|
755
834
|
if (body.length > 0) {
|
|
756
835
|
try {
|
|
757
836
|
const parsed = JSON.parse(body.toString());
|
|
@@ -766,9 +845,10 @@ export async function startProxy(opts = {}) {
|
|
|
766
845
|
// context_management: clear_thinking does NOT reduce input token billing.
|
|
767
846
|
// Real Claude Code strips thinking before building the next request.
|
|
768
847
|
stripThinkingFromHistory(r);
|
|
769
|
-
// 2.
|
|
848
|
+
// 2. Rewrite tool names to CC equivalents (Anthropic fingerprints on tool names)
|
|
849
|
+
toolMappings = rewriteToolNames(r);
|
|
850
|
+
// 3. Scrub non-CC fields and normalize field ordering
|
|
770
851
|
const reordered = scrubAndReorderFields(r);
|
|
771
|
-
// Copy reordered keys back (r is a reference to result)
|
|
772
852
|
for (const key of Object.keys(r))
|
|
773
853
|
delete r[key];
|
|
774
854
|
Object.assign(r, reordered);
|
|
@@ -793,7 +873,8 @@ export async function startProxy(opts = {}) {
|
|
|
793
873
|
if (!r.max_tokens || r.max_tokens !== 64000) {
|
|
794
874
|
r.max_tokens = 64000;
|
|
795
875
|
}
|
|
796
|
-
|
|
876
|
+
// Force effort to medium — CC default. Client 'high' is a fingerprint.
|
|
877
|
+
if (supportsThinking) {
|
|
797
878
|
r.output_config = { effort: 'medium' };
|
|
798
879
|
}
|
|
799
880
|
if (supportsThinking && !r.context_management) {
|
|
@@ -941,7 +1022,14 @@ export async function startProxy(opts = {}) {
|
|
|
941
1022
|
}
|
|
942
1023
|
}
|
|
943
1024
|
else {
|
|
944
|
-
|
|
1025
|
+
// Reverse tool names in streaming chunks
|
|
1026
|
+
if (toolMappings.length > 0) {
|
|
1027
|
+
const text = new TextDecoder().decode(value);
|
|
1028
|
+
res.write(reverseToolNames(text, toolMappings));
|
|
1029
|
+
}
|
|
1030
|
+
else {
|
|
1031
|
+
res.write(value);
|
|
1032
|
+
}
|
|
945
1033
|
}
|
|
946
1034
|
}
|
|
947
1035
|
// Flush remaining buffer
|
|
@@ -959,9 +1047,10 @@ export async function startProxy(opts = {}) {
|
|
|
959
1047
|
}
|
|
960
1048
|
else {
|
|
961
1049
|
// Buffer and forward
|
|
962
|
-
|
|
1050
|
+
let responseBody = await upstream.text();
|
|
1051
|
+
// Reverse tool name mapping so client sees original names
|
|
1052
|
+
responseBody = reverseToolNames(responseBody, toolMappings);
|
|
963
1053
|
if (isOpenAI && upstream.status >= 200 && upstream.status < 300) {
|
|
964
|
-
// Translate Anthropic response → OpenAI format
|
|
965
1054
|
try {
|
|
966
1055
|
const parsed = JSON.parse(responseBody);
|
|
967
1056
|
res.end(JSON.stringify(anthropicToOpenai(parsed)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Use your Claude subscription as an API. No API key needed. Local proxy for Claude Max/Pro subscriptions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -61,4 +61,4 @@
|
|
|
61
61
|
"tsx": "^4.19.0",
|
|
62
62
|
"typescript": "^5.7.0"
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
}
|