@askalf/dario 2.11.0 → 3.0.2

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.
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Claude Code request template — the exact tool definitions, system structure,
3
+ * and request shape that real Claude Code sends.
4
+ *
5
+ * Instead of transforming third-party requests signal-by-signal, we replace
6
+ * the entire request with a CC template and inject only the conversation content.
7
+ * The upstream sees a genuine CC request. Anthropic can't detect it without
8
+ * flagging their own binary.
9
+ *
10
+ * Source: MITM capture + binary RE of Claude Code v2.1.100
11
+ */
12
+ /** Claude Code's exact tool definitions (from binary RE + MITM capture). */
13
+ export const CC_TOOL_DEFINITIONS = [
14
+ {
15
+ name: 'Bash',
16
+ description: 'Execute a bash command and return its output. The working directory persists between commands. Use this for system commands, file operations, git, npm, etc.',
17
+ input_schema: {
18
+ type: 'object',
19
+ properties: {
20
+ command: { type: 'string', description: 'The command to execute' },
21
+ timeout: { type: 'number', description: 'Optional timeout in milliseconds (max 600000)' },
22
+ },
23
+ required: ['command'],
24
+ },
25
+ },
26
+ {
27
+ name: 'Read',
28
+ description: 'Reads a file from the local filesystem. The file_path parameter must be an absolute path.',
29
+ input_schema: {
30
+ type: 'object',
31
+ properties: {
32
+ file_path: { type: 'string', description: 'The absolute path to the file to read' },
33
+ offset: { type: 'integer', description: 'The line number to start reading from' },
34
+ limit: { type: 'integer', description: 'The number of lines to read' },
35
+ },
36
+ required: ['file_path'],
37
+ },
38
+ },
39
+ {
40
+ name: 'Write',
41
+ description: 'Writes a file to the local filesystem. This tool will overwrite the existing file if there is one.',
42
+ input_schema: {
43
+ type: 'object',
44
+ properties: {
45
+ file_path: { type: 'string', description: 'The absolute path to the file to write' },
46
+ content: { type: 'string', description: 'The content to write to the file' },
47
+ },
48
+ required: ['file_path', 'content'],
49
+ },
50
+ },
51
+ {
52
+ name: 'Edit',
53
+ description: 'Performs exact string replacements in files. The edit will FAIL if old_string is not unique in the file.',
54
+ input_schema: {
55
+ type: 'object',
56
+ properties: {
57
+ file_path: { type: 'string', description: 'The absolute path to the file to modify' },
58
+ old_string: { type: 'string', description: 'The text to replace' },
59
+ new_string: { type: 'string', description: 'The text to replace it with' },
60
+ replace_all: { type: 'boolean', description: 'Replace all occurrences', default: false },
61
+ },
62
+ required: ['file_path', 'old_string', 'new_string'],
63
+ },
64
+ },
65
+ {
66
+ name: 'Glob',
67
+ description: 'Fast file pattern matching tool that works with any codebase size. Returns matching file paths.',
68
+ input_schema: {
69
+ type: 'object',
70
+ properties: {
71
+ pattern: { type: 'string', description: 'The glob pattern to match files against' },
72
+ path: { type: 'string', description: 'The directory to search in' },
73
+ },
74
+ required: ['pattern'],
75
+ },
76
+ },
77
+ {
78
+ name: 'Grep',
79
+ description: 'A powerful search tool built on ripgrep. Supports full regex syntax.',
80
+ input_schema: {
81
+ type: 'object',
82
+ properties: {
83
+ pattern: { type: 'string', description: 'The regular expression pattern to search for' },
84
+ path: { type: 'string', description: 'File or directory to search in' },
85
+ output_mode: { type: 'string', enum: ['content', 'files_with_matches', 'count'], description: 'Output mode' },
86
+ },
87
+ required: ['pattern'],
88
+ },
89
+ },
90
+ {
91
+ name: 'WebFetch',
92
+ description: 'Fetches a URL from the internet and returns the content.',
93
+ input_schema: {
94
+ type: 'object',
95
+ properties: {
96
+ url: { type: 'string', description: 'The URL to fetch' },
97
+ },
98
+ required: ['url'],
99
+ },
100
+ },
101
+ {
102
+ name: 'WebSearch',
103
+ description: 'Searches the web using a search engine and returns results.',
104
+ input_schema: {
105
+ type: 'object',
106
+ properties: {
107
+ query: { type: 'string', description: 'The search query' },
108
+ },
109
+ required: ['query'],
110
+ },
111
+ },
112
+ {
113
+ name: 'NotebookEdit',
114
+ description: 'Edits a Jupyter notebook cell.',
115
+ input_schema: {
116
+ type: 'object',
117
+ properties: {
118
+ notebook_path: { type: 'string', description: 'Path to the notebook file' },
119
+ cell_number: { type: 'integer', description: 'Cell number to edit' },
120
+ new_source: { type: 'string', description: 'New cell source code' },
121
+ },
122
+ required: ['notebook_path', 'cell_number', 'new_source'],
123
+ },
124
+ },
125
+ {
126
+ name: 'Agent',
127
+ description: 'Launch a new agent to handle complex tasks. The agent runs in an isolated context.',
128
+ input_schema: {
129
+ type: 'object',
130
+ properties: {
131
+ prompt: { type: 'string', description: 'The task for the agent to perform' },
132
+ description: { type: 'string', description: 'A short description of the task' },
133
+ },
134
+ required: ['description', 'prompt'],
135
+ },
136
+ },
137
+ {
138
+ name: 'AskUserQuestion',
139
+ description: 'Ask the user a question and wait for their response.',
140
+ input_schema: {
141
+ type: 'object',
142
+ properties: {
143
+ question: { type: 'string', description: 'The question to ask' },
144
+ },
145
+ required: ['question'],
146
+ },
147
+ },
148
+ ];
149
+ const TOOL_MAP = {
150
+ // Direct maps
151
+ bash: { ccTool: 'Bash', translateArgs: (a) => ({ command: a.cmd || a.command || a.c || '' }) },
152
+ exec: { ccTool: 'Bash', translateArgs: (a) => ({ command: a.cmd || a.command || a.c || '' }) },
153
+ shell: { ccTool: 'Bash', translateArgs: (a) => ({ command: a.cmd || a.command || a.c || '' }) },
154
+ run: { ccTool: 'Bash', translateArgs: (a) => ({ command: a.cmd || a.command || '' }) },
155
+ command: { ccTool: 'Bash', translateArgs: (a) => ({ command: a.cmd || a.command || '' }) },
156
+ terminal: { ccTool: 'Bash', translateArgs: (a) => ({ command: a.cmd || a.command || '' }) },
157
+ process: { ccTool: 'Bash', translateArgs: (a) => ({ command: a.action || a.cmd || '' }) },
158
+ read: { ccTool: 'Read', translateArgs: (a) => ({ file_path: a.path || a.file_path || '' }) },
159
+ read_file: { ccTool: 'Read', translateArgs: (a) => ({ file_path: a.path || a.file_path || '' }) },
160
+ write: { ccTool: 'Write', translateArgs: (a) => ({ file_path: a.path || a.file_path || '', content: a.content || '' }) },
161
+ write_file: { ccTool: 'Write', translateArgs: (a) => ({ file_path: a.path || a.file_path || '', content: a.content || '' }) },
162
+ edit: { ccTool: 'Edit', translateArgs: (a) => ({ file_path: a.path || a.file_path || '', old_string: a.old || a.old_string || '', new_string: a.new || a.new_string || '' }) },
163
+ edit_file: { ccTool: 'Edit' },
164
+ glob: { ccTool: 'Glob' },
165
+ find_files: { ccTool: 'Glob', translateArgs: (a) => ({ pattern: a.pattern || a.query || '' }) },
166
+ list_files: { ccTool: 'Glob', translateArgs: (a) => ({ pattern: a.pattern || '*' }) },
167
+ grep: { ccTool: 'Grep' },
168
+ search: { ccTool: 'Grep', translateArgs: (a) => ({ pattern: a.query || a.pattern || '' }) },
169
+ search_files: { ccTool: 'Grep', translateArgs: (a) => ({ pattern: a.query || a.pattern || '' }) },
170
+ web_search: { ccTool: 'WebSearch', translateArgs: (a) => ({ query: a.query || a.q || '' }) },
171
+ websearch: { ccTool: 'WebSearch', translateArgs: (a) => ({ query: a.query || a.q || '' }) },
172
+ web_fetch: { ccTool: 'WebFetch', translateArgs: (a) => ({ url: a.url || a.u || '' }) },
173
+ webfetch: { ccTool: 'WebFetch', translateArgs: (a) => ({ url: a.url || a.u || '' }) },
174
+ fetch: { ccTool: 'WebFetch', translateArgs: (a) => ({ url: a.url || '' }) },
175
+ browse: { ccTool: 'WebFetch', translateArgs: (a) => ({ url: a.url || '' }) },
176
+ notebook: { ccTool: 'NotebookEdit' },
177
+ notebook_edit: { ccTool: 'NotebookEdit' },
178
+ };
179
+ /**
180
+ * Build a CC-template request from a client request.
181
+ * Replaces the entire request structure — tools, fields, ordering — with
182
+ * what real CC sends. Only the conversation content is preserved.
183
+ */
184
+ export function buildCCRequest(clientBody, billingTag, agentIdentity, cache1h, identity) {
185
+ const model = clientBody.model || 'claude-sonnet-4-6';
186
+ const isHaiku = model.toLowerCase().includes('haiku');
187
+ const messages = clientBody.messages || [];
188
+ const clientTools = clientBody.tools;
189
+ const stream = clientBody.stream ?? false;
190
+ // ── Strip thinking from history ──
191
+ for (const msg of messages) {
192
+ if (msg.role === 'assistant' && Array.isArray(msg.content)) {
193
+ msg.content = msg.content.filter(b => b.type !== 'thinking');
194
+ }
195
+ // Strip cache_control from message blocks
196
+ if (Array.isArray(msg.content)) {
197
+ for (const block of msg.content) {
198
+ delete block.cache_control;
199
+ }
200
+ }
201
+ }
202
+ // ── Build tool mapping ──
203
+ const activeToolMap = new Map();
204
+ const unmappedTools = [];
205
+ if (clientTools) {
206
+ for (const tool of clientTools) {
207
+ const name = (tool.name || '').toLowerCase();
208
+ const mapping = TOOL_MAP[name];
209
+ if (mapping) {
210
+ activeToolMap.set(tool.name, mapping);
211
+ }
212
+ else {
213
+ unmappedTools.push(tool.name);
214
+ // Distribute unmapped tools across CC tool names to avoid suspicious
215
+ // patterns where every unknown tool maps to Bash
216
+ const CC_FALLBACK_TOOLS = ['Bash', 'Read', 'Grep', 'Glob', 'WebSearch', 'WebFetch'];
217
+ const fallbackTool = CC_FALLBACK_TOOLS[unmappedTools.length % CC_FALLBACK_TOOLS.length];
218
+ activeToolMap.set(tool.name, {
219
+ ccTool: fallbackTool,
220
+ translateArgs: (a) => {
221
+ // Translate args to match the CC tool's expected schema
222
+ switch (fallbackTool) {
223
+ case 'Bash': return { command: `echo "${JSON.stringify(a).slice(0, 200)}"` };
224
+ case 'Read': return { file_path: String(a.path || a.file || a.url || '/tmp/output') };
225
+ case 'Grep': return { pattern: String(a.query || a.pattern || a.search || '.'), path: '.' };
226
+ case 'Glob': return { pattern: String(a.pattern || a.glob || '*') };
227
+ case 'WebSearch': return { query: String(a.query || a.q || a.search || '') };
228
+ case 'WebFetch': return { url: String(a.url || a.uri || '') };
229
+ default: return a;
230
+ }
231
+ },
232
+ });
233
+ }
234
+ }
235
+ }
236
+ // ── Remap tool_use and tool_result references in message history ──
237
+ // Track tool_use_id → CC tool name for consistent remapping
238
+ const toolUseIdMap = new Map();
239
+ for (const msg of messages) {
240
+ if (Array.isArray(msg.content)) {
241
+ for (const block of msg.content) {
242
+ if (block.type === 'tool_use' && typeof block.name === 'string') {
243
+ const mapping = activeToolMap.get(block.name);
244
+ if (mapping) {
245
+ block.name = mapping.ccTool;
246
+ if (mapping.translateArgs && block.input) {
247
+ block.input = mapping.translateArgs(block.input);
248
+ }
249
+ }
250
+ // Track the ID so tool_results stay consistent
251
+ if (typeof block.id === 'string') {
252
+ toolUseIdMap.set(block.id, block.name);
253
+ }
254
+ }
255
+ // Strip any client-specific fields from tool_result blocks that CC wouldn't send
256
+ if (block.type === 'tool_result') {
257
+ // Remove non-standard fields clients may add
258
+ for (const key of Object.keys(block)) {
259
+ if (!['type', 'tool_use_id', 'content', 'is_error'].includes(key)) {
260
+ delete block[key];
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+ // ── Compact conversation history ──
268
+ // Real CC conversations have specific patterns. Strip metadata that
269
+ // third-party frameworks inject into tool_result content.
270
+ for (const msg of messages) {
271
+ if (Array.isArray(msg.content)) {
272
+ for (const block of msg.content) {
273
+ // Truncate very long tool_result content — CC tool results are typically
274
+ // shorter because CC truncates file reads, command output, etc.
275
+ if (block.type === 'tool_result' && typeof block.content === 'string' && block.content.length > 30000) {
276
+ block.content = block.content.slice(0, 30000) + '\n[...truncated]';
277
+ }
278
+ // Also handle array-form tool_result content
279
+ if (block.type === 'tool_result' && Array.isArray(block.content)) {
280
+ for (const sub of block.content) {
281
+ if (sub.type === 'text' && typeof sub.text === 'string' && sub.text.length > 30000) {
282
+ sub.text = sub.text.slice(0, 30000) + '\n[...truncated]';
283
+ }
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
289
+ // ── Merge system prompt ──
290
+ let systemText = '';
291
+ const sys = clientBody.system;
292
+ if (typeof sys === 'string') {
293
+ systemText = sys;
294
+ }
295
+ else if (Array.isArray(sys)) {
296
+ systemText = sys
297
+ .filter(b => b.text && !b.text.includes('x-anthropic-billing-header:'))
298
+ .map(b => b.text)
299
+ .join('\n\n');
300
+ }
301
+ // Strip framework identifiers from system prompt that would flag non-CC usage
302
+ const FRAMEWORK_PATTERNS = [
303
+ /\b(openclaw|hermes|aider|cursor|windsurf|cline|continue|copilot|cody)\b/gi,
304
+ /\b(openai|gpt-4|gpt-3\.5)\b/gi,
305
+ /powered by [a-z]+/gi,
306
+ /\bgateway\b/gi,
307
+ ];
308
+ for (const pattern of FRAMEWORK_PATTERNS) {
309
+ systemText = systemText.replace(pattern, '');
310
+ }
311
+ // ── Build the CC request from template ──
312
+ const ccRequest = {
313
+ model,
314
+ messages,
315
+ system: [
316
+ { type: 'text', text: billingTag },
317
+ { type: 'text', text: agentIdentity, cache_control: cache1h },
318
+ { type: 'text', text: systemText || 'You are a helpful assistant.', cache_control: cache1h },
319
+ ],
320
+ max_tokens: 64000,
321
+ };
322
+ // Model-specific fields (matches CC v2.1.104 exactly)
323
+ if (!isHaiku) {
324
+ ccRequest.thinking = { type: 'adaptive' };
325
+ ccRequest.output_config = { effort: 'medium' };
326
+ ccRequest.context_management = { edits: [{ type: 'clear_thinking_20251015', keep: 'all' }] };
327
+ // CC sends temperature:1 explicitly when not in thinking-only mode
328
+ ccRequest.temperature = 1;
329
+ }
330
+ // Always include metadata
331
+ ccRequest.metadata = {
332
+ user_id: JSON.stringify({
333
+ device_id: identity.deviceId,
334
+ account_uuid: identity.accountUuid,
335
+ session_id: identity.sessionId,
336
+ }),
337
+ };
338
+ ccRequest.stream = stream;
339
+ // Use CC's exact tool definitions — not the client's
340
+ if (clientTools && clientTools.length > 0) {
341
+ ccRequest.tools = CC_TOOL_DEFINITIONS;
342
+ }
343
+ return { body: ccRequest, toolMap: activeToolMap, unmappedTools };
344
+ }
345
+ /**
346
+ * Reverse-map CC tool calls in a response back to client tool names.
347
+ */
348
+ export function reverseMapResponse(responseBody, toolMap) {
349
+ if (toolMap.size === 0)
350
+ return responseBody;
351
+ let result = responseBody;
352
+ // Build reverse map: CC tool name → original client tool name
353
+ const reverseMap = new Map();
354
+ for (const [clientName, mapping] of toolMap) {
355
+ // Only add if not a direct CC tool name
356
+ if (clientName.toLowerCase() !== mapping.ccTool.toLowerCase()) {
357
+ reverseMap.set(mapping.ccTool, clientName);
358
+ }
359
+ }
360
+ for (const [ccName, clientName] of reverseMap) {
361
+ result = result.replace(new RegExp(`"name"\\s*:\\s*"${ccName}"`, 'g'), `"name":"${clientName}"`);
362
+ }
363
+ return result;
364
+ }
package/dist/oauth.js CHANGED
@@ -108,7 +108,7 @@ export async function startAutoOAuthFlow() {
108
108
  .catch(reject);
109
109
  });
110
110
  let port = 0;
111
- server.listen(0, 'localhost', () => {
111
+ server.listen(0, 'localhost', async () => {
112
112
  const addr = server.address();
113
113
  port = typeof addr === 'object' && addr ? addr.port : 0;
114
114
  const params = new URLSearchParams({
@@ -127,7 +127,7 @@ export async function startAutoOAuthFlow() {
127
127
  console.log(` If the browser didn't open, visit: ${authUrl}`);
128
128
  console.log('');
129
129
  // Open browser using platform-specific commands (no external deps)
130
- const { exec } = require('node:child_process');
130
+ const { exec } = await import('node:child_process');
131
131
  const cmd = process.platform === 'win32' ? `start "" "${authUrl}"`
132
132
  : process.platform === 'darwin' ? `open "${authUrl}"`
133
133
  : `xdg-open "${authUrl}"`;