export interface AdapterOptions {
serverUrl?: string;
logMessage: string;
variables: string[];
level?: 'info' | 'error' | 'debug' | 'warn';
projectPath?: string; // Optional project path for tool library detection
}
export interface AdapterResult {
code: string;
language: string;
description: string;
}
export interface EnvironmentAdapter {
name: string;
generateDebugCode(options: AdapterOptions): AdapterResult;
}
// Import port manager for dynamic URL
import { portManager } from '../http/port-manager.js';
// Helper function to get server URL dynamically
function getServerUrl(): string {
return portManager.getServerUrl();
}
// Helper function to check if DebugHttpClient.java exists in project
function checkDebugHttpClientExists(projectPath?: string): boolean {
if (!projectPath) return false;
const fs = require('fs');
const path = require('path');
const possiblePaths = [
path.join(projectPath, 'DebugHttpClient.java'),
path.join(projectPath, 'src', 'DebugHttpClient.java'),
path.join(projectPath, 'src', 'main', 'java', 'com', 'debug', 'mcp', 'DebugHttpClient.java'),
path.join(projectPath, 'app', 'src', 'main', 'java', 'com', 'debug', 'mcp', 'DebugHttpClient.java')
];
return possiblePaths.some(p => {
try {
return fs.existsSync(p);
} catch {
return false;
}
});
}
// Helper function to detect Android network library from build.gradle files
function detectAndroidNetworkLibrary(projectPath?: string): 'retrofit' | 'okhttp' | 'volley' | null {
if (!projectPath) return null;
const fs = require('fs');
const path = require('path');
// Possible build.gradle file paths
const possiblePaths = [
path.join(projectPath, 'build.gradle'),
path.join(projectPath, 'app', 'build.gradle'),
path.join(projectPath, 'build.gradle.kts'),
path.join(projectPath, 'app', 'build.gradle.kts')
];
// Read all available build.gradle files
let allContent = '';
for (const gradlePath of possiblePaths) {
try {
if (fs.existsSync(gradlePath)) {
allContent += fs.readFileSync(gradlePath, 'utf8') + '\n';
}
} catch {
// Continue if file doesn't exist or can't be read
}
}
if (!allContent) return null;
// Detection patterns (priority: Retrofit > OkHttp > Volley)
// Retrofit detection
if (/\b(retrofit2|com\.squareup\.retrofit2|retrofit)\b/i.test(allContent)) {
return 'retrofit';
}
// OkHttp detection (but not if Retrofit is present, as Retrofit uses OkHttp)
if (/\b(okhttp3|com\.squareup\.okhttp3|okhttp)\b/i.test(allContent)) {
return 'okhttp';
}
// Volley detection
if (/\b(volley|com\.android\.volley)\b/i.test(allContent)) {
return 'volley';
}
return null;
}
// Helper function to generate OkHttp code for Android
function generateOkHttpCode(serverUrl: string, logMessage: string, varsMap: string, level: string): string {
const escapedMessage = logMessage.replace(/"/g, '\\"');
// Build JSON data string from HashMap
const buildJsonDataCode = `java.util.Map<String, Object> dataMap = ${varsMap};
StringBuilder dataJson = new StringBuilder("{");
boolean first = true;
for (java.util.Map.Entry<String, Object> entry : dataMap.entrySet()) {
if (!first) dataJson.append(",");
first = false;
dataJson.append("\\"").append(entry.getKey()).append("\\":");
Object value = entry.getValue();
if (value instanceof String) {
dataJson.append("\\"").append(value.toString().replace("\\"", "\\\\\\"").replace("\\\\", "\\\\\\\\")).append("\\"");
} else if (value instanceof Number || value instanceof Boolean) {
dataJson.append(value.toString());
} else {
dataJson.append("\\"").append(value != null ? value.toString().replace("\\"", "\\\\\\"").replace("\\\\", "\\\\\\\\") : "null").append("\\"");
}
}
dataJson.append("}");`;
return `// debug-start
// Android: Network requests must be executed in a background thread
if (android.os.Looper.getMainLooper().getThread() == Thread.currentThread()) {
// We are on the main thread, execute in background thread
new Thread(() -> {
try {
okhttp3.OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.connectTimeout(100, java.util.concurrent.TimeUnit.MILLISECONDS)
.readTimeout(100, java.util.concurrent.TimeUnit.MILLISECONDS)
.build();
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", java.util.Locale.US);
sdf.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
String timestamp = sdf.format(new java.util.Date());
${buildJsonDataCode}
String jsonPayload = "{"
+ "\\"timestamp\\":\\"" + timestamp + "\\","
+ "\\"level\\":\\"${level}\\","
+ "\\"message\\":\\"${escapedMessage}\\","
+ "\\"data\\":" + dataJson.toString()
+ "}";
okhttp3.RequestBody body = okhttp3.RequestBody.create(
jsonPayload,
okhttp3.MediaType.parse("application/json; charset=utf-8")
);
okhttp3.Request request = new okhttp3.Request.Builder()
.url("${serverUrl}")
.post(body)
.build();
client.newCall(request).execute();
} catch (Exception e) {
// Silent fail - do not interrupt main logic
}
}).start();
} else {
// Already in background thread, execute directly
try {
okhttp3.OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.connectTimeout(100, java.util.concurrent.TimeUnit.MILLISECONDS)
.readTimeout(100, java.util.concurrent.TimeUnit.MILLISECONDS)
.build();
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", java.util.Locale.US);
sdf.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
String timestamp = sdf.format(new java.util.Date());
${buildJsonDataCode}
String jsonPayload = "{"
+ "\\"timestamp\\":\\"" + timestamp + "\\","
+ "\\"level\\":\\"${level}\\","
+ "\\"message\\":\\"${escapedMessage}\\","
+ "\\"data\\":" + dataJson.toString()
+ "}";
okhttp3.RequestBody body = okhttp3.RequestBody.create(
jsonPayload,
okhttp3.MediaType.parse("application/json; charset=utf-8")
);
okhttp3.Request request = new okhttp3.Request.Builder()
.url("${serverUrl}")
.post(body)
.build();
client.newCall(request).execute();
} catch (Exception e) {
// Silent fail - do not interrupt main logic
}
}
// debug-end`;
}
// Helper function to generate Retrofit code for Android
// Note: Retrofit requires interface definition, so we use OkHttp directly (Retrofit uses OkHttp internally)
// This is simpler and doesn't require additional interface definitions
function generateRetrofitCode(serverUrl: string, logMessage: string, varsMap: string, level: string): string {
// Retrofit uses OkHttp internally, so we can use OkHttp directly
// This avoids needing to define Retrofit interfaces
return generateOkHttpCode(serverUrl, logMessage, varsMap, level);
}
// Helper function to generate Volley code for Android
function generateVolleyCode(serverUrl: string, logMessage: string, varsMap: string, level: string): string {
const escapedMessage = logMessage.replace(/"/g, '\\"');
// Build JSON data string from HashMap
const buildJsonDataCode = `java.util.Map<String, Object> dataMap = ${varsMap};
StringBuilder dataJson = new StringBuilder("{");
boolean first = true;
for (java.util.Map.Entry<String, Object> entry : dataMap.entrySet()) {
if (!first) dataJson.append(",");
first = false;
dataJson.append("\\"").append(entry.getKey()).append("\\":");
Object value = entry.getValue();
if (value instanceof String) {
dataJson.append("\\"").append(value.toString().replace("\\"", "\\\\\\"").replace("\\\\", "\\\\\\\\")).append("\\"");
} else if (value instanceof Number || value instanceof Boolean) {
dataJson.append(value.toString());
} else {
dataJson.append("\\"").append(value != null ? value.toString().replace("\\"", "\\\\\\"").replace("\\\\", "\\\\\\\\") : "null").append("\\"");
}
}
dataJson.append("}");`;
return `// debug-start
// Android: Network requests using Volley
// Note: Volley automatically handles threading, context can be obtained from Activity, Fragment, or Application
try {
android.content.Context ctx = null;
// Try to get context from common Android components
if (this instanceof android.app.Activity) {
ctx = (android.app.Activity) this;
} else if (this instanceof android.app.Fragment) {
ctx = ((android.app.Fragment) this).getContext();
} else if (this instanceof android.view.View) {
ctx = ((android.view.View) this).getContext();
}
// If context is still null, you may need to pass it explicitly or use Application context
if (ctx != null) {
com.android.volley.RequestQueue queue = com.android.volley.Volley.newRequestQueue(ctx);
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", java.util.Locale.US);
sdf.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
String timestamp = sdf.format(new java.util.Date());
${buildJsonDataCode}
String jsonPayload = "{"
+ "\\"timestamp\\":\\"" + timestamp + "\\","
+ "\\"level\\":\\"${level}\\","
+ "\\"message\\":\\"${escapedMessage}\\","
+ "\\"data\\":" + dataJson.toString()
+ "}";
com.android.volley.StringRequest request = new com.android.volley.StringRequest(
com.android.volley.Request.Method.POST,
"${serverUrl}",
response -> {
// Silent success
},
error -> {
// Silent fail - do not interrupt main logic
}
) {
@Override
public byte[] getBody() {
return jsonPayload.getBytes(java.nio.charset.StandardCharsets.UTF_8);
}
@Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
};
request.setRetryPolicy(new com.android.volley.DefaultRetryPolicy(
100, // timeout in milliseconds
0, // max retries
com.android.volley.DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
));
queue.add(request);
}
} catch (Exception e) {
// Silent fail - do not interrupt main logic
}
// debug-end`;
}
// Helper function to get DebugHttpClient template content
function getDebugHttpClientTemplate(): string {
const fs = require('fs');
const path = require('path');
const templatePath = path.join(__dirname, 'templates', 'DebugHttpClient.java');
try {
return fs.readFileSync(templatePath, 'utf8');
} catch {
// Fallback template if file not found
return `package com.debug.mcp;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class DebugHttpClient {
private static final int CONNECT_TIMEOUT_MS = 100;
private static final int READ_TIMEOUT_MS = 100;
public static void sendLog(String serverUrl, String logMessage, Object variables, String level) {
try {
URL url = new URL(serverUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
conn.setReadTimeout(READ_TIMEOUT_MS);
conn.setDoOutput(true);
String jsonPayload = buildJsonPayload(logMessage, variables, level);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = jsonPayload.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
conn.getResponseCode();
conn.disconnect();
} catch (Exception e) {
// Silent fail
}
}
private static String buildJsonPayload(String logMessage, Object variables, String level) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String timestamp = sdf.format(new Date());
StringBuilder json = new StringBuilder();
json.append("{");
json.append("\\"timestamp\\":\\"").append(timestamp).append("\\",");
json.append("\\"level\\":\\"").append(level != null ? level : "info").append("\\",");
json.append("\\"message\\":\\"").append(escapeJson(logMessage)).append("\\",");
json.append("\\"data\\":");
json.append(variables != null ? variables.toString() : "{}");
json.append("}");
return json.toString();
}
private static String escapeJson(String str) {
if (str == null) return "";
return str.replace("\\\\", "\\\\\\\\")
.replace("\\"", "\\\\\\"")
.replace("\\n", "\\\\n")
.replace("\\r", "\\\\r")
.replace("\\t", "\\\\t");
}
}`;
}
}
export class BrowserAdapter implements EnvironmentAdapter {
name = 'browser';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsObject = variables.length > 0
? `{ ${variables.join(', ')} }`
: '{}';
const code = `// debug-start
fetch('${serverUrl}', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
timestamp: new Date().toISOString(),
level: '${level}',
message: '${logMessage}',
data: ${varsObject}
})
}).catch(err => console.error('[Debug Log Failed]', err));
// debug-end`;
return {
code,
language: 'javascript',
description: 'Browser environment using fetch API'
};
}
}
export class NodeAdapter implements EnvironmentAdapter {
name = 'node';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsObject = variables.length > 0
? `{ ${variables.join(', ')} }`
: '{}';
const code = `// debug-start
fetch('${serverUrl}', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
timestamp: new Date().toISOString(),
level: '${level}',
message: '${logMessage}',
data: ${varsObject}
})
}).catch(err => console.error('[Debug Log Failed]', err));
// debug-end`;
return {
code,
language: 'javascript',
description: 'Node.js environment using fetch API (Node 18+)'
};
}
}
export class NodeLegacyAdapter implements EnvironmentAdapter {
name = 'node-legacy';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsObject = variables.length > 0
? `{ ${variables.join(', ')} }`
: '{}';
const code = `// debug-start
const http = require('http');
const logData = JSON.stringify({
timestamp: new Date().toISOString(),
level: '${level}',
message: '${logMessage}',
data: ${varsObject}
});
const req = http.request('${serverUrl}', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, (res) => {
if (res.statusCode !== 200) console.error('[Debug Log Failed]', res.statusCode);
});
req.on('error', (err) => console.error('[Debug Log Failed]', err));
req.write(logData);
req.end();
// debug-end`;
return {
code,
language: 'javascript',
description: 'Node.js legacy environment using http module (Node 14-17)'
};
}
}
export class ReactNativeAdapter implements EnvironmentAdapter {
name = 'react-native';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsObject = variables.length > 0
? `{ ${variables.join(', ')} }`
: '{}';
const code = `// debug-start
fetch('${serverUrl}', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
timestamp: new Date().toISOString(),
level: '${level}',
message: '${logMessage}',
data: ${varsObject}
})
}).catch(err => console.error('[Debug Log Failed]', err));
// debug-end`;
return {
code,
language: 'javascript',
description: 'React Native environment using fetch API'
};
}
}
export class ElectronMainAdapter implements EnvironmentAdapter {
name = 'electron-main';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const varsObject = variables.length > 0
? `{ ${variables.join(', ')} }`
: '{}';
const code = `// debug-start
const fs = require('fs');
const path = require('path');
const logEntry = {
timestamp: new Date().toISOString(),
level: '${level}',
message: '${logMessage}',
data: ${varsObject}
};
const logPath = path.join(app.getPath('userData'), '.debug', 'debug.log');
try {
fs.mkdirSync(path.dirname(logPath), { recursive: true });
fs.appendFileSync(logPath, JSON.stringify(logEntry) + '\\n');
} catch (err) {
console.error('[Debug Log Failed]', err);
}
// debug-end`;
return {
code,
language: 'javascript',
description: 'Electron main process using direct file write'
};
}
}
export class ElectronRendererAdapter implements EnvironmentAdapter {
name = 'electron-renderer';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const varsObject = variables.length > 0
? `{ ${variables.join(', ')} }`
: '{}';
const code = `// debug-start
const { ipcRenderer } = require('electron');
ipcRenderer.send('debug-log', {
timestamp: new Date().toISOString(),
level: '${level}',
message: '${logMessage}',
data: ${varsObject}
});
// debug-end`;
return {
code,
language: 'javascript',
description: 'Electron renderer process using IPC'
};
}
}
export class WeChatMiniAdapter implements EnvironmentAdapter {
name = 'wechat-miniprogram';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsObject = variables.length > 0
? `{ ${variables.join(', ')} }`
: '{}';
const code = `// debug-start
wx.request({
url: '${serverUrl}',
method: 'POST',
data: {
timestamp: new Date().toISOString(),
level: '${level}',
message: '${logMessage}',
data: ${varsObject}
},
fail: (err) => console.error('[Debug Log Failed]', err)
});
// debug-end`;
return {
code,
language: 'javascript',
description: 'WeChat Mini Program using wx.request'
};
}
}
export class PHPAdapter implements EnvironmentAdapter {
name = 'php';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsArray = variables.length > 0
? variables.map(v => `'${v}' => $${v}`).join(', ')
: '';
const code = `// debug-start
$logData = json_encode([
'timestamp' => date('c'),
'level' => '${level}',
'message' => '${logMessage}',
'data' => [${varsArray}]
]);
$ch = curl_init('${serverUrl}');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $logData);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 100);
curl_exec($ch);
curl_close($ch);
// debug-end`;
return {
code,
language: 'php',
description: 'PHP server-side using curl'
};
}
}
export class PythonAdapter implements EnvironmentAdapter {
name = 'python';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsDict = variables.length > 0
? variables.map(v => `'${v}': ${v}`).join(', ')
: '';
const code = `# debug-start
import requests
import json
try:
requests.post(
'${serverUrl}',
json={
'timestamp': datetime.now().isoformat(),
'level': '${level}',
'message': '${logMessage}',
'data': {${varsDict}}
},
timeout=0.1
)
except:
pass
# debug-end`;
return {
code,
language: 'python',
description: 'Python server-side using requests library'
};
}
}
export class JavaAdapter implements EnvironmentAdapter {
name = 'java';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const projectPath = options.projectPath;
const varsMap = variables.length > 0
? `new java.util.HashMap<String, Object>() {{
${variables.map(v => `put("${v}", ${v});`).join('\n ')}
}}`
: 'new java.util.HashMap<String, Object>()';
const hasToolLibrary = projectPath ? checkDebugHttpClientExists(projectPath) : false;
const toolLibraryNote = hasToolLibrary
? ''
: '\n// ⚠️ IMPORTANT: Before using this code, add DebugHttpClient.java to your project.\n// The tool library template will be provided separately.';
const code = `// debug-start${toolLibraryNote}
try {
DebugHttpClient.sendLog(
"${serverUrl}",
"${logMessage.replace(/"/g, '\\"')}",
${varsMap},
"${level}"
);
} catch (Exception e) {
// Silent fail - do not interrupt main logic
}
// debug-end`;
return {
code,
language: 'java',
description: 'Java server-side using DebugHttpClient utility'
};
}
}
export class AndroidAdapter implements EnvironmentAdapter {
name = 'android';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const projectPath = options.projectPath;
const varsMap = variables.length > 0
? `new java.util.HashMap<String, Object>() {{
${variables.map(v => `put("${v}", ${v});`).join('\n ')}
}}`
: 'new java.util.HashMap<String, Object>()';
// Detect network library in project
const detectedLibrary = projectPath ? detectAndroidNetworkLibrary(projectPath) : null;
let code: string;
let description: string;
if (detectedLibrary === 'retrofit') {
// Retrofit detected - use OkHttp (Retrofit uses OkHttp internally)
code = generateOkHttpCode(serverUrl, logMessage, varsMap, level);
description = 'Android environment using OkHttp (detected Retrofit, which uses OkHttp internally)';
} else if (detectedLibrary === 'okhttp') {
// OkHttp detected
code = generateOkHttpCode(serverUrl, logMessage, varsMap, level);
description = 'Android environment using OkHttp library';
} else if (detectedLibrary === 'volley') {
// Volley detected
code = generateVolleyCode(serverUrl, logMessage, varsMap, level);
description = 'Android environment using Volley library';
} else {
// No network library detected - use DebugHttpClient (same as JavaAdapter)
const hasToolLibrary = projectPath ? checkDebugHttpClientExists(projectPath) : false;
const toolLibraryNote = hasToolLibrary
? ''
: '\n// ⚠️ IMPORTANT: Before using this code, add DebugHttpClient.java to your project.\n// The tool library template will be provided separately.';
code = `// debug-start${toolLibraryNote}
// Android: Network requests must be executed in a background thread
if (android.os.Looper.getMainLooper().getThread() == Thread.currentThread()) {
// We are on the main thread, execute in background thread
new Thread(() -> {
try {
DebugHttpClient.sendLog(
"${serverUrl}",
"${logMessage.replace(/"/g, '\\"')}",
${varsMap},
"${level}"
);
} catch (Exception e) {
// Silent fail - do not interrupt main logic
}
}).start();
} else {
// Already in background thread, execute directly
try {
DebugHttpClient.sendLog(
"${serverUrl}",
"${logMessage.replace(/"/g, '\\"')}",
${varsMap},
"${level}"
);
} catch (Exception e) {
// Silent fail - do not interrupt main logic
}
}
// debug-end`;
description = 'Android environment using DebugHttpClient with thread safety (no network library detected)';
}
return {
code,
language: 'java',
description
};
}
}
export class KotlinAdapter implements EnvironmentAdapter {
name = 'kotlin';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const projectPath = options.projectPath;
const varsMap = variables.length > 0
? `mapOf(${variables.map(v => `"${v}" to ${v}`).join(', ')})`
: 'emptyMap<String, Any>()';
const hasToolLibrary = projectPath ? checkDebugHttpClientExists(projectPath) : false;
const toolLibraryNote = hasToolLibrary
? ''
: '\n// ⚠️ IMPORTANT: Before using this code, add DebugHttpClient.java to your project.\n// The tool library template will be provided separately.';
const code = `// debug-start${toolLibraryNote}
// Kotlin: Use coroutines for async network requests (Android) or direct call (Java)
try {
// For Android: Use coroutine scope
// CoroutineScope(Dispatchers.IO).launch {
// DebugHttpClient.sendLog(...)
// }
// For standard Java: Direct call
DebugHttpClient.sendLog(
"${serverUrl}",
"${logMessage.replace(/"/g, '\\"')}",
${varsMap},
"${level}"
)
} catch (e: Exception) {
// Silent fail - do not interrupt main logic
}
// debug-end`;
return {
code,
language: 'kotlin',
description: 'Kotlin environment using DebugHttpClient with coroutine support'
};
}
}
export class ObjectiveCAdapter implements EnvironmentAdapter {
name = 'objective-c';
generateDebugCode(options: AdapterOptions): AdapterResult {
const { logMessage, variables, level = 'info' } = options;
const serverUrl = options.serverUrl || getServerUrl();
const varsDict = variables.length > 0
? `@{${variables.map(v => `@"${v}": ${v}`).join(', ')}}`
: '@{}';
const code = `// debug-start
NSURL *url = [NSURL URLWithString:@"${serverUrl}"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setTimeoutInterval:0.1];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
[formatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
NSString *timestamp = [formatter stringFromDate:[NSDate date]];
NSDictionary *logData = @{
@"timestamp": timestamp,
@"level": @"${level}",
@"message": @"${logMessage.replace(/"/g, '\\"')}",
@"data": ${varsDict}
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:logData options:0 error:&error];
if (jsonData) {
[request setHTTPBody:jsonData];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Silent fail - do not interrupt main logic
}];
[task resume];
}
// debug-end`;
return {
code,
language: 'objective-c',
description: 'Objective-C environment using NSURLSession (iOS/macOS)'
};
}
}
// Export all adapters
export const adapters: EnvironmentAdapter[] = [
new BrowserAdapter(),
new NodeAdapter(),
new NodeLegacyAdapter(),
new ReactNativeAdapter(),
new ElectronMainAdapter(),
new ElectronRendererAdapter(),
new WeChatMiniAdapter(),
new PHPAdapter(),
new PythonAdapter(),
new JavaAdapter(),
new AndroidAdapter(),
new KotlinAdapter(),
new ObjectiveCAdapter()
];
export function getAdapterByName(name: string): EnvironmentAdapter | undefined {
return adapters.find(adapter => adapter.name === name);
}